mirror of
https://github.com/Hessenuk/DiscordTickets.git
synced 2024-11-09 14:23:08 +02:00
Settings command, many small changes, improvements, and fixes
This commit is contained in:
parent
e6fa1d067f
commit
ffc415354a
@ -1,4 +1,6 @@
|
|||||||
const Command = require('../modules/commands/command');
|
const Command = require('../modules/commands/command');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
const { MessageAttachment } = require('discord.js');
|
||||||
|
|
||||||
module.exports = class SettingsCommand extends Command {
|
module.exports = class SettingsCommand extends Command {
|
||||||
constructor(client) {
|
constructor(client) {
|
||||||
@ -12,11 +14,108 @@ module.exports = class SettingsCommand extends Command {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute({ guild, member, channel, args }, message) {
|
async execute({ guild, channel, member }, message) {
|
||||||
|
|
||||||
let settings = await guild.settings;
|
let settings = await guild.settings;
|
||||||
const i18n = this.client.i18n.get(settings.locale);
|
const i18n = this.client.i18n.get(settings.locale);
|
||||||
|
|
||||||
channel.send('Settings!');
|
let attachments = [ ...message.attachments.values() ];
|
||||||
|
|
||||||
|
if (attachments.length >= 1) {
|
||||||
|
// load settings from json
|
||||||
|
let data = await (await fetch(attachments[0].url)).json();
|
||||||
|
|
||||||
|
settings.colour = data.colour;
|
||||||
|
settings.error_colour = data.error_colour;
|
||||||
|
settings.locale = data.locale;
|
||||||
|
settings.log_messages = data.log_messages;
|
||||||
|
settings.success_colour = data.success_colour;
|
||||||
|
await settings.save();
|
||||||
|
|
||||||
|
for (let c of data.categories) {
|
||||||
|
let permissions = [
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
id: guild.roles.everyone,
|
||||||
|
deny: ['VIEW_CHANNEL']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
...c.roles.map(r => {
|
||||||
|
return {
|
||||||
|
id: r,
|
||||||
|
allow: ['VIEW_CHANNEL', 'READ_MESSAGE_HISTORY', 'SEND_MESSAGES', 'ATTACH_FILES']
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
if (c.id) {
|
||||||
|
// existing category
|
||||||
|
let category = await this.client.db.models.Category.findOne({
|
||||||
|
where: {
|
||||||
|
id: c.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
category.name = c.name;
|
||||||
|
category.roles = c.roles;
|
||||||
|
category.save();
|
||||||
|
|
||||||
|
let cat_channel = await this.client.channels.fetch(c.id);
|
||||||
|
await cat_channel.edit({
|
||||||
|
name: c.name, // await cat_channel.setName(c.name);
|
||||||
|
permissionOverwrites: permissions // await cat_channel.overwritePermissions(permissions);
|
||||||
|
},
|
||||||
|
`Tickets category updated by ${member.user.tag}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// create a new category
|
||||||
|
let created = await guild.channels.create(c.name, {
|
||||||
|
type: 'category',
|
||||||
|
reason: `Tickets category created by ${member.user.tag}`,
|
||||||
|
permissionOverwrites: permissions
|
||||||
|
});
|
||||||
|
await this.client.db.models.Category.create({
|
||||||
|
id: created.id,
|
||||||
|
name: c.name,
|
||||||
|
guild: guild.id,
|
||||||
|
roles: c.roles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.send(`\`\`\`json\n${JSON.stringify(data, null, 2)}\n\`\`\``);
|
||||||
|
} else {
|
||||||
|
// upload settings as json to be modified
|
||||||
|
let data = {
|
||||||
|
categories: [],
|
||||||
|
colour: settings.colour,
|
||||||
|
error_colour: settings.error_colour,
|
||||||
|
locale: settings.locale,
|
||||||
|
log_messages: settings.log_messages,
|
||||||
|
success_colour: settings.success_colour,
|
||||||
|
};
|
||||||
|
|
||||||
|
let categories = await this.client.db.models.Category.findAll({
|
||||||
|
where: {
|
||||||
|
guild: guild.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let c of categories) {
|
||||||
|
data.categories.push({
|
||||||
|
id: c.id,
|
||||||
|
name: c.name,
|
||||||
|
roles: c.roles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let attachment = new MessageAttachment(
|
||||||
|
Buffer.from(JSON.stringify(data, null, 2)),
|
||||||
|
`Settings for ${guild.name}.json`
|
||||||
|
);
|
||||||
|
|
||||||
|
channel.send(i18n('commands.settings.response'), {
|
||||||
|
files: [attachment]
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
@ -5,7 +5,6 @@ const {
|
|||||||
const { path } = require('../utils/fs');
|
const { path } = require('../utils/fs');
|
||||||
const config = require('../../user/config');
|
const config = require('../../user/config');
|
||||||
const types = require('./dialects');
|
const types = require('./dialects');
|
||||||
const supported = Object.keys(types);
|
|
||||||
|
|
||||||
module.exports = async (log) => {
|
module.exports = async (log) => {
|
||||||
|
|
||||||
@ -21,6 +20,7 @@ module.exports = async (log) => {
|
|||||||
|
|
||||||
let type = (DB_TYPE || 'sqlite').toLowerCase();
|
let type = (DB_TYPE || 'sqlite').toLowerCase();
|
||||||
|
|
||||||
|
const supported = Object.keys(types);
|
||||||
if (!supported.includes(type)) {
|
if (!supported.includes(type)) {
|
||||||
log.error(new Error(`DB_TYPE (${type}) is not a valid type`));
|
log.error(new Error(`DB_TYPE (${type}) is not a valid type`));
|
||||||
return process.exit();
|
return process.exit();
|
||||||
@ -68,10 +68,22 @@ module.exports = async (log) => {
|
|||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
},
|
},
|
||||||
|
locale: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: config.locale
|
||||||
|
},
|
||||||
colour: {
|
colour: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
defaultValue: config.defaults.colour
|
defaultValue: config.defaults.colour
|
||||||
},
|
},
|
||||||
|
success_colour: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'GREEN'
|
||||||
|
},
|
||||||
|
error_colour: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: 'RED'
|
||||||
|
},
|
||||||
log_messages: {
|
log_messages: {
|
||||||
type: DataTypes.BOOLEAN,
|
type: DataTypes.BOOLEAN,
|
||||||
defaultValue: config.defaults.log_messages
|
defaultValue: config.defaults.log_messages
|
||||||
@ -100,6 +112,9 @@ module.exports = async (log) => {
|
|||||||
},
|
},
|
||||||
unique: 'name_guild'
|
unique: 'name_guild'
|
||||||
},
|
},
|
||||||
|
roles: {
|
||||||
|
type: DataTypes.JSON
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
tableName: DB_TABLE_PREFIX + 'categories'
|
tableName: DB_TABLE_PREFIX + 'categories'
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@ module.exports = {
|
|||||||
|
|
||||||
switch (interaction.type) {
|
switch (interaction.type) {
|
||||||
case 1:
|
case 1:
|
||||||
client.log.debug('Received interaction ping, responding with pong');
|
client.log.info('Received interaction ping, responding with pong');
|
||||||
await client.api.interactions(interaction.id, interaction.token).callback.post({
|
await client.api.interactions(interaction.id, interaction.token).callback.post({
|
||||||
data: {
|
data: {
|
||||||
type: 1, // PONG
|
type: 1, // PONG
|
||||||
@ -13,7 +13,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
client.commands.handle(interaction);
|
client.commands.handle(interaction, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,13 +2,6 @@ module.exports = {
|
|||||||
event: 'messageDelete',
|
event: 'messageDelete',
|
||||||
execute: async (client, message) => {
|
execute: async (client, message) => {
|
||||||
|
|
||||||
if (message.partial)
|
|
||||||
try {
|
|
||||||
await message.fetch();
|
|
||||||
} catch (err) {
|
|
||||||
return client.log.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let settings = await message.guild?.settings;
|
let settings = await message.guild?.settings;
|
||||||
|
|
||||||
if (settings?.log_messages) {
|
if (settings?.log_messages) {
|
||||||
|
@ -2,12 +2,13 @@ module.exports = {
|
|||||||
event: 'msgUpdate',
|
event: 'msgUpdate',
|
||||||
execute: async (client, oldm, newm) => {
|
execute: async (client, oldm, newm) => {
|
||||||
|
|
||||||
if (newm.partial)
|
if (newm.partial) {
|
||||||
try {
|
try {
|
||||||
await newm.fetch();
|
await newm.fetch();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return client.log.error(err);
|
return client.log.error(err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let settings = await newm.guild?.settings;
|
let settings = await newm.guild?.settings;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"description": "Configure Discord Tickets"
|
"description": "Configure Discord Tickets"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"must_be_slash": "❌ This command must be invoked by a slash command interaction (`/%s`).",
|
||||||
"no_perm": "❌ You do not have the permissions required to use this command:\n%s",
|
"no_perm": "❌ You do not have the permissions required to use this command:\n%s",
|
||||||
"support_only": "❌ You must be a member of staff to use this command."
|
"support_only": "❌ You must be a member of staff to use this command."
|
||||||
}
|
}
|
@ -70,7 +70,7 @@ module.exports = class CommandManager {
|
|||||||
if (typeof data.description !== 'string')
|
if (typeof data.description !== 'string')
|
||||||
throw new TypeError(`Expected type of command description to be a string, got ${typeof data.description}`);
|
throw new TypeError(`Expected type of command description to be a string, got ${typeof data.description}`);
|
||||||
|
|
||||||
if (data.name.length < 1 || data.name.length > 100)
|
if (data.description.length < 1 || data.description.length > 100)
|
||||||
throw new TypeError('Length of description must be 3-32');
|
throw new TypeError('Length of description must be 3-32');
|
||||||
|
|
||||||
if (typeof data.options !== 'undefined' && !(data.options instanceof Array))
|
if (typeof data.options !== 'undefined' && !(data.options instanceof Array))
|
||||||
@ -139,9 +139,9 @@ module.exports = class CommandManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a command
|
* Execute a command
|
||||||
* @param {(Interaction|Message)} interaction - Command interaction, or message
|
* @param {(Interaction|Message)} interaction_or_message - Command interaction or message
|
||||||
*/
|
*/
|
||||||
async handle(interaction, slash) {
|
async handle(interaction_or_message, slash) {
|
||||||
slash = slash === false ? false : true;
|
slash = slash === false ? false : true;
|
||||||
let cmd_name,
|
let cmd_name,
|
||||||
args = {},
|
args = {},
|
||||||
@ -151,54 +151,62 @@ module.exports = class CommandManager {
|
|||||||
member_id;
|
member_id;
|
||||||
|
|
||||||
if (slash) {
|
if (slash) {
|
||||||
cmd_name = interaction.data.name;
|
cmd_name = interaction_or_message.data.name;
|
||||||
|
|
||||||
guild_id = interaction.guild_id;
|
guild_id = interaction_or_message.guild_id;
|
||||||
channel_id = interaction.channel_id;
|
channel_id = interaction_or_message.channel_id;
|
||||||
member_id = interaction.member.user.id;
|
member_id = interaction_or_message.member.user.id;
|
||||||
|
|
||||||
if (interaction.data.options)
|
if (interaction_or_message.data.options)
|
||||||
interaction.data.options.forEach(({ name, value }) => args[name] = value);
|
interaction_or_message.data.options.forEach(({ name, value }) => args[name] = value);
|
||||||
} else {
|
} else {
|
||||||
cmd_name = interaction.content.match(/^tickets\/(\S+)/mi);
|
cmd_name = interaction_or_message.content.match(/^tickets\/(\S+)/mi);
|
||||||
if (cmd_name) cmd_name = cmd_name[1];
|
if (cmd_name) cmd_name = cmd_name[1];
|
||||||
|
|
||||||
guild_id = interaction.guild.id;
|
guild_id = interaction_or_message.guild.id;
|
||||||
channel_id = interaction.channel.id;
|
channel_id = interaction_or_message.channel.id;
|
||||||
member_id = interaction.author.id;
|
member_id = interaction_or_message.author.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd_name === null || !this.commands.has(cmd_name))
|
if (cmd_name === null || !this.commands.has(cmd_name))
|
||||||
throw new Error(`Received "${cmd_name}" command invocation, but the command manager does not have a "${cmd_name}" command`);
|
return this.client.log.warn(`Received "${cmd_name}" command invocation, but the command manager does not have a "${cmd_name}" command registered`);
|
||||||
|
|
||||||
data.args = args;
|
data.args = args;
|
||||||
data.guild = await this.client.guilds.fetch(guild_id);
|
data.guild = await this.client.guilds.fetch(guild_id);
|
||||||
data.channel = await this.client.channels.fetch(channel_id),
|
data.channel = await this.client.channels.fetch(channel_id),
|
||||||
data.member = await data.guild.members.fetch(member_id);
|
data.member = await data.guild.members.fetch(member_id);
|
||||||
|
|
||||||
const cmd = this.commands.get(cmd_name);
|
|
||||||
|
|
||||||
let settings = await data.guild.settings;
|
let settings = await data.guild.settings;
|
||||||
if (!settings) settings = await data.guild.createSettings();
|
if (!settings) settings = await data.guild.createSettings();
|
||||||
const i18n = this.client.i18n.get(settings.locale);
|
const i18n = this.client.i18n.get(settings.locale);
|
||||||
|
|
||||||
// if (cmd.staff_only) {}
|
const cmd = this.commands.get(cmd_name);
|
||||||
|
|
||||||
|
if (cmd.slash && !slash) {
|
||||||
|
this.client.log.commands(`Blocking command execution for the "${cmd_name}" command as it was invoked by a message, not a slash command interaction_or_message.`);
|
||||||
|
try {
|
||||||
|
data.channel.send(i18n('must_be_slash', cmd_name)); // interaction_or_message.reply
|
||||||
|
} catch (err) {
|
||||||
|
this.client.log.warn('Failed to reply to blocked command invocation message');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const no_perm = cmd.permissions instanceof Array
|
const no_perm = cmd.permissions instanceof Array
|
||||||
&& !data.member.hasPermission(cmd.permissions);
|
&& !data.member.hasPermission(cmd.permissions);
|
||||||
if (no_perm) {
|
if (no_perm) {
|
||||||
let perms = cmd.permissions.map(p => `\`${p}\``).join(', ');
|
let perms = cmd.permissions.map(p => `\`${p}\``).join(', ');
|
||||||
let msg = i18n('no_perm', perms);
|
let msg = i18n('no_perm', perms);
|
||||||
if (slash) return await cmd.sendResponse(interaction, msg, true);
|
if (slash) return await cmd.respond(interaction_or_message, msg, true);
|
||||||
else return await interaction.channel.send(msg);
|
else return await interaction_or_message.channel.send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (slash) await cmd.acknowledge(interaction, true); // respond to discord
|
if (slash) await cmd.acknowledge(interaction_or_message, true); // respond to discord
|
||||||
this.client.log.commands(`Executing "${cmd_name}" command (invoked by ${data.member.user.tag})`);
|
this.client.log.commands(`Executing "${cmd_name}" command (invoked by ${data.member.user.tag})`);
|
||||||
await cmd.execute(data, interaction); // run the command
|
await cmd.execute(data, interaction_or_message); // run the command
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.client.log.warn(`(COMMANDS) An error occurred whilst executed the ${cmd_name} command`);
|
this.client.log.warn(`An error occurred whilst executing the ${cmd_name} command`);
|
||||||
this.client.log.error(e);
|
this.client.log.error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,16 +39,36 @@ module.exports = class TicketManager extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Close a ticket
|
* Close a ticket
|
||||||
* @param {string} ticket - The channel ID, or the ticket number
|
* @param {string} ticket - The channel ID, or the ticket number
|
||||||
|
* @param {string} [closer] - ID of the member who is closing the ticket
|
||||||
*/
|
*/
|
||||||
async close(ticket) {
|
async close(ticket, closer) {
|
||||||
|
if (!this.client.channels.resolve(ticket)) {
|
||||||
|
let row = await this.client.db.Models.Ticket.findOne({
|
||||||
|
where: {
|
||||||
|
number: ticket
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!row) throw new Error(`Could not find a ticket with number ${ticket}`);
|
||||||
|
ticket = row.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let row = await this.client.db.Models.Ticket.findOne({
|
||||||
|
where: {
|
||||||
|
id: ticket
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!row) throw new Error(`Could not find a ticket with ID ${ticket}`);
|
||||||
|
|
||||||
|
this.emit('beforeClose', ticket, closer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* for each message in table, create entities
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Close multiple tickets
|
|
||||||
* @param {string[]} tickets - An array of channel IDs to close **(does not accept ticket numbers)**
|
|
||||||
*/
|
|
||||||
async closeMultiple(tickets) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
@ -25,12 +25,12 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
debug: false,
|
debug: false,
|
||||||
defaults: {
|
defaults: {
|
||||||
colour: '#009999',
|
colour: '#009999', // https://discord.js.org/#/docs/main/stable/typedef/ColorResolvable
|
||||||
log_messages: true, // transcripts/archives will be empty if false
|
log_messages: true, // transcripts/archives will be empty if false
|
||||||
prefix: '-',
|
prefix: '-',
|
||||||
ticket_welcome: 'Hello {{name}}, thank you for creating a ticket. A member of staff will soon be available to assist you.\n\n__All messages in this channel are stored for future reference.__',
|
ticket_welcome: 'Hello {{name}}, thank you for creating a ticket. A member of staff will soon be available to assist you.\n\n__All messages in this channel are stored for future reference.__',
|
||||||
},
|
},
|
||||||
locale: 'en-GB',
|
locale: 'en-GB', // used for globals (such as commands) and the default guild locale
|
||||||
logs: {
|
logs: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
keep_for: 30
|
keep_for: 30
|
||||||
|
Loading…
Reference in New Issue
Block a user