From 05ffeeb88843b60211bb8a6524e0302c95f0a786 Mon Sep 17 00:00:00 2001 From: Julian Date: Sun, 27 Jun 2021 18:37:00 +0200 Subject: [PATCH 1/7] cleanup badword class --- src/BadWord.js | 64 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/BadWord.js b/src/BadWord.js index 5d7e78dc8..9c3e0e5ea 100644 --- a/src/BadWord.js +++ b/src/BadWord.js @@ -27,27 +27,27 @@ class BadWord extends ChatTriggeredFeature { * @return {BadWord} */ constructor(gid, json, id) { - super(id); - this.gid = gid; + super(id); + this.gid = gid; - if (json) { - this.trigger = json.trigger; - this.punishment = typeof(json.punishment) === 'string' ? JSON.parse(json.punishment) : json.punishment; - this.response = json.response; - if (this.punishment && this.punishment.action === 'dm' && this.response && this.response !== 'disabled') { - if (this.response === 'default') { - this.punishment.message = BadWord.defaultResponse; - } else { - this.punishment.message = this.response; - } + if (json) { + this.trigger = json.trigger; + this.punishment = typeof(json.punishment) === 'string' ? JSON.parse(json.punishment) : json.punishment; + this.response = json.response; + if (this.punishment && this.punishment.action === 'dm' && this.response && this.response !== 'disabled') { + if (this.response === 'default') { + this.punishment.message = BadWord.defaultResponse; + } else { + this.punishment.message = this.response; + } + } + this.global = json.global; + this.channels = json.channels; } - this.global = json.global; - this.channels = json.channels; - } - if (!this.channels) { - this.channels = []; - } + if (!this.channels) { + this.channels = []; + } } /** @@ -55,7 +55,7 @@ class BadWord extends ChatTriggeredFeature { * @returns {(*|string)[]} */ serialize() { - return [this.gid, JSON.stringify(this.trigger), JSON.stringify(this.punishment), this.response, this.global, this.channels.join(',')]; + return [this.gid, JSON.stringify(this.trigger), JSON.stringify(this.punishment), this.response, this.global, this.channels.join(',')]; } /** @@ -65,19 +65,19 @@ class BadWord extends ChatTriggeredFeature { * @returns {module:"discord.js".MessageEmbed} */ embed(title, color) { - const embed = new Discord.MessageEmbed() - .setTitle(title + ` [${this.id}]`) - .setColor(color) - .addFields( - /** @type {any} */[ - {name: "Trigger", value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000)}, - {name: "Response", value: this.response === 'default' ? BadWord.defaultResponse :this.response.substring(0,1000)}, - {name: "Channels", value: this.global ? "global" : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000)} - ]); - if (this.punishment.action) { - embed.addField("Punishment", `${this.punishment.action} ${this.punishment.duration ? `for ${this.punishment.duration}` : ''}`) - } - return embed; + const embed = new Discord.MessageEmbed() + .setTitle(title + ` [${this.id}]`) + .setColor(color) + .addFields( + /** @type {any} */[ + {name: 'Trigger', value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000)}, + {name: 'Response', value: this.response === 'default' ? BadWord.defaultResponse :this.response.substring(0,1000)}, + {name: 'Channels', value: this.global ? 'global' : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000)} + ]); + if (this.punishment.action) { + embed.addField('Punishment', `${this.punishment.action} ${this.punishment.duration ? `for ${this.punishment.duration}` : ''}`); + } + return embed; } } From 5e6e0c0ed9eba5df959e12b5f2ff219a614ff260 Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 2 Jul 2021 15:06:05 +0200 Subject: [PATCH 2/7] style autoresponse --- src/AutoResponse.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/AutoResponse.js b/src/AutoResponse.js index 6a7fcc99a..42cec4737 100644 --- a/src/AutoResponse.js +++ b/src/AutoResponse.js @@ -22,19 +22,19 @@ class AutoResponse extends ChatTriggeredFeature { * @return {AutoResponse} the auto response */ constructor(gid, json, id) { - super(id); - this.gid = gid; + super(id); + this.gid = gid; - if (json) { - this.trigger = json.trigger; - this.response = json.response; - this.global = json.global; - this.channels = json.channels; - } + if (json) { + this.trigger = json.trigger; + this.response = json.response; + this.global = json.global; + this.channels = json.channels; + } - if (!this.channels) { - this.channels = []; - } + if (!this.channels) { + this.channels = []; + } } /** @@ -42,7 +42,7 @@ class AutoResponse extends ChatTriggeredFeature { * @returns {(*|string)[]} */ serialize() { - return [this.gid, JSON.stringify(this.trigger), this.response, this.global, this.channels.join(',')]; + return [this.gid, JSON.stringify(this.trigger), this.response, this.global, this.channels.join(',')]; } /** @@ -52,15 +52,15 @@ class AutoResponse extends ChatTriggeredFeature { * @returns {module:"discord.js".MessageEmbed} */ embed(title, color) { - return new Discord.MessageEmbed() - .setTitle(title + ` [${this.id}]`) - .setColor(color) - .addFields( - /** @type {any} */[ - {name: "Trigger", value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000)}, - {name: "Response", value: this.response.substring(0,1000)}, - {name: "Channels", value: this.global ? "global" : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000)} - ]); + return new Discord.MessageEmbed() + .setTitle(title + ` [${this.id}]`) + .setColor(color) + .addFields( + /** @type {any} */[ + {name: 'Trigger', value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000)}, + {name: 'Response', value: this.response.substring(0,1000)}, + {name: 'Channels', value: this.global ? 'global' : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000)} + ]); } } From 249a7be4199ed4c9b81a4ea0ccd935353b1e277d Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 2 Jul 2021 15:23:07 +0200 Subject: [PATCH 3/7] refactor bad words --- src/AutoResponse.js | 3 +- src/BadWord.js | 246 +++++++++++++++++------- src/ChatTriggeredFeature.js | 67 ++++++- src/Command.js | 2 +- src/Trigger.js | 37 ++++ src/Typedefs.js | 14 -- src/commands/legacy/badword.js | 51 ----- src/commands/legacy/badword/add.js | 101 ---------- src/commands/legacy/badword/info.js | 22 --- src/commands/legacy/badword/list.js | 21 -- src/commands/legacy/badword/remove.js | 45 ----- src/commands/settings/BadWordCommand.js | 162 ++++++++++++++++ 12 files changed, 435 insertions(+), 336 deletions(-) create mode 100644 src/Trigger.js delete mode 100644 src/commands/legacy/badword.js delete mode 100644 src/commands/legacy/badword/add.js delete mode 100644 src/commands/legacy/badword/info.js delete mode 100644 src/commands/legacy/badword/list.js delete mode 100644 src/commands/legacy/badword/remove.js create mode 100644 src/commands/settings/BadWordCommand.js diff --git a/src/AutoResponse.js b/src/AutoResponse.js index 42cec4737..2ebdb2ac3 100644 --- a/src/AutoResponse.js +++ b/src/AutoResponse.js @@ -22,11 +22,10 @@ class AutoResponse extends ChatTriggeredFeature { * @return {AutoResponse} the auto response */ constructor(gid, json, id) { - super(id); + super(id, json.trigger); this.gid = gid; if (json) { - this.trigger = json.trigger; this.response = json.response; this.global = json.global; this.channels = json.channels; diff --git a/src/BadWord.js b/src/BadWord.js index 9c3e0e5ea..c7a87a482 100644 --- a/src/BadWord.js +++ b/src/BadWord.js @@ -1,84 +1,184 @@ const ChatTriggeredFeature = require('./ChatTriggeredFeature'); const Discord = require('discord.js'); +const util = require('./util'); /** * Class representing a bad word */ class BadWord extends ChatTriggeredFeature { - static punishmentTypes = ['none','ban','kick','mute','softban','strike','dm']; - - static defaultResponse = 'Your message includes words/phrases that are not allowed here!'; - - static tableName = 'badWords'; - - static columns = ['guildid', 'trigger', 'punishment', 'response', 'global', 'channels']; - - /** - * constructor - create a bad word - * @param {module:"discord.js".Snowflake} gid guild ID - * @param {Object} json options - * @param {Trigger} json.trigger filter that triggers the bad word - * @param {String|Punishment} json.punishment punishment for the members which trigger this - * @param {String} [json.response] a message that is send by this filter. It's automatically deleted after 5 seconds - * @param {Boolean} json.global does this apply to all channels in this guild - * @param {module:"discord.js".Snowflake[]} [json.channels] channels that this applies to - * @param {Number} [id] id in DB - * @return {BadWord} - */ - constructor(gid, json, id) { - super(id); - this.gid = gid; - - if (json) { - this.trigger = json.trigger; - this.punishment = typeof(json.punishment) === 'string' ? JSON.parse(json.punishment) : json.punishment; - this.response = json.response; - if (this.punishment && this.punishment.action === 'dm' && this.response && this.response !== 'disabled') { - if (this.response === 'default') { - this.punishment.message = BadWord.defaultResponse; - } else { - this.punishment.message = this.response; - } - } - this.global = json.global; - this.channels = json.channels; - } - - if (!this.channels) { - this.channels = []; - } - } - - /** - * serialize the bad word - * @returns {(*|string)[]} - */ - serialize() { - return [this.gid, JSON.stringify(this.trigger), JSON.stringify(this.punishment), this.response, this.global, this.channels.join(',')]; - } - - /** - * generate an Embed displaying the info of this bad word - * @param {String} title - * @param {Number} color - * @returns {module:"discord.js".MessageEmbed} - */ - embed(title, color) { - const embed = new Discord.MessageEmbed() - .setTitle(title + ` [${this.id}]`) - .setColor(color) - .addFields( - /** @type {any} */[ - {name: 'Trigger', value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000)}, - {name: 'Response', value: this.response === 'default' ? BadWord.defaultResponse :this.response.substring(0,1000)}, - {name: 'Channels', value: this.global ? 'global' : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000)} - ]); - if (this.punishment.action) { - embed.addField('Punishment', `${this.punishment.action} ${this.punishment.duration ? `for ${this.punishment.duration}` : ''}`); - } - return embed; - } + static punishmentTypes = ['none', 'ban', 'kick', 'mute', 'softban', 'strike', 'dm']; + + static defaultResponse = 'Your message includes words/phrases that are not allowed here!'; + + static tableName = 'badWords'; + + static columns = ['guildid', 'trigger', 'punishment', 'response', 'global', 'channels']; + + /** + * constructor - create a bad word + * @param {module:"discord.js".Snowflake} gid guild ID + * @param {Object} json options + * @param {Trigger} json.trigger filter that triggers the bad word + * @param {String|Punishment} json.punishment punishment for the members which trigger this + * @param {String} [json.response] a message that is send by this filter. It's automatically deleted after 5 seconds + * @param {Boolean} json.global does this apply to all channels in this guild + * @param {module:"discord.js".Snowflake[]} [json.channels] channels that this applies to + * @param {Number} [json.priority] badword priority (higher -> more important) + * @param {Number} [id] id in DB + * @return {BadWord} + */ + constructor(gid, json, id) { + super(id, json.trigger); + this.gid = gid; + + if (json) { + this.punishment = typeof (json.punishment) === 'string' ? JSON.parse(json.punishment) : json.punishment; + this.response = json.response; + if (this.punishment && this.punishment.action === 'dm' && this.response && this.response !== 'disabled') { + if (this.response === 'default') { + this.punishment.message = BadWord.defaultResponse; + } else { + this.punishment.message = this.response; + } + } + this.global = json.global; + this.channels = json.channels; + this.priority = json.priority || 0; + } + + if (!this.channels) { + this.channels = []; + } + } + + /** + * serialize the bad word + * @returns {(*|string)[]} + */ + serialize() { + return [this.gid, JSON.stringify(this.trigger), JSON.stringify(this.punishment), this.response, this.global, this.channels.join(',')]; + } + + /** + * generate an Embed displaying the info of this bad word + * @param {String} title + * @param {Number} color + * @returns {module:"discord.js".MessageEmbed} + */ + embed(title, color) { + const embed = new Discord.MessageEmbed() + .setTitle(title + ` [${this.id}]`) + .setColor(color) + .addFields( + /** @type {any} */[ + { + name: 'Trigger', + value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000) + }, + { + name: 'Response', + value: this.response === 'default' ? BadWord.defaultResponse : this.response.substring(0, 1000) + }, + { + name: 'Channels', + value: this.global ? 'global' : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000) + } + ]); + if (this.punishment.action) { + embed.addField('Punishment', `${this.punishment.action} ${this.punishment.duration ? `for ${this.punishment.duration}` : ''}`); + } + return embed; + } + + /** + * create a new bad word + * @param {Snowflake} guildID + * @param {boolean} global + * @param {Snowflake[]|null} channels + * @param {String} triggerType + * @param {String} triggerContent + * @returns {Promise<{success:boolean, badWord: BadWord, message: String}>} + */ + static async create(guildID, global, channels, triggerType, triggerContent) { + + let trigger = this.getTrigger(triggerType, triggerContent); + if (!trigger.success) return trigger; + + const badWord = new BadWord(guildID, { + trigger: trigger.trigger, + punishment: {action: 'none'}, + global, + channels, + response: 'disabled' + }); + await badWord.save(); + return {success: true, badWord}; + } + + /** + * edit this badword + * @param {String} option option to change + * @param {String[]} args + * @param {module:"discord.js".Guild} guild + * @returns {Promise} response message + */ + async edit(option, args, guild) { + switch (option) { + case 'trigger': { + let trigger = this.constructor.getTrigger(args.shift(), args.join(' ')); + if (!trigger.success) return trigger.message; + this.trigger = trigger.trigger; + await this.save(); + return 'Successfully changed trigger'; + } + + case 'response': { + let response = args.join(' '); + if (!response) response = 'disabled'; + + this.response = response; + await this.save(); + return `Successfully ${response === 'disabled' ? 'disabled' : 'changed'} response`; + } + + case 'punishment': { + let action = args.shift().toLowerCase(), + duration = args.join(' '); + if (!this.constructor.punishmentTypes.includes(action)) return 'Unknown punishment'; + this.punishment = {action, duration}; + await this.save(); + return `Successfully ${action === 'none' ? 'disabled' : 'changed'} punishment`; + } + + case 'priority': { + let priority = parseInt(args.shift()); + if (Number.isNaN(priority)) return 'Invalid priority'; + this.priority = priority; + await this.save(); + return `Successfully changed priority to ${priority}`; + } + + case 'channels': { + if (args[0].toLowerCase() === 'global') { + this.global = true; + this.channels = []; + } + else { + let channels = util.channelMentions(guild, args); + if (!channels) return 'No valid channels specified'; + this.global = false; + this.channels = channels; + } + await this.save(); + return global ? 'Successfully made this badword global' : 'Successfully changed channels'; + } + + default: { + return 'Unknown option'; + } + } + } } module.exports = BadWord; diff --git a/src/ChatTriggeredFeature.js b/src/ChatTriggeredFeature.js index c7f166367..13d31d87e 100644 --- a/src/ChatTriggeredFeature.js +++ b/src/ChatTriggeredFeature.js @@ -1,4 +1,5 @@ const Discord = require('discord.js'); +const Trigger = require('./Trigger'); /** * Database @@ -38,11 +39,21 @@ class ChatTriggeredFeature { */ static columns; + /** + * @type {Object} + * @property {String} type + * @property {String} content + * @property {String} [flags] + */ + trigger; + /** * @param {Number} id ID in the database + * @param {Trigger} trigger */ - constructor(id) { + constructor(id, trigger) { this.id = id; + this.trigger = new Trigger(trigger); } /** @@ -89,24 +100,25 @@ class ChatTriggeredFeature { */ matches(message) { switch (this.trigger.type) { - case "include": + case 'include': if (message.content.toLowerCase().includes(this.trigger.content.toLowerCase())) { return true; } break; - case "match": + case 'match': if (message.content.toLowerCase() === this.trigger.content.toLowerCase()) { return true; } break; - case "regex": - let regex = new RegExp(this.trigger.content,this.trigger.flags); + case 'regex': { + let regex = new RegExp(this.trigger.content, this.trigger.flags); if (regex.test(message.content)) { return true; } break; + } } return false; @@ -125,7 +137,7 @@ class ChatTriggeredFeature { this.id = dbentry.insertId; if (this.global) { - if (!this.constructor.getGuildCache().has(this.gid)) this.constructor.getGuildCache().set(this.gid, new Discord.Collection()) + if (!this.constructor.getGuildCache().has(this.gid)) this.constructor.getGuildCache().set(this.gid, new Discord.Collection()); this.constructor.getGuildCache().get(this.gid).set(this.id, this); } else { @@ -160,6 +172,49 @@ class ChatTriggeredFeature { } } + /** + * Get a single bad word / autoresponse + * @param {String|Number} id + * @returns {Promise} + */ + static async getByID(id) { + const result = await database.query(`SELECT * FROM ${database.escapeId(this.tableName)} WHERE id = ?`, [id]); + if (!result) return null; + return new this(result.guildid, { + trigger: JSON.parse(result.trigger), + punishment: result.punishment, + response: result.response, + global: result.global === 1, + channels: result.channels.split(',') + }, result.id); + } + + /** + * get a trigger + * @param {String} type trigger type + * @param {String} value trigger value + * @returns {{trigger: Trigger, success: boolean, message: string}} + */ + static getTrigger(type, value) { + if (!this.triggerTypes.includes(type)) return {success: false, message: 'Unknown trigger type'}; + if (!value) return {success: false, message:'Empty triggers are not allowed'}; + + let content = value, flags; + if (type === 'regex') { + /** @type {String[]}*/ + let parts = value.split(/(? '; - -command.names = ['badword','badwords','blacklist']; - -command.execute = async (message, args, database, bot) => { - //Permission check - if (!message.member.hasPermission('MANAGE_GUILD')) { - await message.channel.send('You need the "Manage Server" permission to use this command.'); - return; - } - if (!args.length) { - return await message.channel.send(await util.usage(message,command.names[0])); - } - - let responses = await BadWord.getAll(message.guild.id); - - switch (args.shift().toLowerCase()) { - case 'list': - await list(responses,message); - break; - - case 'add': - await add(message); - break; - - case 'delete': - case 'remove': - await remove(responses, message, args); - break; - - case 'info': - await info(responses, message, args); - break; - - default: - return await message.channel.send(await util.usage(message,command.names[0])); - } -} - -module.exports = command; diff --git a/src/commands/legacy/badword/add.js b/src/commands/legacy/badword/add.js deleted file mode 100644 index 82f31300a..000000000 --- a/src/commands/legacy/badword/add.js +++ /dev/null @@ -1,101 +0,0 @@ -const BadWord = require('../../../BadWord'); -const util = require('../../../util.js'); - -/** - * add a bad word - * @param {module:"discord.js".Message} message - * @returns {Promise} - */ -module.exports = async (message) => { - await message.channel.send("Please enter your trigger type (\`regex\`, \`include\` or \`match\`)!"); - let type = await util.getResponse(message.channel,message.author.id); - - if (type === null) return; - - type = type.toLowerCase(); - - if (!BadWord.triggerTypes.includes(type)) { - return await message.channel.send("Not a valid trigger type!"); - } - - await message.channel.send(`Please enter your trigger (${ type === 'regex' ? '`/regex/flags`' :'`example trigger`'})!`); - let content = await util.getResponse(message.channel,message.author.id); - - if (content === null) return; - - content = content.replace(/^`(.*)`$/,(a,b) => b); - - let flags; - if (type === 'regex') { - let regex = content.split(/(?} - */ -module.exports = async (badWords, message, args) => { - if (!args.length) { - await message.channel.send("Provide the id of the bad word you want to view"); - return; - } - let badWord = badWords.get(parseInt(args.shift())); - if (!badWord) { - await message.channel.send("Invalid id!"); - return; - } - - await message.channel.send(badWord.embed("Bad word",util.color.green)); -}; diff --git a/src/commands/legacy/badword/list.js b/src/commands/legacy/badword/list.js deleted file mode 100644 index 0dfbf68d4..000000000 --- a/src/commands/legacy/badword/list.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * list bad words - * @param {module:"discord.js".Collection} badWords - * @param {module:"discord.js".Message} message - * @returns {Promise} - */ -module.exports = async (badWords, message) => { - if (!badWords.size) { - return await message.channel.send("No bad words!"); - } - - let text = ''; - for (const [id, badWord] of badWords) { - if(text.length > 1500){ - await message.channel.send(text.substring(0, 2000)); - text = ''; - } - text += `[${id}] ${badWord.global ? "global" : badWord.channels.map(c => `<#${c}>`).join(', ')} (${badWord.trigger.type}): \`${badWord.trigger.type === 'regex' ? '/' + badWord.trigger.content + '/' + badWord.trigger.flags : badWord.trigger.content}\` \n`; - } - await message.channel.send(text.substring(0, 2000)); -}; diff --git a/src/commands/legacy/badword/remove.js b/src/commands/legacy/badword/remove.js deleted file mode 100644 index 84f685ec6..000000000 --- a/src/commands/legacy/badword/remove.js +++ /dev/null @@ -1,45 +0,0 @@ -const util = require('../../../util.js'); -const icons = require('../../../icons'); - -/** - * remove a bad word - * @param {module:"discord.js".Collection} badWords - * @param {module:"discord.js".Message} message - * @param {String[]} args - * @returns {Promise} - */ -module.exports = async (badWords, message, args) => { - if (!args.length) { - await message.channel.send("Provide the id of the bad word you want to remove"); - return; - } - let badWord = badWords.get(parseInt(args.shift())); - if (!badWord) { - await message.channel.send("Invalid id!"); - return; - } - - let confirmation = await message.channel.send("Do you really want to delete this bad word?", badWord.embed("Remove bad word", util.color.red)); - { - let yes = confirmation.react(icons.yes); - let no = confirmation.react(icons.no); - await Promise.all([yes,no]); - } - - let confirmed; - try { - confirmed = (await confirmation.awaitReactions((reaction, user) => { - return user.id === message.author.id && (reaction.emoji.name === icons.yes || reaction.emoji.name === icons.no); - }, { max: 1, time: 15000, errors: ['time'] })).first().emoji.name === icons.yes; - } - catch { - return await message.channel.send("You took to long to react!"); - } - if (!confirmed) { - return await message.channel.send("Canceled!"); - } - - await badWord.remove(); - - await message.channel.send(`Removed the bad word with the id ${badWord.id}!`); -}; diff --git a/src/commands/settings/BadWordCommand.js b/src/commands/settings/BadWordCommand.js new file mode 100644 index 000000000..25ed026f0 --- /dev/null +++ b/src/commands/settings/BadWordCommand.js @@ -0,0 +1,162 @@ +const Command = require('../../Command'); +const Discord = require('discord.js'); +const util = require('../../util'); +const BadWord = require('../../BadWord'); + +class BadWordCommand extends Command { + + static description = 'Configure bad words'; + + static usage = 'list|add|remove|show|edit'; + + static subCommands = { + list: { + usage:'', + description: 'List all bad words' + }, + add: { + usage: 'global| regex|include|match ', + description: 'Add a bad word' + }, + remove: { + usage: '', + description: 'Remove a bad word' + }, + show: { + usage: '', + description: 'Display a bad word' + }, + edit: { + usage: ' trigger|response|punishment|priority|channels ', + description: 'change options of a bad word' + } + } + + static names = ['badword','badwords','blacklist']; + + static userPerms = ['MANAGE_GUILD']; + + async execute() { + if (this.args.length === 0) { + await this.sendUsage(); + return; + } + + /** @type {module:"discord.js".Collection} */ + const badWords = await BadWord.getAll(/** @type {Snowflake} */ this.message.guild.id); + + switch (this.args.shift().toLowerCase()) { + case 'list': { + if (!badWords.size) return this.message.channel.send('No bad words!'); + + let text = ''; + for (const [id, badWord] of badWords) { + const info = `[${id}] ${badWord.global ? 'global' : badWord.channels.map(c => `\`${c}\``).join(', ')} ` + + badWord.trigger.asString() + '\n'; + + if (text.length + info.length < 2000) { + text += info; + } else { + await this.message.channel.send(text); + text = info; + } + } + if (text.length) await this.message.channel.send(text); + break; + } + + case 'add': { + if (this.args.length < 2) return this.sendSubCommandUsage('add'); + + const global = this.args[0].toLowerCase() === 'global'; + + let channels; + console.log(!!this.message.guild.channels.resolve('749286933171142780')); + if (!global) channels = await util.channelMentions(this.message.guild, this.args); + else this.args.shift(); + + const type = this.args.shift().toLowerCase(); + const content = this.args.join(' '); + + let badWord = await BadWord.create(this.message.guild.id, global, channels, type, content); + if (!badWord.success) return this.message.channel.send(badWord.message); + + await this.message.channel.send(badWord.badWord.embed('Added new bad word', util.color.green)); + break; + } + + case 'remove': { + const badWord = await this.getBadWord(this.args.shift(), 'remove'); + if (!badWord) return; + await badWord.remove(); + await this.message.channel.send(badWord.embed(`Removed bad word ${badWord.id}`, util.color.red)); + break; + } + + case 'show': { + const badWord = await this.getBadWord(this.args.shift(), 'remove'); + if (!badWord) return; + await this.message.channel.send(badWord.embed(`Bad Word ${badWord.id}`, util.color.green)); + break; + } + + case 'edit': { + if (this.args.length < 3) return this.sendSubCommandUsage('edit'); + const badWord = await this.getBadWord(this.args.shift(), 'remove'); + if (!badWord) return; + + await this.message.channel.send(badWord.embed(await badWord.edit(this.args.shift(), this.args, this.message.guild), util.color.green)); + break; + } + + default: + await this.sendUsage(); + } + + } + + /** + * get a single bad word + * @param {String|Number} id + * @param {String} subCommand + * @returns {Promise} + */ + async getBadWord(id, subCommand) { + if (!id || !parseInt(id)) { + await this.sendSubCommandUsage(subCommand); + return null; + } + const result = await BadWord.getByID(id); + if (!result) { + await this.sendSubCommandUsage(subCommand); + return null; + } + return result; + } + + /** + * send usage for a subcommand + * @param {String} commandName sub command name + * @returns {Promise} + */ + async sendSubCommandUsage(commandName) { + commandName = commandName.toLowerCase(); + let subCommand = this.constructor.subCommands[commandName]; + if (!subCommand) throw 'Unknown subcommand'; + + return this.message.channel.send(new Discord.MessageEmbed() + .setAuthor(`Help for ${this.name} ${commandName} | Prefix: ${this.prefix}`) + .setFooter(`Command executed by ${util.escapeFormatting(this.message.author.tag)}`) + .addFields( + /** @type {any} */ + { name: 'Usage', value: `\`${this.prefix}${this.name} ${commandName} ${subCommand.usage}\``, inline: true}, + /** @type {any} */ { name: 'Description', value: subCommand.description, inline: true}, + /** @type {any} */ { name: 'Required Permissions', value: this.constructor.userPerms.map(p => `\`${p}\``).join(',') || 'none' } + ) + .setColor(util.color.green) + .setTimestamp() + ); + } +} + +module.exports = BadWordCommand; From c78a9b5f283f2cc482e668f798c9aaec9c95f3fa Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 2 Jul 2021 19:49:15 +0200 Subject: [PATCH 4/7] style badwordmod --- src/features/message/badwordmod.js | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/features/message/badwordmod.js b/src/features/message/badwordmod.js index 3ed376e9d..f11112abe 100644 --- a/src/features/message/badwordmod.js +++ b/src/features/message/badwordmod.js @@ -4,22 +4,22 @@ const Log = require('../../Log'); const strike = require('../../commands/legacy/strike'); exports.event = async (options, message) => { - if (!message.guild || await util.ignoresAutomod(message)) return; + if (!message.guild || await util.ignoresAutomod(message)) return; - const words = await BadWord.get(message.channel.id, message.guild.id); - for (let [,word] of words) { - if (word.matches(message)) { - const reason = 'Using forbidden words or phrases'; - await util.delete(message, { reason: reason } ); - if (word.response !== 'disabled') { - const response = await message.reply(word.response === 'default' ? BadWord.defaultResponse : word.response); - await util.delete(response, { timeout: 5000 }); - } - await Log.logMessageDeletion(message, reason); - if (word.punishment.action !== 'none') { - await strike.executePunishment(word.punishment, message.guild, message.author, options.bot, options.database, reason); - } - return; + const words = await BadWord.get(message.channel.id, message.guild.id); + for (let [,word] of words) { + if (word.matches(message)) { + const reason = 'Using forbidden words or phrases'; + await util.delete(message, { reason: reason } ); + if (word.response !== 'disabled') { + const response = await message.reply(word.response === 'default' ? BadWord.defaultResponse : word.response); + await util.delete(response, { timeout: 5000 }); + } + await Log.logMessageDeletion(message, reason); + if (word.punishment.action !== 'none') { + await strike.executePunishment(word.punishment, message.guild, message.author, options.bot, options.database, reason); + } + return; + } } - } }; From eaf85ba5f007b0c82e683f44389ad4d3b1800e99 Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 2 Jul 2021 20:00:34 +0200 Subject: [PATCH 5/7] add more things to embed, inline --- src/BadWord.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/BadWord.js b/src/BadWord.js index c7a87a482..6b7d56e83 100644 --- a/src/BadWord.js +++ b/src/BadWord.js @@ -67,28 +67,38 @@ class BadWord extends ChatTriggeredFeature { * @returns {module:"discord.js".MessageEmbed} */ embed(title, color) { - const embed = new Discord.MessageEmbed() + const duration = this.punishment.duration, trigger = this.trigger; + return new Discord.MessageEmbed() .setTitle(title + ` [${this.id}]`) .setColor(color) .addFields( /** @type {any} */[ { name: 'Trigger', - value: `${this.trigger.type}: \`${this.trigger.type === 'regex' ? '/' + this.trigger.content + '/' + this.trigger.flags : this.trigger.content}\``.substring(0, 1000) + value: `${trigger.type}: \`${trigger.type === 'regex' ? '/' + trigger.content + '/' + trigger.flags : trigger.content}\``.substring(0, 1000), + inline: true }, { name: 'Response', - value: this.response === 'default' ? BadWord.defaultResponse : this.response.substring(0, 1000) + value: this.response === 'default' ? BadWord.defaultResponse : this.response.substring(0, 1000), + inline: true }, { name: 'Channels', - value: this.global ? 'global' : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000) - } + value: this.global ? 'global' : this.channels.map(c => `<#${c}>`).join(', ').substring(0, 1000), + inline: true + }, + { + name: 'Punishment', + value: `${this.punishment.action} ${duration ? `for ${duration}` : ''}`, + inline: true + }, + { + name: 'Priority', + value: this.priority, + inline: true + }, ]); - if (this.punishment.action) { - embed.addField('Punishment', `${this.punishment.action} ${this.punishment.duration ? `for ${this.punishment.duration}` : ''}`); - } - return embed; } /** From 49e9e089666a576d131d197ea1610d07e480c87a Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 2 Jul 2021 20:07:49 +0200 Subject: [PATCH 6/7] style database --- src/Database.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Database.js b/src/Database.js index bdcd64760..56e7793ca 100644 --- a/src/Database.js +++ b/src/Database.js @@ -59,7 +59,7 @@ class Database { * @private */ _handleConnectionError(err) { - monitor.error('A fatal database error occurred', err) + monitor.error('A fatal database error occurred', err); if (err.code === 'ER_ACCESS_DENIED_ERROR') { console.error('Access to database denied. Make sure your config and database are set up correctly!'); process.exit(1); @@ -79,11 +79,11 @@ class Database { * @return {Promise} */ async createTables() { - await this.query("CREATE TABLE IF NOT EXISTS `channels` (`id` VARCHAR(20) NOT NULL, `config` TEXT NOT NULL, PRIMARY KEY (`id`), `guildid` VARCHAR(20))"); - await this.query("CREATE TABLE IF NOT EXISTS `guilds` (`id` VARCHAR(20) NOT NULL, `config` TEXT NOT NULL, PRIMARY KEY (`id`))"); - await this.query("CREATE TABLE IF NOT EXISTS `responses` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL)"); - await this.query("CREATE TABLE IF NOT EXISTS `badWords` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `punishment` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL)"); - await this.query("CREATE TABLE IF NOT EXISTS `moderations` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `userid` VARCHAR(20) NOT NULL, `action` VARCHAR(10) NOT NULL,`created` bigint NOT NULL, `value` int DEFAULT 0,`expireTime` bigint NULL DEFAULT NULL, `reason` TEXT,`moderator` VARCHAR(20) NULL DEFAULT NULL, `active` BOOLEAN DEFAULT TRUE)"); + await this.query('CREATE TABLE IF NOT EXISTS `channels` (`id` VARCHAR(20) NOT NULL, `config` TEXT NOT NULL, PRIMARY KEY (`id`), `guildid` VARCHAR(20))'); + await this.query('CREATE TABLE IF NOT EXISTS `guilds` (`id` VARCHAR(20) NOT NULL, `config` TEXT NOT NULL, PRIMARY KEY (`id`))'); + await this.query('CREATE TABLE IF NOT EXISTS `responses` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL)'); + await this.query('CREATE TABLE IF NOT EXISTS `badWords` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `punishment` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL)'); + await this.query('CREATE TABLE IF NOT EXISTS `moderations` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `userid` VARCHAR(20) NOT NULL, `action` VARCHAR(10) NOT NULL,`created` bigint NOT NULL, `value` int DEFAULT 0,`expireTime` bigint NULL DEFAULT NULL, `reason` TEXT,`moderator` VARCHAR(20) NULL DEFAULT NULL, `active` BOOLEAN DEFAULT TRUE)'); } /** @@ -159,11 +159,11 @@ class Database { */ async addModeration(guildId, userId, action, reason, duration, moderatorId) { //disable old moderations - await this.query("UPDATE moderations SET active = FALSE WHERE active = TRUE AND guildid = ? AND userid = ? AND action = ?", [guildId, userId, action]); + await this.query('UPDATE moderations SET active = FALSE WHERE active = TRUE AND guildid = ? AND userid = ? AND action = ?', [guildId, userId, action]); const now = Math.floor(Date.now()/1000); /** @property {Number} insertId*/ - const insert = await this.queryAll("INSERT INTO moderations (guildid, userid, action, created, expireTime, reason, moderator) VALUES (?,?,?,?,?,?,?)",[guildId, userId, action, now, duration ? now + duration : null, reason, moderatorId]); + const insert = await this.queryAll('INSERT INTO moderations (guildid, userid, action, created, expireTime, reason, moderator) VALUES (?,?,?,?,?,?,?)',[guildId, userId, action, now, duration ? now + duration : null, reason, moderatorId]); return insert.insertId; } } From 0a0f4381bef28b2ec37711e8d94c99ed6946a77d Mon Sep 17 00:00:00 2001 From: Julian Date: Fri, 2 Jul 2021 21:07:57 +0200 Subject: [PATCH 7/7] add priority, fixes --- package.json | 2 +- src/BadWord.js | 6 +- src/ChatTriggeredFeature.js | 83 ++++++++++++++----------- src/Database.js | 11 +++- src/commands/settings/BadWordCommand.js | 5 +- src/features/message/badwordmod.js | 2 +- update/0.5.0.js | 18 ++++++ 7 files changed, 81 insertions(+), 46 deletions(-) create mode 100644 update/0.5.0.js diff --git a/package.json b/package.json index cf97897c0..38930750f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "modbot", - "version": "0.4.0", + "version": "0.5.0", "description": "Discord Bot for the Aternos Discord server", "main": "index.js", "scripts": { diff --git a/src/BadWord.js b/src/BadWord.js index 6b7d56e83..174dbc270 100644 --- a/src/BadWord.js +++ b/src/BadWord.js @@ -13,7 +13,7 @@ class BadWord extends ChatTriggeredFeature { static tableName = 'badWords'; - static columns = ['guildid', 'trigger', 'punishment', 'response', 'global', 'channels']; + static columns = ['guildid', 'trigger', 'punishment', 'response', 'global', 'channels', 'priority']; /** * constructor - create a bad word @@ -57,7 +57,7 @@ class BadWord extends ChatTriggeredFeature { * @returns {(*|string)[]} */ serialize() { - return [this.gid, JSON.stringify(this.trigger), JSON.stringify(this.punishment), this.response, this.global, this.channels.join(',')]; + return [this.gid, JSON.stringify(this.trigger), JSON.stringify(this.punishment), this.response, this.global, this.channels.join(','), this.priority]; } /** @@ -110,7 +110,7 @@ class BadWord extends ChatTriggeredFeature { * @param {String} triggerContent * @returns {Promise<{success:boolean, badWord: BadWord, message: String}>} */ - static async create(guildID, global, channels, triggerType, triggerContent) { + static async new(guildID, global, channels, triggerType, triggerContent) { let trigger = this.getTrigger(triggerType, triggerContent); if (!trigger.success) return trigger; diff --git a/src/ChatTriggeredFeature.js b/src/ChatTriggeredFeature.js index 13d31d87e..91d85b573 100644 --- a/src/ChatTriggeredFeature.js +++ b/src/ChatTriggeredFeature.js @@ -124,30 +124,48 @@ class ChatTriggeredFeature { return false; } + /** + * serialize this object + * must return data in same order as the static columns array + * @returns {(*|string)[]} + */ + serialize() { + throw 'Abstract method not overridden!'; + } + /** * Save to db and cache * @async * @return {Promise} id in db */ async save() { - if (!this.channels) this.channels = null; - - let dbentry = await database.queryAll(`INSERT INTO ${database.escapeId(this.constructor.tableName)} (${database.escapeIdArray(this.constructor.columns).join(', ')}) VALUES (${',?'.repeat(this.constructor.columns.length).slice(1)})`,this.serialize()); - - this.id = dbentry.insertId; + if (this.id) { + let assignments = [], + columns = this.constructor.columns, + data = this.serialize(); + for (let i = 0; i < columns.length; i++) { + assignments.push(`${database.escapeId(columns[i])}=${database.escapeValue(data[i])}`); + } + if (data.length !== columns.length) throw 'Unable to update, lengths differ!'; + await database.queryAll(`UPDATE ${database.escapeId(this.constructor.tableName)} SET ${assignments.join(', ')} WHERE id = ?`, [this.id]); + } + else { + let dbentry = await database.queryAll(`INSERT INTO ${database.escapeId(this.constructor.tableName)} (${database.escapeIdArray(this.constructor.columns).join(', ')}) VALUES (${',?'.repeat(this.constructor.columns.length).slice(1)})`,this.serialize()); + this.id = dbentry.insertId; + } if (this.global) { - if (!this.constructor.getGuildCache().has(this.gid)) this.constructor.getGuildCache().set(this.gid, new Discord.Collection()); + if (!this.constructor.getGuildCache().has(this.gid)) return this.id; this.constructor.getGuildCache().get(this.gid).set(this.id, this); } else { for (const channel of this.channels) { - if(!this.constructor.getChannelCache().has(channel)) this.constructor.getChannelCache().set(channel, new Discord.Collection()); + if(!this.constructor.getChannelCache().has(channel)) continue; this.constructor.getChannelCache().get(channel).set(this.id, this); } } - return dbentry.insertId; + return this.id; } /** @@ -172,6 +190,22 @@ class ChatTriggeredFeature { } } + /** + * create this object from data retrieved from the database + * @param data + * @returns {Promise} + */ + static fromData(data) { + return new this(data.guildid, { + trigger: JSON.parse(data.trigger), + punishment: data.punishment, + response: data.response, + global: data.global === 1, + channels: data.channels.split(','), + priority: data.priority + }, data.id); + } + /** * Get a single bad word / autoresponse * @param {String|Number} id @@ -180,13 +214,7 @@ class ChatTriggeredFeature { static async getByID(id) { const result = await database.query(`SELECT * FROM ${database.escapeId(this.tableName)} WHERE id = ?`, [id]); if (!result) return null; - return new this(result.guildid, { - trigger: JSON.parse(result.trigger), - punishment: result.punishment, - response: result.response, - global: result.global === 1, - channels: result.channels.split(',') - }, result.id); + return this.fromData(result); } /** @@ -246,13 +274,7 @@ class ChatTriggeredFeature { const collection = new Discord.Collection(); for (const res of result) { - collection.set(res.id, new this(res.guildid, { - trigger: JSON.parse(res.trigger), - punishment: res.punishment, - response: res.response, - global: res.global === 1, - channels: res.channels.split(',') - }, res.id)); + collection.set(res.id, this.fromData(res)); } return collection.sort((a, b) => a.id - b.id); @@ -268,14 +290,7 @@ class ChatTriggeredFeature { const newItems = new Discord.Collection(); for (const res of result) { - const o = new this(res.guildid, { - trigger: JSON.parse(res.trigger), - punishment: res.punishment, - response: res.response, - global: true, - channels: [] - }, res.id); - newItems.set(res.id, o); + newItems.set(res.id, this.fromData(res)); } this.getGuildCache().set(guildId, newItems); setTimeout(() => { @@ -293,13 +308,7 @@ class ChatTriggeredFeature { const newItems = new Discord.Collection(); for (const res of result) { - newItems.set(res.id, new this(res.guildid, { - trigger: JSON.parse(res.trigger), - response: res.response, - punishment: res.punishment, - global: false, - channels: res.channels.split(',') - }, res.id)); + newItems.set(res.id, this.fromData(res)); } this.getChannelCache().set(channelId, newItems); setTimeout(() => { diff --git a/src/Database.js b/src/Database.js index 56e7793ca..8e43174c5 100644 --- a/src/Database.js +++ b/src/Database.js @@ -82,7 +82,7 @@ class Database { await this.query('CREATE TABLE IF NOT EXISTS `channels` (`id` VARCHAR(20) NOT NULL, `config` TEXT NOT NULL, PRIMARY KEY (`id`), `guildid` VARCHAR(20))'); await this.query('CREATE TABLE IF NOT EXISTS `guilds` (`id` VARCHAR(20) NOT NULL, `config` TEXT NOT NULL, PRIMARY KEY (`id`))'); await this.query('CREATE TABLE IF NOT EXISTS `responses` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL)'); - await this.query('CREATE TABLE IF NOT EXISTS `badWords` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `punishment` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL)'); + await this.query('CREATE TABLE IF NOT EXISTS `badWords` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `trigger` TEXT NOT NULL, `punishment` TEXT NOT NULL, `response` TEXT NOT NULL, `global` BOOLEAN NOT NULL, `channels` TEXT NULL DEFAULT NULL, `priority` int NULL)'); await this.query('CREATE TABLE IF NOT EXISTS `moderations` (`id` int PRIMARY KEY AUTO_INCREMENT, `guildid` VARCHAR(20) NOT NULL, `userid` VARCHAR(20) NOT NULL, `action` VARCHAR(10) NOT NULL,`created` bigint NOT NULL, `value` int DEFAULT 0,`expireTime` bigint NULL DEFAULT NULL, `reason` TEXT,`moderator` VARCHAR(20) NULL DEFAULT NULL, `active` BOOLEAN DEFAULT TRUE)'); } @@ -132,6 +132,15 @@ class Database { return mysql.escapeId(...args); } + /** + * escape a value + * @param args + * @returns {string} + */ + escapeValue(...args) { + return mysql.escape(...args); + } + /** * Escape an array of table/column names * diff --git a/src/commands/settings/BadWordCommand.js b/src/commands/settings/BadWordCommand.js index 25ed026f0..d6cbbea82 100644 --- a/src/commands/settings/BadWordCommand.js +++ b/src/commands/settings/BadWordCommand.js @@ -32,7 +32,7 @@ class BadWordCommand extends Command { } } - static names = ['badword','badwords','blacklist']; + static names = ['badword','badwords','bw']; static userPerms = ['MANAGE_GUILD']; @@ -71,14 +71,13 @@ class BadWordCommand extends Command { const global = this.args[0].toLowerCase() === 'global'; let channels; - console.log(!!this.message.guild.channels.resolve('749286933171142780')); if (!global) channels = await util.channelMentions(this.message.guild, this.args); else this.args.shift(); const type = this.args.shift().toLowerCase(); const content = this.args.join(' '); - let badWord = await BadWord.create(this.message.guild.id, global, channels, type, content); + let badWord = await BadWord.new(this.message.guild.id, global, channels, type, content); if (!badWord.success) return this.message.channel.send(badWord.message); await this.message.channel.send(badWord.badWord.embed('Added new bad word', util.color.green)); diff --git a/src/features/message/badwordmod.js b/src/features/message/badwordmod.js index f11112abe..d38afcbdc 100644 --- a/src/features/message/badwordmod.js +++ b/src/features/message/badwordmod.js @@ -6,7 +6,7 @@ const strike = require('../../commands/legacy/strike'); exports.event = async (options, message) => { if (!message.guild || await util.ignoresAutomod(message)) return; - const words = await BadWord.get(message.channel.id, message.guild.id); + const words = (await BadWord.get(message.channel.id, message.guild.id)).sort((a,b) => b.priority - a.priority); for (let [,word] of words) { if (word.matches(message)) { const reason = 'Using forbidden words or phrases'; diff --git a/update/0.5.0.js b/update/0.5.0.js new file mode 100644 index 000000000..ebe4159c8 --- /dev/null +++ b/update/0.5.0.js @@ -0,0 +1,18 @@ +const Database = require('../src/Database'); +const config = require('../config.json'); +const database = new Database(config.db); + +async function update() { + console.log('Starting update to v0.5.0'); + + console.log('Updating tables'); + await database.waitForConnection(); + await database.query('ALTER TABLE badWords ADD `priority` int NULL;'); + console.log('Done!'); + process.exit(0); +} + +update().catch(e => { + console.error(e); + process.exit(1); +});