diff --git a/README.md b/README.md index c7a6376..ab35b1a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# The Artificer - A Dice Rolling Discord Bot | V2.0.3 - 2022/07/09 +# The Artificer - A Dice Rolling Discord Bot | V2.1.0 - 2022/07/10 [![SonarCloud](https://sonarcloud.io/images/project_badges/sonarcloud-orange.svg)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=bugs)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=TheArtificer) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=TheArtificer&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=TheArtificer) @@ -59,6 +59,10 @@ The Artificer comes with a few supplemental commands to the main rolling command * If you encounter a command that errors out or returns something unexpected, please use this command to alert the developers of the problem. * Example: * `[[report [[2+2]] returned 5 when I expected it to return 4` will send the entire message after `[[report` to the devs via Discord. +* `[[opt-out` or `[[ignore-me` + * Adds you to an ignore list so the bot will never respond to you +* `[[opt-in` + * Removes you from the ignore list * `[[xdydzracsq!]]` * This is the command the bot was built specifically for. * It looks a little complicated at first, but if you are familiar with the [Roll20 formatting](https://artificer.eanm.dev/roll20), this will no different. diff --git a/config.example.ts b/config.example.ts index f13cb3b..ee0f2be 100644 --- a/config.example.ts +++ b/config.example.ts @@ -1,6 +1,6 @@ export const config = { 'name': 'The Artificer', // Name of the bot - 'version': '2.0.3', // Version of the bot + 'version': '2.1.0', // Version of the bot 'token': 'the_bot_token', // Discord API Token for this bot 'localtoken': 'local_testing_token', // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token" 'prefix': '[[', // Prefix for all commands diff --git a/db/initialize.ts b/db/initialize.ts index 9986eeb..b6175ac 100644 --- a/db/initialize.ts +++ b/db/initialize.ts @@ -19,8 +19,20 @@ await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_HEATMAP;`); await dbClient.execute(`DROP TABLE IF EXISTS roll_time_heatmap;`); await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`); await dbClient.execute(`DROP TABLE IF EXISTS command_cnt;`); +await dbClient.execute(`DROP TABLE IF EXISTS ignore_list;`); console.log('Tables dropped'); +// Table to hold list of users who want to be ignored by the bot +console.log('Attempting to create table ignore_list'); +await dbClient.execute(` + CREATE TABLE ignore_list ( + userid bigint unsigned NOT NULL, + PRIMARY KEY (userid), + UNIQUE KEY ignore_list_userid_UNIQUE (userid) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +`); +console.log('Table created'); + // Light telemetry on how many commands have been run console.log('Attempting to create table command_cnt'); await dbClient.execute(` diff --git a/db/populateDefaults.ts b/db/populateDefaults.ts index 0d48521..192a625 100644 --- a/db/populateDefaults.ts +++ b/db/populateDefaults.ts @@ -10,7 +10,7 @@ await dbClient.execute('INSERT INTO all_keys(userid,apiKey) values(?,?)', [confi console.log('Inesrtion done'); console.log('Attempting to insert default commands into command_cnt'); -const commands = ['ping', 'rip', 'rollhelp', 'help', 'info', 'version', 'report', 'stats', 'roll', 'emojis', 'api', 'privacy', 'mention', 'audit', 'heatmap', 'rollDecorators']; +const commands = ['ping', 'rip', 'rollhelp', 'help', 'info', 'version', 'report', 'stats', 'roll', 'emojis', 'api', 'privacy', 'mention', 'audit', 'heatmap', 'rollDecorators', 'opt-out', 'opt-in']; for (const command of commands) { await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [command]).catch((e) => { console.log(`Failed to insert ${command} into database`, e); diff --git a/mod.ts b/mod.ts index c05d54a..4126a75 100644 --- a/mod.ts +++ b/mod.ts @@ -25,7 +25,7 @@ import { startBot, } from './deps.ts'; import api from './src/api.ts'; -import { dbClient } from './src/db.ts'; +import { dbClient, ignoreList } from './src/db.ts'; import commands from './src/commands/_index.ts'; import intervals from './src/intervals.ts'; import { successColor, warnColor } from './src/commandUtils.ts'; @@ -173,6 +173,9 @@ startBot({ // Ignore all other bots if (message.isBot) return; + // Ignore users who requested to be ignored + if (ignoreList.includes(message.authorId) && !message.content.startsWith(`${config.prefix}opt-in`)) return; + // Ignore all messages that are not commands if (message.content.indexOf(config.prefix) !== 0) { // Handle @bot messages @@ -193,6 +196,17 @@ startBot({ // All commands below here switch (command) { + case 'opt-out': + case 'ignore-me': + // [[opt-out or [[ignore-me + // Tells the bot to add you to the ignore list. + commands.optOut(message); + break; + case 'opt-in': + // [[opt-in + // Tells the bot to remove you from the ignore list. + commands.optIn(message); + break; case 'ping': // [[ping // Its a ping test, what else do you want. diff --git a/src/commands/_index.ts b/src/commands/_index.ts index 6f3dc19..2395f4e 100644 --- a/src/commands/_index.ts +++ b/src/commands/_index.ts @@ -14,6 +14,8 @@ import { roll } from './roll.ts'; import { handleMentions } from './handleMentions.ts'; import { audit } from './audit.ts'; import { heatmap } from './heatmap.ts'; +import { optOut } from './optOut.ts'; +import { optIn } from './optIn.ts'; export default { ping, @@ -32,4 +34,6 @@ export default { handleMentions, audit, heatmap, + optOut, + optIn, }; diff --git a/src/commands/help.ts b/src/commands/help.ts index e4ce8ec..e380500 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -76,6 +76,16 @@ export const help = (message: DiscordenoMessage) => { value: 'Heatmap of when the roll command is run the most', inline: true, }, + { + name: `\`${config.prefix}opt-out\` or \`${config.prefix}ignore-me\``, + value: 'Adds you to an ignore list so the bot will never respond to you', + inline: true, + }, + { + name: `\`${config.prefix}opt-in\``, + value: 'Removes you from the ignore list', + inline: true, + }, { name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`, value: diff --git a/src/commands/info.ts b/src/commands/info.ts index cf6b3c2..f723cc4 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -1,3 +1,4 @@ +import config from '../../config.ts'; import { dbClient, queries } from '../db.ts'; import { // Discordeno deps @@ -8,16 +9,16 @@ import utils from '../utils.ts'; export const info = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('info')).catch((e) => utils.commonLoggers.dbError('info.ts:14', 'call sproc INC_CNT on', e)); + dbClient.execute(queries.callIncCnt('info')).catch((e) => utils.commonLoggers.dbError('info.ts:12', 'call sproc INC_CNT on', e)); message.send({ embeds: [{ color: infoColor2, - title: 'The Artificer, a Discord bot that specializing in rolling dice and calculating math', - description: `The Artificer is developed by Ean AKA Burn_E99. + title: `${config.name}, a Discord bot that specializing in rolling dice and calculating math`, + description: `${config.name} is developed by Ean AKA Burn_E99. Additional information can be found on my website [here](https://discord.burne99.com/TheArtificer/). Want to check out my source code? Check it out [here](https://github.com/Burn-E99/TheArtificer). Need help with this bot? Join my support server [here](https://discord.gg/peHASXMZYv).`, }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('info.ts:27', message, e)); + }).catch((e: Error) => utils.commonLoggers.messageSendError('info.ts:23', message, e)); }; diff --git a/src/commands/optIn.ts b/src/commands/optIn.ts new file mode 100644 index 0000000..a491f5c --- /dev/null +++ b/src/commands/optIn.ts @@ -0,0 +1,39 @@ +import config from '../../config.ts'; +import { dbClient, ignoreList, queries } from '../db.ts'; +import { + // Discordeno deps + DiscordenoMessage, +} from '../../deps.ts'; +import { successColor, failColor } from '../commandUtils.ts'; +import utils from '../utils.ts'; + +export const optIn = async (message: DiscordenoMessage) => { + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('opt-out')).catch((e) => utils.commonLoggers.dbError('optIn.ts:11', 'call sproc INC_CNT on', e)); + + try { + const idIdx = ignoreList.indexOf(message.authorId); + if (idIdx !== -1) { + ignoreList.splice(idIdx, 1); + await dbClient.execute('DELETE FROM ignore_list WHERE userid = ?', [message.authorId]); + } + + message.reply({ + embeds: [{ + color: successColor, + title: `${config.name} will now respond to you again.`, + description: `If you want ${config.name} to ignore to you again, please run the following command: + +\`${config.prefix}opt-out\``, + }], + }).catch((e: Error) => utils.commonLoggers.messageSendError('optIn.ts:27', message, e)); + } catch (err) { + message.reply({ + embeds: [{ + color: failColor, + title: 'Opt-In failed', + description: 'Please try the command again. If the issue persists, please join the support server, linked in my About Me section.', + }], + }).catch((e: Error) => utils.commonLoggers.messageSendError('optIn.ts:27', message, e)); + } +}; diff --git a/src/commands/optOut.ts b/src/commands/optOut.ts new file mode 100644 index 0000000..ca31921 --- /dev/null +++ b/src/commands/optOut.ts @@ -0,0 +1,36 @@ +import config from '../../config.ts'; +import { dbClient, ignoreList, queries } from '../db.ts'; +import { + // Discordeno deps + DiscordenoMessage, +} from '../../deps.ts'; +import { successColor, failColor } from '../commandUtils.ts'; +import utils from '../utils.ts'; + +export const optOut = async (message: DiscordenoMessage) => { + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('opt-out')).catch((e) => utils.commonLoggers.dbError('optOut.ts:11', 'call sproc INC_CNT on', e)); + + try { + ignoreList.push(message.authorId); + await dbClient.execute('INSERT INTO ignore_list(userid) values(?)', [message.authorId]); + + message.reply({ + embeds: [{ + color: successColor, + title: `${config.name} will no longer respond to you.`, + description: `If you want ${config.name} to respond to you again, please run the following command: + +\`${config.prefix}opt-in\``, + }], + }).catch((e: Error) => utils.commonLoggers.messageSendError('optOut.ts:25', message, e)); + } catch (err) { + message.reply({ + embeds: [{ + color: failColor, + title: 'Opt-Out failed', + description: `Please try the command again. If the issue persists, please report this using the \`${config.prefix}report opt-out failed\` command.`, + }], + }).catch((e: Error) => utils.commonLoggers.messageSendError('optOut.ts:33', message, e)); + } +}; diff --git a/src/db.ts b/src/db.ts index 155fea8..a73cedb 100644 --- a/src/db.ts +++ b/src/db.ts @@ -2,6 +2,10 @@ import config from '../config.ts'; import { Client } from '../deps.ts'; import { LOCALMODE } from '../flags.ts'; +type UserIdObj = { + userid: bigint; +}; + export const dbClient = await new Client().connect({ hostname: LOCALMODE ? config.db.localhost : config.db.host, port: config.db.port, @@ -10,6 +14,13 @@ export const dbClient = await new Client().connect({ password: config.db.password, }); +// List of userIds who have requested that the bot ignore them +export const ignoreList: Array = []; +const dbIgnoreList = await dbClient.query("SELECT * FROM ignore_list"); +dbIgnoreList.forEach((userIdObj: UserIdObj) => { + ignoreList.push(userIdObj.userid); +}); + export const weekDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; export const queries = {