diff --git a/db/initialize.ts b/db/initialize.ts index f576d60..6569c81 100644 --- a/db/initialize.ts +++ b/db/initialize.ts @@ -1,25 +1,25 @@ // This file will create all tables for the artificer schema // DATA WILL BE LOST IF DB ALREADY EXISTS, RUN AT OWN RISK -import config from "../config.ts"; -import { dbClient } from "../src/db.ts"; +import config from '../config.ts'; +import { dbClient } from '../src/db.ts'; -console.log("Attempting to create DB"); +console.log('Attempting to create DB'); await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`); await dbClient.execute(`USE ${config.db.name}`); -console.log("DB created"); +console.log('DB created'); -console.log("Attempt to drop all tables"); +console.log('Attempt to drop all tables'); await dbClient.execute(`DROP TABLE IF EXISTS allowed_channels;`); await dbClient.execute(`DROP TABLE IF EXISTS all_keys;`); await dbClient.execute(`DROP TABLE IF EXISTS allowed_guilds;`); await dbClient.execute(`DROP TABLE IF EXISTS roll_log;`); await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`); await dbClient.execute(`DROP TABLE IF EXISTS command_cnt;`); -console.log("Tables dropped"); +console.log('Tables dropped'); // Light telemetry on how many commands have been run -console.log("Attempting to create table command_cnt"); +console.log('Attempting to create table command_cnt'); await dbClient.execute(` CREATE TABLE command_cnt ( command char(20) NOT NULL, @@ -28,9 +28,9 @@ await dbClient.execute(` UNIQUE KEY command_cnt_command_UNIQUE (command) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `); -console.log("Table created"); +console.log('Table created'); -console.log("Attempt creating increment Stored Procedure"); +console.log('Attempt creating increment Stored Procedure'); await dbClient.execute(` CREATE PROCEDURE INC_CNT( IN cmd CHAR(20) @@ -41,10 +41,10 @@ await dbClient.execute(` UPDATE command_cnt SET count = oldcnt + 1 WHERE command = cmd; END `); -console.log("Stored Procedure created"); +console.log('Stored Procedure created'); // Roll log, holds rolls when requests -console.log("Attempting to create table roll_log"); +console.log('Attempting to create table roll_log'); await dbClient.execute(` CREATE TABLE roll_log ( id int unsigned NOT NULL AUTO_INCREMENT, @@ -59,10 +59,10 @@ await dbClient.execute(` UNIQUE KEY roll_log_resultid_UNIQUE (resultid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `); -console.log("Table created"); +console.log('Table created'); // Api guild settings -console.log("Attempting to create table allowed_guilds"); +console.log('Attempting to create table allowed_guilds'); await dbClient.execute(` CREATE TABLE allowed_guilds ( guildid bigint unsigned NOT NULL, @@ -74,10 +74,10 @@ await dbClient.execute(` PRIMARY KEY (guildid, channelid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `); -console.log("Table created"); +console.log('Table created'); // Api keys -console.log("Attempting to create table all_keys"); +console.log('Attempting to create table all_keys'); await dbClient.execute(` CREATE TABLE all_keys ( userid bigint unsigned NOT NULL, @@ -93,10 +93,10 @@ await dbClient.execute(` UNIQUE KEY all_keys_email_UNIQUE (email) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `); -console.log("Table created"); +console.log('Table created'); // Api user settings -console.log("Attempting to create table allowed_channels"); +console.log('Attempting to create table allowed_channels'); await dbClient.execute(` CREATE TABLE allowed_channels ( userid bigint unsigned NOT NULL, @@ -108,7 +108,7 @@ await dbClient.execute(` CONSTRAINT allowed_channels_userid_FK FOREIGN KEY (userid) REFERENCES all_keys (userid) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `); -console.log("Table created"); +console.log('Table created'); await dbClient.close(); -console.log("Done!"); +console.log('Done!'); diff --git a/db/populateDefaults.ts b/db/populateDefaults.ts index 74229c7..108e0b9 100644 --- a/db/populateDefaults.ts +++ b/db/populateDefaults.ts @@ -1,22 +1,22 @@ // This file will populate the tables with default values -import config from "../config.ts"; -import { dbClient } from "../src/db.ts"; +import config from '../config.ts'; +import { dbClient } from '../src/db.ts'; -console.log("Attempting to populate DB Admin API key"); -await dbClient.execute("INSERT INTO all_keys(userid,apiKey) values(?,?)", [config.api.admin, config.api.adminKey]).catch(e => { - console.log("Failed to insert into database", e); +console.log('Attempting to populate DB Admin API key'); +await dbClient.execute('INSERT INTO all_keys(userid,apiKey) values(?,?)', [config.api.admin, config.api.adminKey]).catch((e) => { + console.log('Failed to insert into database', e); }); -console.log("Inesrtion done"); +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"]; +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']; for (let i = 0; i < commands.length; i++) { - await dbClient.execute("INSERT INTO command_cnt(command) values(?)", [commands[i]]).catch(e => { + await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [commands[i]]).catch((e) => { console.log(`Failed to insert into database`, e); }); } -console.log("Insertion done"); +console.log('Insertion done'); await dbClient.close(); -console.log("Done!"); +console.log('Done!'); diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..df465f1 --- /dev/null +++ b/deno.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "allowJs": true, + "lib": ["deno.window"], + "strict": true + }, + "lint": { + "files": { + "include": ["src/", "db/", "mod.ts"], + "exclude": [] + }, + "rules": { + "tags": ["recommended"], + "include": ["ban-untagged-todo"], + "exclude": [] + } + }, + "fmt": { + "files": { + "include": ["src/", "db/", "mod.ts"], + "exclude": [] + }, + "options": { + "useTabs": true, + "lineWidth": 200, + "indentWidth": 2, + "singleQuote": true, + "proseWrap": "preserve" + } + } +} \ No newline at end of file diff --git a/mod.ts b/mod.ts index 701469c..0c8b632 100644 --- a/mod.ts +++ b/mod.ts @@ -4,26 +4,32 @@ * December 21, 2020 */ -import config from "./config.ts"; -import { DEBUG, DEVMODE, LOCALMODE } from "./flags.ts"; +import config from './config.ts'; +import { DEBUG, DEVMODE, LOCALMODE } from './flags.ts'; import { - // Discordeno deps - startBot, editBotStatus, editBotNickname, + botId, + cache, + DiscordActivityTypes, + DiscordenoGuild, + DiscordenoMessage, + editBotNickname, + editBotStatus, + initLog, Intents, - sendMessage, - cache, botId, - DiscordActivityTypes, DiscordenoGuild, DiscordenoMessage, - + log, // Log4Deno deps - LT, initLog, log -} from "./deps.ts"; -import api from "./src/api.ts"; -import commands from "./src/commands/_index.ts"; -import intervals from "./src/intervals.ts"; -import utils from "./src/utils.ts"; + LT, + sendMessage, + // Discordeno deps + startBot, +} from './deps.ts'; +import api from './src/api.ts'; +import commands from './src/commands/_index.ts'; +import intervals from './src/intervals.ts'; +import utils from './src/utils.ts'; // Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup -initLog("logs", DEBUG); +initLog('logs', DEBUG); // Start up the Discord Bot startBot({ @@ -34,25 +40,25 @@ startBot({ log(LT.INFO, `${config.name} Logged in!`); editBotStatus({ activities: [{ - name: "Booting up . . .", + name: 'Booting up . . .', type: DiscordActivityTypes.Game, - createdAt: new Date().getTime() + createdAt: new Date().getTime(), }], - status: "online" + status: 'online', }); // Interval to rotate the status text every 30 seconds to show off more commands setInterval(async () => { - log(LT.LOG, "Changing bot status"); + log(LT.LOG, 'Changing bot status'); try { // Wrapped in try-catch due to hard crash possible editBotStatus({ activities: [{ name: await intervals.getRandomStatus(), type: DiscordActivityTypes.Game, - createdAt: new Date().getTime() + createdAt: new Date().getTime(), }], - status: "online" + status: 'online', }); } catch (e) { log(LT.ERROR, `Failed to update status: ${JSON.stringify(e)}`); @@ -60,45 +66,45 @@ startBot({ }, 30000); // Interval to update bot list stats every 24 hours - LOCALMODE ? log(LT.INFO, "updateListStatistics not running") : setInterval(() => { - log(LT.LOG, "Updating all bot lists statistics"); + LOCALMODE ? log(LT.INFO, 'updateListStatistics not running') : setInterval(() => { + log(LT.LOG, 'Updating all bot lists statistics'); intervals.updateListStatistics(botId, cache.guilds.size); }, 86400000); // setTimeout added to make sure the startup message does not error out setTimeout(() => { LOCALMODE && editBotNickname(config.devServer, `LOCAL - ${config.name}`); - LOCALMODE ? log(LT.INFO, "updateListStatistics not running") : intervals.updateListStatistics(botId, cache.guilds.size); + LOCALMODE ? log(LT.INFO, 'updateListStatistics not running') : intervals.updateListStatistics(botId, cache.guilds.size); editBotStatus({ activities: [{ - name: "Booting Complete", + name: 'Booting Complete', type: DiscordActivityTypes.Game, - createdAt: new Date().getTime() + createdAt: new Date().getTime(), }], - status: "online" + status: 'online', }); - sendMessage(config.logChannel, `${config.name} has started, running version ${config.version}.`).catch(e => { + sendMessage(config.logChannel, `${config.name} has started, running version ${config.version}.`).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); }); }, 1000); }, guildCreate: (guild: DiscordenoGuild) => { log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`); - sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch(e => { + sendMessage(config.logChannel, `New guild joined: ${guild.name} (id: ${guild.id}). This guild has ${guild.memberCount} members!`).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); }); }, guildDelete: (guild: DiscordenoGuild) => { log(LT.LOG, `Handling leaving guild ${JSON.stringify(guild)}`); - sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch(e => { + sendMessage(config.logChannel, `I have been removed from: ${guild.name} (id: ${guild.id}).`).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(e)}`); }); }, - debug: DEVMODE ? dmsg => log(LT.LOG, `Debug Message | ${JSON.stringify(dmsg)}`) : () => {}, + debug: DEVMODE ? (dmsg) => log(LT.LOG, `Debug Message | ${JSON.stringify(dmsg)}`) : () => {}, messageCreate: (message: DiscordenoMessage) => { // Ignore all other bots if (message.isBot) return; - + // Ignore all messages that are not commands if (message.content.indexOf(config.prefix) !== 0) { // Handle @bot messages @@ -109,7 +115,7 @@ startBot({ // return as we are done handling this command return; } - + log(LT.LOG, `Handling ${config.prefix}command message: ${JSON.stringify(message)}`); // Split into standard command + args format @@ -120,77 +126,55 @@ startBot({ // [[ping // Its a ping test, what else do you want. - if (command === "ping") { + if (command === 'ping') { commands.ping(message); - } - - // [[rip [[memory + } // [[rip [[memory // Displays a short message I wanted to include - else if (command === "rip" || command === "memory") { + else if (command === 'rip' || command === 'memory') { commands.rip(message); - } - - // [[rollhelp or [[rh or [[hr or [[?? + } // [[rollhelp or [[rh or [[hr or [[?? // Help command specifically for the roll command - else if (command === "rollhelp" || command === "rh" || command === "hr" || command === "??" || command?.startsWith("xdy")) { + else if (command === 'rollhelp' || command === 'rh' || command === 'hr' || command === '??' || command?.startsWith('xdy')) { commands.rollHelp(message); - } - - // [[help or [[h or [[? + } // [[help or [[h or [[? // Help command, prints from help file - else if (command === "help" || command === "h" || command === "?") { + else if (command === 'help' || command === 'h' || command === '?') { commands.help(message); - } - - // [[info or [[i + } // [[info or [[i // Info command, prints short desc on bot and some links - else if (command === "info" || command === "i") { + else if (command === 'info' || command === 'i') { commands.info(message); - } - - // [[privacy + } // [[privacy // Privacy command, prints short desc on bot's privacy policy - else if (command === "privacy") { + else if (command === 'privacy') { commands.privacy(message); - } - - // [[version or [[v + } // [[version or [[v // Returns version of the bot - else if (command === "version" || command === "v") { + else if (command === 'version' || command === 'v') { commands.version(message); - } - - // [[report or [[r (command that failed) + } // [[report or [[r (command that failed) // Manually report a failed roll - else if (command === "report" || command === "r") { + else if (command === 'report' || command === 'r') { commands.report(message, args); - } - - // [[stats or [[s + } // [[stats or [[s // Displays stats on the bot - else if (command === "stats" || command === "s") { + else if (command === 'stats' || command === 's') { commands.stats(message); - } - - // [[api arg + } // [[api arg // API sub commands - else if (command === "api") { + else if (command === 'api') { commands.api(message, args); - } - - // [[roll]] + } // [[roll]] // Dice rolling commence! - else if (command && (`${command}${args.join("")}`).indexOf(config.postfix) > -1) { + else if (command && (`${command}${args.join('')}`).indexOf(config.postfix) > -1) { commands.roll(message, args, command); - } - - // [[emoji or [[emojialias + } // [[emoji or [[emojialias // Check if the unhandled command is an emoji request else if (command) { commands.emoji(message, command); } - } - } + }, + }, }); // Start up the command prompt for debug usage diff --git a/src/api.ts b/src/api.ts index f81f0c1..a497e03 100644 --- a/src/api.ts +++ b/src/api.ts @@ -6,27 +6,26 @@ import { // Discordeno deps - cache, CreateMessage, - sendMessage, sendDirectMessage, - - // httpd deps - Status, STATUS_TEXT, - + cache, + CreateMessage, + log, + // Log4Deno deps + LT, // nanoid deps nanoid, + sendDirectMessage, + sendMessage, + // httpd deps + Status, + STATUS_TEXT, +} from '../deps.ts'; - // Log4Deno deps - LT, log -} from "../deps.ts"; +import { RollModifiers } from './mod.d.ts'; +import { dbClient, queries } from './db.ts'; +import solver from './solver/_index.ts'; +import { generateApiDeleteEmail, generateApiKeyEmail, generateDMFailed } from './constantCmds.ts'; -import { RollModifiers } from "./mod.d.ts"; -import { dbClient, queries } from "./db.ts"; -import solver from "./solver/_index.ts"; -import { - generateApiKeyEmail, generateApiDeleteEmail, generateDMFailed -} from "./constantCmds.ts"; - -import config from "../config.ts"; +import config from '../config.ts'; // start(databaseClient) returns nothing // start initializes and runs the entire API for the bot @@ -51,14 +50,14 @@ const start = async (): Promise => { let rateLimited = false; let updateRateLimitTime = false; let apiUserid = 0n; - let apiUseridStr = ""; - let apiUserEmail = ""; - let apiUserDelCode = ""; + let apiUseridStr = ''; + let apiUserEmail = ''; + let apiUserDelCode = ''; // Check the requests API key - if (request.headers.has("X-Api-Key")) { + if (request.headers.has('X-Api-Key')) { // Get the userid and flags for the specific key - const dbApiQuery = await dbClient.query("SELECT userid, email, deleteCode FROM all_keys WHERE apiKey = ? AND active = 1 AND banned = 0", [request.headers.get("X-Api-Key")]); + const dbApiQuery = await dbClient.query('SELECT userid, email, deleteCode FROM all_keys WHERE apiKey = ? AND active = 1 AND banned = 0', [request.headers.get('X-Api-Key')]); // If only one user returned, is not banned, and is currently active, mark as authenticated if (dbApiQuery.length === 1) { @@ -77,7 +76,7 @@ const start = async (): Promise => { const currentCnt = rateLimitCnt.get(apiUseridStr) || 0; if (currentCnt < config.api.rateLimitCnt) { // Limit not yet exceeded, update count - rateLimitCnt.set(apiUseridStr, (currentCnt + 1)); + rateLimitCnt.set(apiUseridStr, currentCnt + 1); } else { // Limit exceeded, prevent API use rateLimited = true; @@ -92,26 +91,26 @@ const start = async (): Promise => { if (authenticated && !rateLimited) { // Get path and query as a string - const [path, tempQ] = request.url.split("?"); + const [path, tempQ] = request.url.split('?'); // Turn the query into a map (if it exists) const query = new Map(); if (tempQ !== undefined) { - tempQ.split("&").forEach((e: string) => { + tempQ.split('&').forEach((e: string) => { log(LT.LOG, `Breaking down request query: ${request} ${e}`); - const [option, params] = e.split("="); + const [option, params] = e.split('='); query.set(option.toLowerCase(), params); }); } // Handle the request switch (request.method) { - case "GET": + case 'GET': switch (path.toLowerCase()) { - case "/api/key": - case "/api/key/": - if ((query.has("user") && ((query.get("user") || "").length > 0)) && (query.has("a") && ((query.get("a") || "").length > 0))) { - if (apiUserid === config.api.admin && apiUserid === BigInt(query.get("a") || "0")) { + case '/api/key': + case '/api/key/': + if ((query.has('user') && ((query.get('user') || '').length > 0)) && (query.has('a') && ((query.get('a') || '').length > 0))) { + if (apiUserid === config.api.admin && apiUserid === BigInt(query.get('a') || '0')) { // Generate new secure key const newKey = await nanoid(25); @@ -119,7 +118,7 @@ const start = async (): Promise => { let erroredOut = false; // Insert new key/user pair into the db - await dbClient.execute("INSERT INTO all_keys(userid,apiKey) values(?,?)", [apiUserid, newKey]).catch(e => { + await dbClient.execute('INSERT INTO all_keys(userid,apiKey) values(?,?)', [apiUserid, newKey]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -130,7 +129,7 @@ const start = async (): Promise => { break; } else { // Send API key as response - requestEvent.respondWith(new Response(JSON.stringify({ "key": newKey, "userid": query.get("user") }), { status: Status.OK })); + requestEvent.respondWith(new Response(JSON.stringify({ 'key': newKey, 'userid': query.get('user') }), { status: Status.OK })); break; } } else { @@ -142,15 +141,15 @@ const start = async (): Promise => { requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); } break; - case "/api/channel": - case "/api/channel/": - if (query.has("user") && ((query.get("user") || "").length > 0)) { - if (apiUserid === BigInt(query.get("user") || "0")) { + case '/api/channel': + case '/api/channel/': + if (query.has('user') && ((query.get('user') || '').length > 0)) { + if (apiUserid === BigInt(query.get('user') || '0')) { // Flag to see if there is an error inside the catch let erroredOut = false; // Get all channels userid has authorized - const dbAllowedChannelQuery = await dbClient.query("SELECT * FROM allowed_channels WHERE userid = ?", [apiUserid]).catch(e => { + const dbAllowedChannelQuery = await dbClient.query('SELECT * FROM allowed_channels WHERE userid = ?', [apiUserid]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -174,11 +173,14 @@ const start = async (): Promise => { requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); } break; - case "/api/roll": - case "/api/roll/": + case '/api/roll': + case '/api/roll/': // Make sure query contains all the needed parts - if ((query.has("rollstr") && ((query.get("rollstr") || "").length > 0)) && (query.has("channel") && ((query.get("channel") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { - if (query.has("n") && query.has("m")) { + if ( + (query.has('rollstr') && ((query.get('rollstr') || '').length > 0)) && (query.has('channel') && ((query.get('channel') || '').length > 0)) && + (query.has('user') && ((query.get('user') || '').length > 0)) + ) { + if (query.has('n') && query.has('m')) { // Alert API user that they shouldn't be doing this requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); break; @@ -189,13 +191,15 @@ const start = async (): Promise => { let hideWarn = false; // Check if the db has the requested userid/channelid combo, and that the requested userid matches the userid linked with the api key - const dbChannelQuery = await dbClient.query("SELECT active, banned FROM allowed_channels WHERE userid = ? AND channelid = ?", [apiUserid, BigInt(query.get("channel") || "0")]); - if (dbChannelQuery.length === 1 && (apiUserid === BigInt(query.get("user") || "0")) && dbChannelQuery[0].active && !dbChannelQuery[0].banned) { - + const dbChannelQuery = await dbClient.query('SELECT active, banned FROM allowed_channels WHERE userid = ? AND channelid = ?', [apiUserid, BigInt(query.get('channel') || '0')]); + if (dbChannelQuery.length === 1 && (apiUserid === BigInt(query.get('user') || '0')) && dbChannelQuery[0].active && !dbChannelQuery[0].banned) { // Get the guild from the channel and make sure user is in said guild - const guild = cache.channels.get(BigInt(query.get("channel") || ""))?.guild; - if (guild && guild.members.get(BigInt(query.get("user") || ""))?.id) { - const dbGuildQuery = await dbClient.query("SELECT active, banned, hidewarn FROM allowed_guilds WHERE guildid = ? AND channelid = ?", [guild.id, BigInt(query.get("channel") || "0")]); + const guild = cache.channels.get(BigInt(query.get('channel') || ''))?.guild; + if (guild && guild.members.get(BigInt(query.get('user') || ''))?.id) { + const dbGuildQuery = await dbClient.query('SELECT active, banned, hidewarn FROM allowed_guilds WHERE guildid = ? AND channelid = ?', [ + guild.id, + BigInt(query.get('channel') || '0'), + ]); // Make sure guild allows API rolls if (dbGuildQuery.length === 1 && dbGuildQuery[0].active && !dbGuildQuery[0].banned) { @@ -211,76 +215,78 @@ const start = async (): Promise => { // Flag to tell if roll was completely successful let errorOut = false; // Make sure rollCmd is not undefined - let rollCmd = query.get("rollstr") || ""; - const originalCommand = query.get("rollstr"); + let rollCmd = query.get('rollstr') || ''; + const originalCommand = query.get('rollstr'); if (rollCmd.length === 0) { // Alert API user that they messed up requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, "EmptyInput", null]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'EmptyInput', null]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); }); break; } - if (query.has("o") && (query.get("o")?.toLowerCase() !== "d" && query.get("o")?.toLowerCase() !== "a")) { + if (query.has('o') && (query.get('o')?.toLowerCase() !== 'd' && query.get('o')?.toLowerCase() !== 'a')) { // Alert API user that they messed up requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, "BadOrder", null]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'BadOrder', null]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); }); break; } // Clip off the leading prefix. API calls must be formatted with a prefix at the start to match how commands are sent in Discord - rollCmd = rollCmd.substring(rollCmd.indexOf(config.prefix) + 2).replace(/%20/g, " "); + rollCmd = rollCmd.substring(rollCmd.indexOf(config.prefix) + 2).replace(/%20/g, ' '); const modifiers: RollModifiers = { noDetails: false, superNoDetails: false, - spoiler: "", - maxRoll: query.has("m"), - nominalRoll: query.has("n"), + spoiler: '', + maxRoll: query.has('m'), + nominalRoll: query.has('n'), gmRoll: false, gms: [], - order: query.has("o") ? (query.get("o")?.toLowerCase() || "") : "", + order: query.has('o') ? (query.get('o')?.toLowerCase() || '') : '', valid: true, - count: query.has("c") + count: query.has('c'), }; // Parse the roll and get the return text const returnmsg = solver.parseRoll(rollCmd, modifiers); // Alert users why this message just appeared and how they can report abues pf this feature - const apiPrefix = hideWarn ? '' : `The following roll was conducted using my built in API. If someone in this channel did not request this roll, please report API abuse here: <${config.api.supportURL}>\n\n`; - let m, returnText = ""; + const apiPrefix = hideWarn + ? '' + : `The following roll was conducted using my built in API. If someone in this channel did not request this roll, please report API abuse here: <${config.api.supportURL}>\n\n`; + let m, returnText = ''; // Handle sending the error message to whoever called the api if (returnmsg.error) { requestEvent.respondWith(new Response(returnmsg.errorMsg, { status: Status.InternalServerError })); // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, returnmsg.errorCode, null]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, returnmsg.errorCode, null]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); }); break; } else { - returnText = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}`; - let spoilerTxt = ""; + returnText = `${apiPrefix}<@${query.get('user')}>${returnmsg.line1}\n${returnmsg.line2}`; + let spoilerTxt = ''; // Determine if spoiler flag was on - if (query.has("s")) { - spoilerTxt = "||"; + if (query.has('s')) { + spoilerTxt = '||'; } // Determine if no details flag was on - if (!query.has("snd")) { - if (query.has("nd")) { - returnText += "\nDetails suppressed by nd query."; + if (!query.has('snd')) { + if (query.has('nd')) { + returnText += '\nDetails suppressed by nd query.'; } else { returnText += `\nDetails:\n${spoilerTxt}${returnmsg.line3}${spoilerTxt}`; } @@ -288,70 +294,74 @@ const start = async (): Promise => { } // If the roll was a GM roll, send DMs to all the GMs - if (query.has("gms")) { + if (query.has('gms')) { // Get all the GM user IDs from the query - const gms = (query.get("gms") || "").split(","); + const gms = (query.get('gms') || '').split(','); if (gms.length === 0) { // Alert API user that they messed up requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, "NoGMsSent", null]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'NoGMsSent', null]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); }); break; } // Make a new return line to be sent to the roller - let normalText = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\nResults have been messaged to the following GMs: `; - gms.forEach(e => { + let normalText = `${apiPrefix}<@${query.get('user')}>${returnmsg.line1}\nResults have been messaged to the following GMs: `; + gms.forEach((e) => { log(LT.LOG, `Appending GM ${e} to roll text`); normalText += `<@${e}> `; }); // Send the return message as a DM or normal message depening on if the channel is set - if ((query.get("channel") || "").length > 0) { + if ((query.get('channel') || '').length > 0) { // todo: embedify - m = await sendMessage(BigInt(query.get("channel") || ""), normalText).catch(() => { - requestEvent.respondWith(new Response("Message 00 failed to send.", { status: Status.InternalServerError })); + m = await sendMessage(BigInt(query.get('channel') || ''), normalText).catch(() => { + requestEvent.respondWith(new Response('Message 00 failed to send.', { status: Status.InternalServerError })); errorOut = true; }); } else { // todo: embedify - m = await sendDirectMessage(BigInt(query.get("user") || ""), normalText).catch(() => { - requestEvent.respondWith(new Response("Message 01 failed to send.", { status: Status.InternalServerError })); + m = await sendDirectMessage(BigInt(query.get('user') || ''), normalText).catch(() => { + requestEvent.respondWith(new Response('Message 01 failed to send.', { status: Status.InternalServerError })); errorOut = true; }); } const newMessage: CreateMessage = {}; // If its too big, collapse it into a .txt file and send that instead. - const b = await new Blob([returnText as BlobPart], { "type": "text" }); + const b = await new Blob([returnText as BlobPart], { 'type': 'text' }); if (b.size > 8388290) { // Update return text - newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; + newMessage.content = `${apiPrefix}<@${ + query.get('user') + }>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; } else { // Update return text - newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`; - newMessage.file = { "blob": b, "name": "rollDetails.txt" }; + newMessage.content = `${apiPrefix}<@${ + query.get('user') + }>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`; + newMessage.file = { 'blob': b, 'name': 'rollDetails.txt' }; } // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged - gms.forEach(async e => { + gms.forEach(async (e) => { log(LT.LOG, `Messaging GM ${e} roll results`); // Attempt to DM the GMs and send a warning if it could not DM a GM await sendDirectMessage(BigInt(e), newMessage).catch(async () => { const failedSend = generateDMFailed(e); // Send the return message as a DM or normal message depening on if the channel is set - if ((query.get("channel") || "").length > 0) { - m = await sendMessage(BigInt(query.get("channel") || ""), failedSend).catch(() => { - requestEvent.respondWith(new Response("Message failed to send.", { status: Status.InternalServerError })); + if ((query.get('channel') || '').length > 0) { + m = await sendMessage(BigInt(query.get('channel') || ''), failedSend).catch(() => { + requestEvent.respondWith(new Response('Message failed to send.', { status: Status.InternalServerError })); errorOut = true; }); } else { - m = await sendDirectMessage(BigInt(query.get("user") || ""), failedSend).catch(() => { - requestEvent.respondWith(new Response("Message failed to send.", { status: Status.InternalServerError })); + m = await sendDirectMessage(BigInt(query.get('user') || ''), failedSend).catch(() => { + requestEvent.respondWith(new Response('Message failed to send.', { status: Status.InternalServerError })); errorOut = true; }); } @@ -359,7 +369,7 @@ const start = async (): Promise => { }); // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, ((typeof m === "object") ? m.id : null)]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, (typeof m === 'object') ? m.id : null]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); }); @@ -378,33 +388,37 @@ const start = async (): Promise => { // When not a GM roll, make sure the message is not too big if (returnText.length > 2000) { // If its too big, collapse it into a .txt file and send that instead. - const b = await new Blob([returnText as BlobPart], { "type": "text" }); + const b = await new Blob([returnText as BlobPart], { 'type': 'text' }); if (b.size > 8388290) { // Update return text - newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; + newMessage.content = `${apiPrefix}<@${ + query.get('user') + }>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; } else { // Update return text - newMessage.content = `${apiPrefix}<@${query.get("user")}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`; - newMessage.file = { "blob": b, "name": "rollDetails.txt" }; + newMessage.content = `${apiPrefix}<@${ + query.get('user') + }>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`; + newMessage.file = { 'blob': b, 'name': 'rollDetails.txt' }; } } // Send the return message as a DM or normal message depening on if the channel is set - if ((query.get("channel") || "").length > 0) { - m = await sendMessage(BigInt(query.get("channel") || ""), newMessage).catch(() => { - requestEvent.respondWith(new Response("Message 20 failed to send.", { status: Status.InternalServerError })); + if ((query.get('channel') || '').length > 0) { + m = await sendMessage(BigInt(query.get('channel') || ''), newMessage).catch(() => { + requestEvent.respondWith(new Response('Message 20 failed to send.', { status: Status.InternalServerError })); errorOut = true; }); } else { - m = await sendDirectMessage(BigInt(query.get("user") || ""), newMessage).catch(() => { - requestEvent.respondWith(new Response("Message 21 failed to send.", { status: Status.InternalServerError })); + m = await sendDirectMessage(BigInt(query.get('user') || ''), newMessage).catch(() => { + requestEvent.respondWith(new Response('Message 21 failed to send.', { status: Status.InternalServerError })); errorOut = true; }); } // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, ((typeof m === "object") ? m.id : null)]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(1, 0), [originalCommand, returnText, (typeof m === 'object') ? m.id : null]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); }); @@ -436,17 +450,17 @@ const start = async (): Promise => { break; } break; - case "POST": + case 'POST': switch (path.toLowerCase()) { - case "/api/channel/add": - case "/api/channel/add/": - if ((query.has("user") && ((query.get("user") || "").length > 0)) && (query.has("channel") && ((query.get("channel") || "").length > 0))) { - if (apiUserid === BigInt(query.get("user") || "0")) { + case '/api/channel/add': + case '/api/channel/add/': + if ((query.has('user') && ((query.get('user') || '').length > 0)) && (query.has('channel') && ((query.get('channel') || '').length > 0))) { + if (apiUserid === BigInt(query.get('user') || '0')) { // Flag to see if there is an error inside the catch let erroredOut = false; // Insert new user/channel pair into the db - await dbClient.execute("INSERT INTO allowed_channels(userid,channelid) values(?,?)", [apiUserid, BigInt(query.get("channel") || "0")]).catch(e => { + await dbClient.execute('INSERT INTO allowed_channels(userid,channelid) values(?,?)', [apiUserid, BigInt(query.get('channel') || '0')]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -475,37 +489,37 @@ const start = async (): Promise => { break; } break; - case "PUT": + case 'PUT': switch (path.toLowerCase()) { - case "/api/key/ban": - case "/api/key/ban/": - case "/api/key/unban": - case "/api/key/unban/": - case "/api/key/activate": - case "/api/key/activate/": - case "/api/key/deactivate": - case "/api/key/deactivate/": - if ((query.has("a") && ((query.get("a") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { - if (apiUserid === config.api.admin && apiUserid === BigInt(query.get("a") || "0")) { + case '/api/key/ban': + case '/api/key/ban/': + case '/api/key/unban': + case '/api/key/unban/': + case '/api/key/activate': + case '/api/key/activate/': + case '/api/key/deactivate': + case '/api/key/deactivate/': + if ((query.has('a') && ((query.get('a') || '').length > 0)) && (query.has('user') && ((query.get('user') || '').length > 0))) { + if (apiUserid === config.api.admin && apiUserid === BigInt(query.get('a') || '0')) { // Flag to see if there is an error inside the catch let key, value, erroredOut = false; // Determine key to edit - if (path.toLowerCase().indexOf("ban") > 0) { - key = "banned"; + if (path.toLowerCase().indexOf('ban') > 0) { + key = 'banned'; } else { - key = "active"; + key = 'active'; } // Determine value to set - if (path.toLowerCase().indexOf("de") > 0 || path.toLowerCase().indexOf("un") > 0) { + if (path.toLowerCase().indexOf('de') > 0 || path.toLowerCase().indexOf('un') > 0) { value = 0; } else { value = 1; } // Execute the DB modification - await dbClient.execute("UPDATE all_keys SET ?? = ? WHERE userid = ?", [key, value, apiUserid]).catch(e => { + await dbClient.execute('UPDATE all_keys SET ?? = ? WHERE userid = ?', [key, value, apiUserid]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -528,24 +542,27 @@ const start = async (): Promise => { requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); } break; - case "/api/channel/ban": - case "/api/channel/ban/": - case "/api/channel/unban": - case "/api/channel/unban/": - if ((query.has("a") && ((query.get("a") || "").length > 0)) && (query.has("channel") && ((query.get("channel") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { - if (apiUserid === config.api.admin && apiUserid === BigInt(query.get("a") || "0")) { + case '/api/channel/ban': + case '/api/channel/ban/': + case '/api/channel/unban': + case '/api/channel/unban/': + if ( + (query.has('a') && ((query.get('a') || '').length > 0)) && (query.has('channel') && ((query.get('channel') || '').length > 0)) && + (query.has('user') && ((query.get('user') || '').length > 0)) + ) { + if (apiUserid === config.api.admin && apiUserid === BigInt(query.get('a') || '0')) { // Flag to see if there is an error inside the catch let value, erroredOut = false; // Determine value to set - if (path.toLowerCase().indexOf("un") > 0) { + if (path.toLowerCase().indexOf('un') > 0) { value = 0; } else { value = 1; } // Execute the DB modification - await dbClient.execute("UPDATE allowed_channels SET banned = ? WHERE userid = ? AND channelid = ?", [value, apiUserid, BigInt(query.get("channel") || "0")]).catch(e => { + await dbClient.execute('UPDATE allowed_channels SET banned = ? WHERE userid = ? AND channelid = ?', [value, apiUserid, BigInt(query.get('channel') || '0')]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -568,24 +585,24 @@ const start = async (): Promise => { requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); } break; - case "/api/channel/activate": - case "/api/channel/activate/": - case "/api/channel/deactivate": - case "/api/channel/deactivate/": - if ((query.has("channel") && ((query.get("channel") || "").length > 0)) && (query.has("user") && ((query.get("user") || "").length > 0))) { - if (apiUserid === BigInt(query.get("user") || "0")) { + case '/api/channel/activate': + case '/api/channel/activate/': + case '/api/channel/deactivate': + case '/api/channel/deactivate/': + if ((query.has('channel') && ((query.get('channel') || '').length > 0)) && (query.has('user') && ((query.get('user') || '').length > 0))) { + if (apiUserid === BigInt(query.get('user') || '0')) { // Flag to see if there is an error inside the catch let value, erroredOut = false; // Determine value to set - if (path.toLowerCase().indexOf("de") > 0) { + if (path.toLowerCase().indexOf('de') > 0) { value = 0; } else { value = 1; } // Update the requested entry - await dbClient.execute("UPDATE allowed_channels SET active = ? WHERE userid = ? AND channelid = ?", [value, apiUserid, BigInt(query.get("channel") || "0")]).catch(e => { + await dbClient.execute('UPDATE allowed_channels SET active = ? WHERE userid = ? AND channelid = ?', [value, apiUserid, BigInt(query.get('channel') || '0')]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -601,7 +618,7 @@ const start = async (): Promise => { } } else { // Alert API user that they shouldn't be doing this - requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.Forbidden), { status: Status.Forbidden })); + requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.Forbidden), { status: Status.Forbidden })); } } else { // Alert API user that they messed up @@ -614,18 +631,18 @@ const start = async (): Promise => { break; } break; - case "DELETE": + case 'DELETE': switch (path.toLowerCase()) { - case "/api/key/delete": - case "/api/key/delete/": - if (query.has("user") && ((query.get("user") || "").length > 0) && query.has("email") && ((query.get("email") || "").length > 0)) { - if (apiUserid === BigInt(query.get("user") || "0") && apiUserEmail === query.get("email")) { - if (query.has("code") && ((query.get("code") || "").length > 0)) { - if ((query.get("code") || "") === apiUserDelCode) { + case '/api/key/delete': + case '/api/key/delete/': + if (query.has('user') && ((query.get('user') || '').length > 0) && query.has('email') && ((query.get('email') || '').length > 0)) { + if (apiUserid === BigInt(query.get('user') || '0') && apiUserEmail === query.get('email')) { + if (query.has('code') && ((query.get('code') || '').length > 0)) { + if ((query.get('code') || '') === apiUserDelCode) { // User has recieved their delete code and we need to delete the account now let erroredOut = false; - await dbClient.execute("DELETE FROM allowed_channels WHERE userid = ?", [apiUserid]).catch(e => { + await dbClient.execute('DELETE FROM allowed_channels WHERE userid = ?', [apiUserid]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -634,7 +651,7 @@ const start = async (): Promise => { break; } - await dbClient.execute("DELETE FROM all_keys WHERE userid = ?", [apiUserid]).catch(e => { + await dbClient.execute('DELETE FROM all_keys WHERE userid = ?', [apiUserid]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -657,7 +674,7 @@ const start = async (): Promise => { let erroredOut = false; // Execute the DB modification - await dbClient.execute("UPDATE all_keys SET deleteCode = ? WHERE userid = ?", [deleteCode, apiUserid]).catch(e => { + await dbClient.execute('UPDATE all_keys SET deleteCode = ? WHERE userid = ?', [deleteCode, apiUserid]).catch((e) => { log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); erroredOut = true; @@ -668,7 +685,7 @@ const start = async (): Promise => { // "Send" the email await sendMessage(config.api.email, generateApiDeleteEmail(apiUserEmail, deleteCode)).catch(() => { - requestEvent.respondWith(new Response("Message 30 failed to send.", { status: Status.InternalServerError })); + requestEvent.respondWith(new Response('Message 30 failed to send.', { status: Status.InternalServerError })); erroredOut = true; }); if (erroredOut) { @@ -685,7 +702,7 @@ const start = async (): Promise => { } } else { // Alert API user that they messed up - requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); + requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.BadRequest), { status: Status.BadRequest })); } break; default: @@ -706,25 +723,25 @@ const start = async (): Promise => { } } else if (!authenticated && !rateLimited) { // Get path and query as a string - const [path, tempQ] = request.url.split("?"); + const [path, tempQ] = request.url.split('?'); // Turn the query into a map (if it exists) const query = new Map(); if (tempQ !== undefined) { - tempQ.split("&").forEach((e: string) => { + tempQ.split('&').forEach((e: string) => { log(LT.LOG, `Parsing request query #2 ${request} ${e}`); - const [option, params] = e.split("="); + const [option, params] = e.split('='); query.set(option.toLowerCase(), params); }); } // Handle the request switch (request.method) { - case "GET": + case 'GET': switch (path.toLowerCase()) { - case "/api/key": - case "/api/key/": - if ((query.has("user") && ((query.get("user") || "").length > 0)) && (query.has("email") && ((query.get("email") || "").length > 0))) { + case '/api/key': + case '/api/key/': + if ((query.has('user') && ((query.get('user') || '').length > 0)) && (query.has('email') && ((query.get('email') || '').length > 0))) { // Generate new secure key const newKey = await nanoid(25); @@ -732,20 +749,22 @@ const start = async (): Promise => { let erroredOut = false; // Insert new key/user pair into the db - await dbClient.execute("INSERT INTO all_keys(userid,apiKey,email) values(?,?,?)", [BigInt(query.get("user") || "0"), newKey, (query.get("email") || "").toLowerCase()]).catch(e => { - log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); - requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); - erroredOut = true; - }); + await dbClient.execute('INSERT INTO all_keys(userid,apiKey,email) values(?,?,?)', [BigInt(query.get('user') || '0'), newKey, (query.get('email') || '').toLowerCase()]).catch( + (e) => { + log(LT.ERROR, `Failed to insert into database: ${JSON.stringify(e)}`); + requestEvent.respondWith(new Response(STATUS_TEXT.get(Status.InternalServerError), { status: Status.InternalServerError })); + erroredOut = true; + }, + ); // Exit this case now if catch errored if (erroredOut) { break; } - + // "Send" the email - await sendMessage(config.api.email, generateApiKeyEmail(query.get("email") || "no email", newKey)).catch(() => { - requestEvent.respondWith(new Response("Message 31 failed to send.", { status: Status.InternalServerError })); + await sendMessage(config.api.email, generateApiKeyEmail(query.get('email') || 'no email', newKey)).catch(() => { + requestEvent.respondWith(new Response('Message 31 failed to send.', { status: Status.InternalServerError })); erroredOut = true; }); diff --git a/src/commands/_index.ts b/src/commands/_index.ts index cffe162..eb10cd9 100644 --- a/src/commands/_index.ts +++ b/src/commands/_index.ts @@ -1,16 +1,16 @@ -import { ping } from "./ping.ts"; -import { rip } from "./rip.ts"; -import { rollHelp } from "./rollHelp.ts"; -import { help } from "./help.ts"; -import { info } from "./info.ts"; -import { privacy } from "./privacy.ts"; -import { version } from "./version.ts"; -import { report } from "./report.ts"; -import { stats } from "./stats.ts"; -import { api } from "./apiCmd.ts"; -import { emoji } from "./emoji.ts"; -import { roll } from "./roll.ts"; -import { handleMentions } from "./handleMentions.ts"; +import { ping } from './ping.ts'; +import { rip } from './rip.ts'; +import { rollHelp } from './rollHelp.ts'; +import { help } from './help.ts'; +import { info } from './info.ts'; +import { privacy } from './privacy.ts'; +import { version } from './version.ts'; +import { report } from './report.ts'; +import { stats } from './stats.ts'; +import { api } from './apiCmd.ts'; +import { emoji } from './emoji.ts'; +import { roll } from './roll.ts'; +import { handleMentions } from './handleMentions.ts'; export default { ping, @@ -25,5 +25,5 @@ export default { api, emoji, roll, - handleMentions + handleMentions, }; diff --git a/src/commands/apiCmd.ts b/src/commands/apiCmd.ts index 3754aa5..6bcd972 100644 --- a/src/commands/apiCmd.ts +++ b/src/commands/apiCmd.ts @@ -1,65 +1,57 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps - DiscordenoMessage, hasGuildPermissions, - + DiscordenoMessage, + hasGuildPermissions, + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import apiCommands from "./apiCmd/_index.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import apiCommands from './apiCmd/_index.ts'; +import { constantCmds } from '../constantCmds.ts'; export const api = async (message: DiscordenoMessage, args: string[]) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("api");`).catch(e => { + dbClient.execute(`CALL INC_CNT("api");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); // Local apiArg in lowercase - const apiArg = (args[0] || "help").toLowerCase(); + const apiArg = (args[0] || 'help').toLowerCase(); // Alert users who DM the bot that this command is for guilds only if (message.guildId === 0n) { - message.send(constantCmds.apiGuildOnly).catch(e => { + message.send(constantCmds.apiGuildOnly).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); return; } // Makes sure the user is authenticated to run the API command - if (await hasGuildPermissions(message.authorId, message.guildId, ["ADMINISTRATOR"])) { + if (await hasGuildPermissions(message.authorId, message.guildId, ['ADMINISTRATOR'])) { // [[api help // Shows API help details - if (apiArg === "help") { + if (apiArg === 'help') { apiCommands.help(message); - } - - // [[api allow/block + } // [[api allow/block // Lets a guild admin allow or ban API rolls from happening in said guild - else if (apiArg === "allow" || apiArg === "block" || apiArg === "enable" || apiArg === "disable") { + else if (apiArg === 'allow' || apiArg === 'block' || apiArg === 'enable' || apiArg === 'disable') { apiCommands.allowBlock(message, apiArg); - } - - // [[api delete + } // [[api delete // Lets a guild admin delete their server from the database - else if (apiArg === "delete") { + else if (apiArg === 'delete') { apiCommands.deleteGuild(message); - } - - // [[api status + } // [[api status // Lets a guild admin check the status of API rolling in said guild - else if (apiArg === "status") { + else if (apiArg === 'status') { apiCommands.status(message); - } - - // [[api show-warn/hide-warn + } // [[api show-warn/hide-warn // Lets a guild admin decide if the API warning should be shown on messages from the API - else if (apiArg === "show-warn" || apiArg === "hide-warn") { + else if (apiArg === 'show-warn' || apiArg === 'hide-warn') { apiCommands.showHideWarn(message, apiArg); } - } else { - message.send(constantCmds.apiPermError).catch(e => { + message.send(constantCmds.apiPermError).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } diff --git a/src/commands/apiCmd/_index.ts b/src/commands/apiCmd/_index.ts index 81fc146..fde6a3d 100644 --- a/src/commands/apiCmd/_index.ts +++ b/src/commands/apiCmd/_index.ts @@ -1,13 +1,13 @@ -import { help } from "./apiHelp.ts"; -import { allowBlock } from "./allowBlock.ts"; -import { deleteGuild } from "./deleteGuild.ts"; -import { status } from "./status.ts"; -import { showHideWarn } from "./showHideWarn.ts"; +import { help } from './apiHelp.ts'; +import { allowBlock } from './allowBlock.ts'; +import { deleteGuild } from './deleteGuild.ts'; +import { status } from './status.ts'; +import { showHideWarn } from './showHideWarn.ts'; export default { help, allowBlock, deleteGuild, status, - showHideWarn + showHideWarn, }; diff --git a/src/commands/apiCmd/allowBlock.ts b/src/commands/apiCmd/allowBlock.ts index bfc5114..be51dba 100644 --- a/src/commands/apiCmd/allowBlock.ts +++ b/src/commands/apiCmd/allowBlock.ts @@ -1,17 +1,17 @@ -import { dbClient } from "../../db.ts"; +import { dbClient } from '../../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../../deps.ts"; -import { generateApiFailed, generateApiSuccess } from "../../constantCmds.ts"; + LT, +} from '../../../deps.ts'; +import { generateApiFailed, generateApiSuccess } from '../../constantCmds.ts'; export const allowBlock = async (message: DiscordenoMessage, apiArg: string) => { - const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { + const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); - message.send(generateApiFailed(apiArg)).catch(e1 => { + message.send(generateApiFailed(apiArg)).catch((e1) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); }); return; @@ -19,26 +19,30 @@ export const allowBlock = async (message: DiscordenoMessage, apiArg: string) => if (guildQuery.length === 0) { // Since guild is not in our DB, add it in - await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,active) values(?,?,?)`, [message.guildId, message.channelId, ((apiArg === "allow" || apiArg === "enable") ? 1 : 0)]).catch(e0 => { - log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`); - message.send(generateApiFailed(apiArg)).catch(e1 => { - log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); - }); - return; - }); + await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,active) values(?,?,?)`, [message.guildId, message.channelId, (apiArg === 'allow' || apiArg === 'enable') ? 1 : 0]).catch( + (e0) => { + log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`); + message.send(generateApiFailed(apiArg)).catch((e1) => { + log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); + }); + return; + }, + ); } else { // Since guild is in our DB, update it - await dbClient.execute(`UPDATE allowed_guilds SET active = ? WHERE guildid = ? AND channelid = ?`, [((apiArg === "allow" || apiArg === "enable") ? 1 : 0), message.guildId, message.channelId]).catch(e0 => { - log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`); - message.send(generateApiFailed(apiArg)).catch(e1 => { - log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); - }); - return; - }); + await dbClient.execute(`UPDATE allowed_guilds SET active = ? WHERE guildid = ? AND channelid = ?`, [(apiArg === 'allow' || apiArg === 'enable') ? 1 : 0, message.guildId, message.channelId]).catch( + (e0) => { + log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`); + message.send(generateApiFailed(apiArg)).catch((e1) => { + log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); + }); + return; + }, + ); } // We won't get here if there's any errors, so we know it has bee successful, so report as such - message.send(generateApiSuccess(`${apiArg}ed`)).catch(e => { + message.send(generateApiSuccess(`${apiArg}ed`)).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/apiCmd/apiHelp.ts b/src/commands/apiCmd/apiHelp.ts index 3bd6009..3cc2e7e 100644 --- a/src/commands/apiCmd/apiHelp.ts +++ b/src/commands/apiCmd/apiHelp.ts @@ -1,14 +1,14 @@ import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../../deps.ts"; -import { constantCmds } from "../../constantCmds.ts"; + LT, +} from '../../../deps.ts'; +import { constantCmds } from '../../constantCmds.ts'; export const help = (message: DiscordenoMessage) => { - message.send(constantCmds.apiHelp).catch(e => { + message.send(constantCmds.apiHelp).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/apiCmd/deleteGuild.ts b/src/commands/apiCmd/deleteGuild.ts index e6000ed..f6c99a1 100644 --- a/src/commands/apiCmd/deleteGuild.ts +++ b/src/commands/apiCmd/deleteGuild.ts @@ -1,24 +1,24 @@ -import { dbClient } from "../../db.ts"; +import { dbClient } from '../../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../../deps.ts"; -import { constantCmds } from "../../constantCmds.ts"; + LT, +} from '../../../deps.ts'; +import { constantCmds } from '../../constantCmds.ts'; export const deleteGuild = async (message: DiscordenoMessage) => { - await dbClient.execute(`DELETE FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { + await dbClient.execute(`DELETE FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); - message.send(constantCmds.apiDeleteFail).catch(e1 => { + message.send(constantCmds.apiDeleteFail).catch((e1) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); }); return; }); // We won't get here if there's any errors, so we know it has bee successful, so report as such - message.send(constantCmds.apiRemoveGuild).catch(e => { + message.send(constantCmds.apiRemoveGuild).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/apiCmd/showHideWarn.ts b/src/commands/apiCmd/showHideWarn.ts index 9f42e17..47e3abf 100644 --- a/src/commands/apiCmd/showHideWarn.ts +++ b/src/commands/apiCmd/showHideWarn.ts @@ -1,17 +1,17 @@ -import { dbClient } from "../../db.ts"; +import { dbClient } from '../../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../../deps.ts"; -import { generateApiFailed, generateApiSuccess } from "../../constantCmds.ts"; + LT, +} from '../../../deps.ts'; +import { generateApiFailed, generateApiSuccess } from '../../constantCmds.ts'; export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) => { - const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { + const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); - message.send(generateApiFailed(`${apiArg} on`)).catch(e1 => { + message.send(generateApiFailed(`${apiArg} on`)).catch((e1) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); }); return; @@ -19,18 +19,18 @@ export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) = if (guildQuery.length === 0) { // Since guild is not in our DB, add it in - await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,hidewarn) values(?,?,?)`, [message.guildId, message.channelId, ((apiArg === "hide-warn") ? 1 : 0)]).catch(e0 => { + await dbClient.execute(`INSERT INTO allowed_guilds(guildid,channelid,hidewarn) values(?,?,?)`, [message.guildId, message.channelId, (apiArg === 'hide-warn') ? 1 : 0]).catch((e0) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e0)}`); - message.send(generateApiFailed(`${apiArg} on`)).catch(e1 => { + message.send(generateApiFailed(`${apiArg} on`)).catch((e1) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); }); return; }); } else { // Since guild is in our DB, update it - await dbClient.execute(`UPDATE allowed_guilds SET hidewarn = ? WHERE guildid = ? AND channelid = ?`, [((apiArg === "hide-warn") ? 1 : 0), message.guildId, message.channelId]).catch(e0 => { + await dbClient.execute(`UPDATE allowed_guilds SET hidewarn = ? WHERE guildid = ? AND channelid = ?`, [(apiArg === 'hide-warn') ? 1 : 0, message.guildId, message.channelId]).catch((e0) => { log(LT.ERROR, `Failed to update DB: ${JSON.stringify(e0)}`); - message.send(generateApiFailed(`${apiArg} on`)).catch(e1 => { + message.send(generateApiFailed(`${apiArg} on`)).catch((e1) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); }); return; @@ -38,7 +38,7 @@ export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) = } // We won't get here if there's any errors, so we know it has bee successful, so report as such - message.send(generateApiSuccess(apiArg)).catch(e => { + message.send(generateApiSuccess(apiArg)).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/apiCmd/status.ts b/src/commands/apiCmd/status.ts index 2018e90..f42836f 100644 --- a/src/commands/apiCmd/status.ts +++ b/src/commands/apiCmd/status.ts @@ -1,18 +1,18 @@ -import { dbClient } from "../../db.ts"; +import { dbClient } from '../../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../../deps.ts"; -import { constantCmds, generateApiStatus } from "../../constantCmds.ts"; + LT, +} from '../../../deps.ts'; +import { constantCmds, generateApiStatus } from '../../constantCmds.ts'; export const status = async (message: DiscordenoMessage) => { // Get status of guild from the db - const guildQuery = await dbClient.query(`SELECT active, banned FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch(e0 => { + const guildQuery = await dbClient.query(`SELECT active, banned FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e0)}`); - message.send(constantCmds.apiStatusFail).catch(e1 => { + message.send(constantCmds.apiStatusFail).catch((e1) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e1)}`); }); return; @@ -22,17 +22,17 @@ export const status = async (message: DiscordenoMessage) => { if (guildQuery.length > 0) { // Check if guild is banned from using API and return appropriate message if (guildQuery[0].banned) { - message.send(generateApiStatus(true, false)).catch(e => { + message.send(generateApiStatus(true, false)).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } else { - message.send(generateApiStatus(false, guildQuery[0].active)).catch(e => { + message.send(generateApiStatus(false, guildQuery[0].active)).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } } else { // Guild is not in DB, therefore they are blocked - message.send(generateApiStatus(false, false)).catch(e => { + message.send(generateApiStatus(false, false)).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } diff --git a/src/commands/emoji.ts b/src/commands/emoji.ts index 049dde1..395958d 100644 --- a/src/commands/emoji.ts +++ b/src/commands/emoji.ts @@ -1,40 +1,40 @@ -import config from "../../config.ts"; -import { dbClient } from "../db.ts"; +import config from '../../config.ts'; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { EmojiConf } from "../mod.d.ts"; + LT, +} from '../../deps.ts'; +import { EmojiConf } from '../mod.d.ts'; const allEmojiAliases: string[] = []; config.emojis.forEach((emoji: EmojiConf) => { - allEmojiAliases.push(...emoji.aliases) + allEmojiAliases.push(...emoji.aliases); }); export const emoji = (message: DiscordenoMessage, command: string) => { - // shortcut + // shortcut if (allEmojiAliases.indexOf(command)) { // Start looping thru the possible emojis config.emojis.some((emoji: EmojiConf) => { log(LT.LOG, `Checking if command was emoji ${JSON.stringify(emoji)}`); // If a match gets found - if (emoji.aliases.indexOf(command || "") > -1) { + if (emoji.aliases.indexOf(command || '') > -1) { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("emojis");`).catch(e => { + dbClient.execute(`CALL INC_CNT("emojis");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); // Send the needed emoji1 - message.send(`<${emoji.animated ? "a" : ""}:${emoji.name}:${emoji.id}>`).catch(e => { + message.send(`<${emoji.animated ? 'a' : ''}:${emoji.name}:${emoji.id}>`).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); // And attempt to delete if needed if (emoji.deleteSender) { - message.delete().catch(e => { + message.delete().catch((e) => { log(LT.WARN, `Failed to delete message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } diff --git a/src/commands/handleMentions.ts b/src/commands/handleMentions.ts index f2f7e2f..dab70e7 100644 --- a/src/commands/handleMentions.ts +++ b/src/commands/handleMentions.ts @@ -1,22 +1,22 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const handleMentions = (message: DiscordenoMessage) => { log(LT.LOG, `Handling @mention message: ${JSON.stringify(message)}`); // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("mention");`).catch(e => { + dbClient.execute(`CALL INC_CNT("mention");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.mention).catch(e => { + message.send(constantCmds.mention).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/help.ts b/src/commands/help.ts index 858ee64..42ee338 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,20 +1,20 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const help = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("help");`).catch(e => { + dbClient.execute(`CALL INC_CNT("help");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.help).catch(e => { + message.send(constantCmds.help).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/info.ts b/src/commands/info.ts index 5cb34ed..d03f119 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -1,20 +1,20 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const info = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("info");`).catch(e => { + dbClient.execute(`CALL INC_CNT("info");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.info).catch(e => { + message.send(constantCmds.info).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 01de28b..9292cf6 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,16 +1,16 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { generatePing } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { generatePing } from '../constantCmds.ts'; export const ping = async (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("ping");`).catch(e => { + dbClient.execute(`CALL INC_CNT("ping");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); diff --git a/src/commands/privacy.ts b/src/commands/privacy.ts index 7548249..3f640db 100644 --- a/src/commands/privacy.ts +++ b/src/commands/privacy.ts @@ -1,20 +1,20 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const privacy = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("privacy");`).catch(e => { + dbClient.execute(`CALL INC_CNT("privacy");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.privacy).catch(e => { + message.send(constantCmds.privacy).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/report.ts b/src/commands/report.ts index 4d6778b..c924a00 100644 --- a/src/commands/report.ts +++ b/src/commands/report.ts @@ -1,29 +1,30 @@ -import config from "../../config.ts"; -import { dbClient } from "../db.ts"; +import config from '../../config.ts'; +import { dbClient } from '../db.ts'; import { // Discordeno deps - DiscordenoMessage, sendMessage, - + DiscordenoMessage, + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds, generateReport } from "../constantCmds.ts"; + LT, + sendMessage, +} from '../../deps.ts'; +import { constantCmds, generateReport } from '../constantCmds.ts'; export const report = (message: DiscordenoMessage, args: string[]) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("report");`).catch(e => { + dbClient.execute(`CALL INC_CNT("report");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - if (args.join(" ")) { - sendMessage(config.reportChannel, generateReport(args.join(" "))).catch(e => { + if (args.join(' ')) { + sendMessage(config.reportChannel, generateReport(args.join(' '))).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); - message.send(constantCmds.report).catch(e => { + message.send(constantCmds.report).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } else { - message.send(constantCmds.reportFail).catch(e => { + message.send(constantCmds.reportFail).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } diff --git a/src/commands/rip.ts b/src/commands/rip.ts index 6502198..189c0a8 100644 --- a/src/commands/rip.ts +++ b/src/commands/rip.ts @@ -1,20 +1,20 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const rip = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("rip");`).catch(e => { + dbClient.execute(`CALL INC_CNT("rip");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.rip).catch(e => { + message.send(constantCmds.rip).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/roll.ts b/src/commands/roll.ts index 28ef81e..a999133 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,26 +1,27 @@ -import config from "../../config.ts"; -import { DEVMODE } from "../../flags.ts"; -import { dbClient, queries } from "../db.ts"; +import config from '../../config.ts'; +import { DEVMODE } from '../../flags.ts'; +import { dbClient, queries } from '../db.ts'; import { // Discordeno deps - DiscordenoMessage, sendDirectMessage, - + DiscordenoMessage, + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import solver from "../solver/_index.ts"; -import { constantCmds, generateDMFailed } from "../constantCmds.ts"; -import rollFuncs from "./roll/_index.ts"; + LT, + sendDirectMessage, +} from '../../deps.ts'; +import solver from '../solver/_index.ts'; +import { constantCmds, generateDMFailed } from '../constantCmds.ts'; +import rollFuncs from './roll/_index.ts'; export const roll = async (message: DiscordenoMessage, args: string[], command: string) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("roll");`).catch(e => { + dbClient.execute(`CALL INC_CNT("roll");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); // If DEVMODE is on, only allow this command to be used in the devServer if (DEVMODE && message.guildId !== config.devServer) { - message.send(constantCmds.indev).catch(e => { + message.send(constantCmds.indev).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); return; @@ -28,7 +29,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: // Rest of this command is in a try-catch to protect all sends/edits from erroring out try { - const originalCommand = `${config.prefix}${command} ${args.join(" ")}`; + const originalCommand = `${config.prefix}${command} ${args.join(' ')}`; const m = await message.send(constantCmds.rolling); @@ -41,10 +42,10 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: } // Rejoin all of the args and send it into the solver, if solver returns a falsy item, an error object will be substituded in - const rollCmd = `${command} ${args.join(" ")}`; - const returnmsg = solver.parseRoll(rollCmd, modifiers) || { error: true, errorCode: "EmptyMessage", errorMsg: "Error: Empty message", line1: "", line2: "", line3: "" }; + const rollCmd = `${command} ${args.join(' ')}`; + const returnmsg = solver.parseRoll(rollCmd, modifiers) || { error: true, errorCode: 'EmptyMessage', errorMsg: 'Error: Empty message', line1: '', line2: '', line3: '' }; - let returnText = ""; + let returnText = ''; // If there was an error, report it to the user in hopes that they can determine what they did wrong if (returnmsg.error) { @@ -53,7 +54,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: if (DEVMODE && config.logRolls) { // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, returnmsg.errorCode, m.id]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, returnmsg.errorCode, m.id]).catch((e) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); }); } @@ -64,7 +65,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: if (!modifiers.superNoDetails) { if (modifiers.noDetails) { - returnText += "\nDetails suppressed by -nd flag."; + returnText += '\nDetails suppressed by -nd flag.'; } else { returnText += `\nDetails:\n${modifiers.spoiler}${returnmsg.line3}${modifiers.spoiler}`; } @@ -74,30 +75,31 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: // If the roll was a GM roll, send DMs to all the GMs if (modifiers.gmRoll) { // Make a new return line to be sent to the roller - const normalText = `<@${message.authorId}>${returnmsg.line1}\nResults have been messaged to the following GMs: ${modifiers.gms.join(" ")}`; + const normalText = `<@${message.authorId}>${returnmsg.line1}\nResults have been messaged to the following GMs: ${modifiers.gms.join(' ')}`; // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged - modifiers.gms.forEach(async e => { + modifiers.gms.forEach(async (e) => { log(LT.LOG, `Messaging GM ${e}`); // If its too big, collapse it into a .txt file and send that instead. - const b = await new Blob([returnText as BlobPart], { "type": "text" }); + const b = await new Blob([returnText as BlobPart], { 'type': 'text' }); if (b.size > 8388290) { // Update return text // todo: embedify - returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; + returnText = + `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; // Attempt to DM the GMs and send a warning if it could not DM a GM - await sendDirectMessage(BigInt(e.substring(2, (e.length - 1))), returnText).catch(() => { + await sendDirectMessage(BigInt(e.substring(2, e.length - 1)), returnText).catch(() => { message.send(generateDMFailed(e)); }); } else { - // Update return + // Update return // todo: embedify returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nFull details have been attached to this messaged as a \`.txt\` file for verification purposes.`; // Attempt to DM the GMs and send a warning if it could not DM a GM - await sendDirectMessage(BigInt(e.substring(2, (e.length - 1))), { "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }).catch(() => { + await sendDirectMessage(BigInt(e.substring(2, e.length - 1)), { 'content': returnText, 'file': { 'blob': b, 'name': 'rollDetails.txt' } }).catch(() => { message.send(generateDMFailed(e)); }); } @@ -108,7 +110,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: if (DEVMODE && config.logRolls) { // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch((e) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); }); } @@ -116,23 +118,25 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: // When not a GM roll, make sure the message is not too big if (returnText.length > 2000) { // If its too big, collapse it into a .txt file and send that instead. - const b = await new Blob([returnText as BlobPart], { "type": "text" }); + const b = await new Blob([returnText as BlobPart], { 'type': 'text' }); if (b.size > 8388290) { // Update return text - returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; + returnText = + `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details could not be attached to this messaged as a \`.txt\` file as the file would be too large for Discord to handle. If you would like to see the details of rolls, please send the rolls in multiple messages instead of bundled into one.`; // Send the results m.edit(returnText); } else { // Update return text - returnText = `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`; + returnText = + `<@${message.authorId}>${returnmsg.line1}\n${returnmsg.line2}\nDetails have been ommitted from this message for being over 2000 characters. Full details have been attached to this messaged as a \`.txt\` file for verification purposes.`; // Remove the original message to send new one with attachment m.delete(); // todo: embedify - await message.send({ "content": returnText, "file": { "blob": b, "name": "rollDetails.txt" } }); + await message.send({ 'content': returnText, 'file': { 'blob': b, 'name': 'rollDetails.txt' } }); } } else { // Finally send the text @@ -141,7 +145,7 @@ export const roll = async (message: DiscordenoMessage, args: string[], command: if (DEVMODE && config.logRolls) { // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(0, 0), [originalCommand, returnText, m.id]).catch((e) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); }); } diff --git a/src/commands/roll/_index.ts b/src/commands/roll/_index.ts index ac6686d..71ee943 100644 --- a/src/commands/roll/_index.ts +++ b/src/commands/roll/_index.ts @@ -1,5 +1,5 @@ -import { getModifiers } from "./getModifiers.ts"; +import { getModifiers } from './getModifiers.ts'; export default { - getModifiers + getModifiers, }; diff --git a/src/commands/roll/getModifiers.ts b/src/commands/roll/getModifiers.ts index c4edbc3..56af1fd 100644 --- a/src/commands/roll/getModifiers.ts +++ b/src/commands/roll/getModifiers.ts @@ -1,88 +1,88 @@ -import config from "../../../config.ts"; -import { DEVMODE } from "../../../flags.ts"; -import { dbClient, queries } from "../../db.ts"; +import config from '../../../config.ts'; +import { DEVMODE } from '../../../flags.ts'; +import { dbClient, queries } from '../../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../../deps.ts"; -import { generateRollError } from "../../constantCmds.ts"; -import { RollModifiers } from "../../mod.d.ts"; + LT, +} from '../../../deps.ts'; +import { generateRollError } from '../../constantCmds.ts'; +import { RollModifiers } from '../../mod.d.ts'; export const getModifiers = (m: DiscordenoMessage, args: string[], command: string, originalCommand: string): RollModifiers => { - const errorType = "Modifiers invalid:"; + const errorType = 'Modifiers invalid:'; const modifiers: RollModifiers = { noDetails: false, superNoDetails: false, - spoiler: "", + spoiler: '', maxRoll: false, nominalRoll: false, gmRoll: false, gms: [], - order: "", + order: '', valid: false, - count: false + count: false, }; // Check if any of the args are command flags and pull those out into the modifiers object for (let i = 0; i < args.length; i++) { - log(LT.LOG, `Checking ${command}${args.join(" ")} for command modifiers ${i}`); + log(LT.LOG, `Checking ${command}${args.join(' ')} for command modifiers ${i}`); let defaultCase = false; switch (args[i].toLowerCase()) { - case "-c": + case '-c': modifiers.count = true; break; - case "-nd": + case '-nd': modifiers.noDetails = true; break; - case "-snd": + case '-snd': modifiers.superNoDetails = true; break; - case "-s": - modifiers.spoiler = "||"; + case '-s': + modifiers.spoiler = '||'; break; - case "-m": + case '-m': modifiers.maxRoll = true; break; - case "-n": + case '-n': modifiers.nominalRoll = true; break; - case "-gm": + case '-gm': modifiers.gmRoll = true; // -gm is a little more complex, as we must get all of the GMs that need to be DMd - while (((i + 1) < args.length) && args[i + 1].startsWith("<@")) { + while (((i + 1) < args.length) && args[i + 1].startsWith('<@')) { log(LT.LOG, `Finding all GMs, checking args ${JSON.stringify(args)}`); // Keep looping thru the rest of the args until one does not start with the discord mention code - modifiers.gms.push(args[i + 1].replace(/[!]/g, "")); - args.splice((i + 1), 1); + modifiers.gms.push(args[i + 1].replace(/[!]/g, '')); + args.splice(i + 1, 1); } if (modifiers.gms.length < 1) { // If -gm is on and none were found, throw an error - m.edit(generateRollError(errorType, "Must specifiy at least one GM by @mentioning them")); + m.edit(generateRollError(errorType, 'Must specifiy at least one GM by @mentioning them')); if (DEVMODE && config.logRolls) { // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, "NoGMsFound", m.id]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, 'NoGMsFound', m.id]).catch((e) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); }); } return modifiers; } break; - case "-o": + case '-o': // Shift the -o out of the array so the next item is the direction args.splice(i, 1); - - if (!args[i] || args[i].toLowerCase()[0] !== "d" && args[i].toLowerCase()[0] !== "a") { + + if (!args[i] || args[i].toLowerCase()[0] !== 'd' && args[i].toLowerCase()[0] !== 'a') { // If -o is on and asc or desc was not specified, error out - m.edit(generateRollError(errorType, "Must specifiy `a` or `d` to order the rolls ascending or descending")); + m.edit(generateRollError(errorType, 'Must specifiy `a` or `d` to order the rolls ascending or descending')); if (DEVMODE && config.logRolls) { // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, "NoOrderFound", m.id]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, 'NoOrderFound', m.id]).catch((e) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); }); } @@ -105,11 +105,11 @@ export const getModifiers = (m: DiscordenoMessage, args: string[], command: stri // maxRoll and nominalRoll cannot both be on, throw an error if (modifiers.maxRoll && modifiers.nominalRoll) { - m.edit(generateRollError(errorType, "Cannot maximise and nominise the roll at the same time")); + m.edit(generateRollError(errorType, 'Cannot maximise and nominise the roll at the same time')); if (DEVMODE && config.logRolls) { // If enabled, log rolls so we can verify the bots math - dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, "MaxAndNominal", m.id]).catch(e => { + dbClient.execute(queries.insertRollLogCmd(0, 1), [originalCommand, 'MaxAndNominal', m.id]).catch((e) => { log(LT.ERROR, `Failed to insert into DB: ${JSON.stringify(e)}`); }); } diff --git a/src/commands/rollHelp.ts b/src/commands/rollHelp.ts index 6adc211..21b9ee6 100644 --- a/src/commands/rollHelp.ts +++ b/src/commands/rollHelp.ts @@ -1,20 +1,20 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const rollHelp = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("rollhelp");`).catch(e => { + dbClient.execute(`CALL INC_CNT("rollhelp");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.rollHelp).catch(e => { + message.send(constantCmds.rollHelp).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/commands/stats.ts b/src/commands/stats.ts index 36fed0a..dfc58ce 100644 --- a/src/commands/stats.ts +++ b/src/commands/stats.ts @@ -1,16 +1,18 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps - cache, cacheHandlers, DiscordenoMessage, - + cache, + cacheHandlers, + DiscordenoMessage, + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds, generateStats } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds, generateStats } from '../constantCmds.ts'; export const stats = async (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("stats");`).catch(e => { + dbClient.execute(`CALL INC_CNT("stats");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); @@ -18,19 +20,19 @@ export const stats = async (message: DiscordenoMessage) => { const m = await message.send(constantCmds.loadingStats); // Calculate how many times commands have been run - const rollQuery = await dbClient.query(`SELECT count FROM command_cnt WHERE command = "roll";`).catch(e => { + const rollQuery = await dbClient.query(`SELECT count FROM command_cnt WHERE command = "roll";`).catch((e) => { log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e)}`); }); - const totalQuery = await dbClient.query(`SELECT SUM(count) as count FROM command_cnt;`).catch(e => { + const totalQuery = await dbClient.query(`SELECT SUM(count) as count FROM command_cnt;`).catch((e) => { log(LT.ERROR, `Failed to query DB: ${JSON.stringify(e)}`); }); const rolls = BigInt(rollQuery[0].count); const total = BigInt(totalQuery[0].count); - const cachedGuilds = await cacheHandlers.size("guilds"); - const cachedChannels = await cacheHandlers.size("channels"); - const cachedMembers = await cacheHandlers.size("members"); - m.edit(generateStats(cachedGuilds + cache.dispatchedGuildIds.size, cachedChannels + cache.dispatchedChannelIds.size, cachedMembers, rolls, total - rolls)).catch(e => { + const cachedGuilds = await cacheHandlers.size('guilds'); + const cachedChannels = await cacheHandlers.size('channels'); + const cachedMembers = await cacheHandlers.size('members'); + m.edit(generateStats(cachedGuilds + cache.dispatchedGuildIds.size, cachedChannels + cache.dispatchedChannelIds.size, cachedMembers, rolls, total - rolls)).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); } catch (e) { diff --git a/src/commands/version.ts b/src/commands/version.ts index 5b9221d..9e4c317 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -1,20 +1,20 @@ -import { dbClient } from "../db.ts"; +import { dbClient } from '../db.ts'; import { // Discordeno deps DiscordenoMessage, - + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; -import { constantCmds } from "../constantCmds.ts"; + LT, +} from '../../deps.ts'; +import { constantCmds } from '../constantCmds.ts'; export const version = (message: DiscordenoMessage) => { // Light telemetry to see how many times a command is being run - dbClient.execute(`CALL INC_CNT("version");`).catch(e => { + dbClient.execute(`CALL INC_CNT("version");`).catch((e) => { log(LT.ERROR, `Failed to call stored procedure INC_CNT: ${JSON.stringify(e)}`); }); - message.send(constantCmds.version).catch(e => { + message.send(constantCmds.version).catch((e) => { log(LT.ERROR, `Failed to send message: ${JSON.stringify(message)} | ${JSON.stringify(e)}`); }); }; diff --git a/src/constantCmds.ts b/src/constantCmds.ts index 3120c9f..87fecba 100644 --- a/src/constantCmds.ts +++ b/src/constantCmds.ts @@ -1,5 +1,5 @@ -import config from "../config.ts"; -import { CountDetails } from "./solver/solver.d.ts"; +import config from '../config.ts'; +import { CountDetails } from './solver/solver.d.ts'; const failColor = 0xe71212; const warnColor = 0xe38f28; @@ -11,211 +11,231 @@ export const constantCmds = { apiDeleteFail: { embeds: [{ color: failColor, - title: "Failed to delete this guild from the database.", - description: "If this issue persists, please report this to the developers." - }] + title: 'Failed to delete this guild from the database.', + description: 'If this issue persists, please report this to the developers.', + }], }, apiGuildOnly: { embeds: [{ color: failColor, - title: "API commands are only available in guilds." - }] + title: 'API commands are only available in guilds.', + }], }, apiHelp: { embeds: [ { color: infoColor2, - title: "The Artificer's API Details:", - description: `The Artificer has a built in API that allows user to roll dice into Discord using third party programs. By default, API rolls are blocked from being sent in your guild. The API warning is also enabled by default. These commands may only be used by the Owner or Admins of your guild. + title: 'The Artificer\'s API Details:', + description: + `The Artificer has a built in API that allows user to roll dice into Discord using third party programs. By default, API rolls are blocked from being sent in your guild. The API warning is also enabled by default. These commands may only be used by the Owner or Admins of your guild. For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer). -You may enable and disable the API rolls for your guild as needed.` - }, { +You may enable and disable the API rolls for your guild as needed.`, + }, + { color: infoColor1, - title: "Available API Commands:", + title: 'Available API Commands:', fields: [ { name: `\`${config.prefix}api help\``, - value: "This command", - inline: true - }, { + value: 'This command', + inline: true, + }, + { name: `\`${config.prefix}api status\``, - value: "Shows the current status of the API for the channel this was run in", - inline: true - }, { + value: 'Shows the current status of the API for the channel this was run in', + inline: true, + }, + { name: `\`${config.prefix}api allow/enable\``, - value: "Allows API Rolls to be sent to the channel this was run in", - inline: true - }, { + value: 'Allows API Rolls to be sent to the channel this was run in', + inline: true, + }, + { name: `\`${config.prefix}api block/disable\``, - value: "Blocks API Rolls from being sent to the channel this was run in", - inline: true - }, { + value: 'Blocks API Rolls from being sent to the channel this was run in', + inline: true, + }, + { name: `\`${config.prefix}api delete\``, - value: "Deletes this channel's settings from The Artificer's database", - inline: true - }, { + value: 'Deletes this channel\'s settings from The Artificer\'s database', + inline: true, + }, + { name: `\`${config.prefix}api show-warn\``, - value: "Shows the API warning on all rolls sent to the channel this was run in", - inline: true - }, { + value: 'Shows the API warning on all rolls sent to the channel this was run in', + inline: true, + }, + { name: `\`${config.prefix}api hide-warn\``, - value: "Hides the API warning on all rolls sent to the channel this was run in", - inline: true - } - ] - } - ] + value: 'Hides the API warning on all rolls sent to the channel this was run in', + inline: true, + }, + ], + }, + ], }, apiPermError: { embeds: [{ color: failColor, - title: "API commands are powerful and can only be used by guild Owners and Admins.", - description: "For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer)." - }] + title: 'API commands are powerful and can only be used by guild Owners and Admins.', + description: 'For information on how to use the API, please check the GitHub README for more information [here](https://github.com/Burn-E99/TheArtificer).', + }], }, apiRemoveGuild: { embeds: [{ color: successColor, - title: "This guild's API setting has been removed from The Artifier's Database." - }] + title: 'This guild\'s API setting has been removed from The Artifier\'s Database.', + }], }, apiStatusFail: { embeds: [{ color: failColor, - title: "Failed to check API rolls status for this guild.", - description: "If this issue persists, please report this to the developers." - }] + title: 'Failed to check API rolls status for this guild.', + description: 'If this issue persists, please report this to the developers.', + }], }, help: { embeds: [{ color: infoColor2, - title: "The Artificer's Available Commands:", + title: 'The Artificer\'s Available Commands:', fields: [ { name: `\`${config.prefix}?\``, - value: "This command", - inline: true - }, { + value: 'This command', + inline: true, + }, + { name: `\`${config.prefix}rollhelp\` or \`${config.prefix}??\``, value: `Details on how to use the roll command, listed as \`${config.prefix}xdy...${config.postfix}\` below`, - inline: true - }, { + inline: true, + }, + { name: `\`${config.prefix}api [subcommand]\``, value: `Administrative tools for the bots's API, run \`${config.prefix}api help\` for more details`, - inline: true - }, { + inline: true, + }, + { name: `\`${config.prefix}ping\``, - value: "Pings the bot to check connectivity", - inline: true - }, { + value: 'Pings the bot to check connectivity', + inline: true, + }, + { name: `\`${config.prefix}info\``, - value: "Prints some information and links relating to the bot", - inline: true - }, { + value: 'Prints some information and links relating to the bot', + inline: true, + }, + { name: `\`${config.prefix}privacy\``, - value: "Prints some information about the Privacy Policy", - inline: true - }, { + value: 'Prints some information about the Privacy Policy', + inline: true, + }, + { name: `\`${config.prefix}version\``, - value: "Prints the bots version", - inline: true - }, { + value: 'Prints the bots version', + inline: true, + }, + { name: `\`${config.prefix}popcat\``, - value: "Popcat", - inline: true - }, { + value: 'Popcat', + inline: true, + }, + { name: `\`${config.prefix}report [text]\``, - value: "Report a command that failed to run", - inline: true - }, { + value: 'Report a command that failed to run', + inline: true, + }, + { name: `\`${config.prefix}stats\``, - value: "Statistics on the bot", - inline: true - }, { + value: 'Statistics on the bot', + inline: true, + }, + { name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`, - value: `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`), run \`${config.prefix}??\` for more details`, - inline: true - } - ] - }] + value: + `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`), run \`${config.prefix}??\` for more details`, + inline: true, + }, + ], + }], }, indev: { embeds: [{ color: warnColor, - title: "Command is in development, please try again later." - }] + title: 'Command is in development, please try again later.', + }], }, info: { embeds: [{ color: infoColor2, - title: "The Artificer, a Discord bot that specializing in rolling dice and calculating math", + title: 'The Artificer, a Discord bot that specializing in rolling dice and calculating math', description: `The Artificer 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).` - }] +Need help with this bot? Join my support server [here](https://discord.gg/peHASXMZYv).`, + }], }, loadingStats: { embeds: [{ color: warnColor, - title: "Compiling latest statistics . . ." - }] + title: 'Compiling latest statistics . . .', + }], }, mention: { embeds: [{ color: infoColor1, title: `Hello! I am ${config.name}!`, fields: [{ - name: "I am a bot that specializes in rolling dice and doing basic algebra", - value: `To learn about my available commands, please run \`${config.prefix}help\`` - }] - }] + name: 'I am a bot that specializes in rolling dice and doing basic algebra', + value: `To learn about my available commands, please run \`${config.prefix}help\``, + }], + }], }, privacy: { embeds: [{ color: infoColor1, - title: "Privacy Policy", + title: 'Privacy Policy', fields: [{ - name: "The Artificer does not track or collect user information via Discord.", - value: `The only user submitted information that is stored is submitted via the \`${config.prefix}report\` command. This information is only stored for a short period of time in a location that only the Developer of The Artificer can see. + name: 'The Artificer does not track or collect user information via Discord.', + value: + `The only user submitted information that is stored is submitted via the \`${config.prefix}report\` command. This information is only stored for a short period of time in a location that only the Developer of The Artificer can see. For more details, please check out the Privacy Policy on the GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/PRIVACY.md). -Terms of Service can also be found on GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/TERMS.md).` - }] - }] +Terms of Service can also be found on GitHub [here](https://github.com/Burn-E99/TheArtificer/blob/master/TERMS.md).`, + }], + }], }, report: { embeds: [{ color: successColor, - title: "Failed command has been reported to my developer.", - description: `For more in depth support, and information about planned maintenance, please join the support server [here](https://discord.gg/peHASXMZYv).` - }] + title: 'Failed command has been reported to my developer.', + description: `For more in depth support, and information about planned maintenance, please join the support server [here](https://discord.gg/peHASXMZYv).`, + }], }, reportFail: { embeds: [{ color: failColor, - title: "Please provide a short description of what failed", - description: "Providing a short description helps my developer quickly diagnose what went wrong." - }] + title: 'Please provide a short description of what failed', + description: 'Providing a short description helps my developer quickly diagnose what went wrong.', + }], }, rip: { embeds: [{ color: infoColor2, - title: "The Artificer was built in memory of my Grandmother, Babka", + title: 'The Artificer was built in memory of my Grandmother, Babka', description: `With much love, Ean - December 21, 2020` - }] + December 21, 2020`, + }], }, rollHelp: { embeds: [ { color: infoColor2, - title: "The Artificer's Roll Command Details:", - description: `You can chain as many of these options as you want, as long as the option does not disallow it. + title: 'The Artificer\'s Roll Command Details:', + description: `You can chain as many of these options as you want, as long as the option does not disallow it. This command also can fully solve math equations with parenthesis. @@ -223,273 +243,314 @@ Terms of Service can also be found on GitHub [here](https://github.com/Burn-E99/ fields: [ { name: `\`${config.prefix}xdydzracsq!${config.postfix}\` ...`, - value: `Rolls all configs requested, you may repeat the command multiple times in the same message (just ensure you close each roll with \`${config.postfix}\`)` - }, { - name: "`x` [Optional]", - value: "Number of dice to roll, if omitted, 1 is used", - inline: true - }, { - name: "`dy` [Required]", - value: "Size of dice to roll, `d20` = 20 sided die", - inline: true - }, { - name: "`dz` or `dlz` [Optional]", - value: "Drops the lowest `z` dice, cannot be used with `kz`", - inline: true - }, { - name: "`kz` or `khz` [Optional]", - value: "Keeps the highest `z` dice, cannot be used with `dz`", - inline: true - }, { - name: "`dhz` [Optional]", - value: "Drops the highest `z` dice, cannot be used with `kz`", - inline: true - }, { - name: "`klz` [Optional]", - value: "Keeps the lowest `z` dice, cannot be used with `dz`", - inline: true - }, { - name: "`ra` or `r=q` [Optional]", - value: "Rerolls any rolls that match `a`, `r3` will reroll every die that land on 3, throwing out old rolls, cannot be used with `ro`", - inline: true - }, { - name: "`rq` [Optional]", - value: "Rerolls any rolls that are greater than or equal to `a`, `r3` will reroll every die that land on 3 or greater, throwing out old rolls, cannot be used with `ro`", - inline: true - }, { - name: "`roa` or `ro=q` [Optional]", - value: "Rerolls any rolls that match `a`, `ro3` will reroll each die that lands on 3 ONLY ONE TIME, throwing out old rolls, cannot be used with `r`", - inline: true - }, { - name: "`roq` [Optional]", - value: "Rerolls any rolls that are greater than or equal to `a`, `ro3` will reroll each die that lands on 3 or greater ONLY ONE TIME, throwing out old rolls, cannot be used with `r`", - inline: true - }, { - name: "`csq` or `cs=q` [Optional]", - value: "Changes crit score to `q`", - inline: true - }, { - name: "`csq` [Optional]", - value: "Changes crit score to be greater than or equal to `q`", - inline: true - }, { - name: "`cfq` or `cf=q` [Optional]", - value: "Changes crit fail to `q`", - inline: true - }, { - name: "`cfq` [Optional]", - value: "Changes crit fail to be greater than or equal to `q`", - inline: true - }, { - name: "`!` [Optional]", - value: "Exploding, rolls another `dy` for every crit success", - inline: true - }, { - name: "`!o` [Optional]", - value: "Exploding Once, rolls one `dy` for each original crit success", - inline: true - }, { - name: "`!=u` [Optional]", - value: "Explode on `u`, rolls another `dy` for every die that lands on `u`", - inline: true - }, { - name: "`!>u` [Optional]", - value: "Explode on `u` and greater, rolls another `dy` for every die that lands on `u` or greater", - inline: true - }, { - name: "`!q` [Optional]', + value: 'Rerolls any rolls that are greater than or equal to `a`, `r3` will reroll every die that land on 3 or greater, throwing out old rolls, cannot be used with `ro`', + inline: true, + }, + { + name: '`roa` or `ro=q` [Optional]', + value: 'Rerolls any rolls that match `a`, `ro3` will reroll each die that lands on 3 ONLY ONE TIME, throwing out old rolls, cannot be used with `r`', + inline: true, + }, + { + name: '`roq` [Optional]', + value: 'Rerolls any rolls that are greater than or equal to `a`, `ro3` will reroll each die that lands on 3 or greater ONLY ONE TIME, throwing out old rolls, cannot be used with `r`', + inline: true, + }, + { + name: '`csq` or `cs=q` [Optional]', + value: 'Changes crit score to `q`', + inline: true, + }, + { + name: '`csq` [Optional]', + value: 'Changes crit score to be greater than or equal to `q`', + inline: true, + }, + { + name: '`cfq` or `cf=q` [Optional]', + value: 'Changes crit fail to `q`', + inline: true, + }, + { + name: '`cfq` [Optional]', + value: 'Changes crit fail to be greater than or equal to `q`', + inline: true, + }, + { + name: '`!` [Optional]', + value: 'Exploding, rolls another `dy` for every crit success', + inline: true, + }, + { + name: '`!o` [Optional]', + value: 'Exploding Once, rolls one `dy` for each original crit success', + inline: true, + }, + { + name: '`!=u` [Optional]', + value: 'Explode on `u`, rolls another `dy` for every die that lands on `u`', + inline: true, + }, + { + name: '`!>u` [Optional]', + value: 'Explode on `u` and greater, rolls another `dy` for every die that lands on `u` or greater', + inline: true, + }, + { + name: '`!u` [Optional]", - value: "Explode Once on `u` and greater, rolls another `dy` for each original die that landed on `u` or greater", - inline: true - }, { - name: "`!ou` [Optional]', + value: 'Explode Once on `u` and greater, rolls another `dy` for each original die that landed on `u` or greater', + inline: true, + }, + { + name: '`!o ({ embeds: [{ color: infoColor1, - title: time === -1 ? "Ping?" : `Pong! Latency is ${time}ms.` - }] + title: time === -1 ? 'Ping?' : `Pong! Latency is ${time}ms.`, + }], }); export const generateReport = (msg: string) => ({ embeds: [{ color: infoColor2, - title: "USER REPORT:", - description: msg || "No message" - }] + title: 'USER REPORT:', + description: msg || 'No message', + }], }); export const generateStats = (guildCount: number, channelCount: number, memberCount: number, rollCount: bigint, utilityCount: bigint) => ({ embeds: [{ color: infoColor2, - title: "The Artificer's Statistics:", + title: 'The Artificer\'s Statistics:', fields: [ { - name: "Guilds:", + name: 'Guilds:', value: `${guildCount}`, - inline: true - }, { - name: "Channels:", + inline: true, + }, + { + name: 'Channels:', value: `${channelCount}`, - inline: true - }, { - name: "Active Members:", + inline: true, + }, + { + name: 'Active Members:', value: `${memberCount}`, - inline: true - }, { - name: "Roll Commands:", + inline: true, + }, + { + name: 'Roll Commands:', value: `${rollCount}`, - inline: true - }, { - name: "Utility Commands:", + inline: true, + }, + { + name: 'Utility Commands:', value: `${utilityCount}`, - inline: true - } - ] - }] + inline: true, + }, + ], + }], }); export const generateApiFailed = (args: string) => ({ embeds: [{ color: failColor, title: `Failed to ${args} API rolls for this guild.`, - description: "If this issue persists, please report this to the developers." - }] + description: 'If this issue persists, please report this to the developers.', + }], }); export const generateApiStatus = (banned: boolean, active: boolean) => ({ embeds: [{ color: infoColor1, - title: `The Artificer's API is ${config.api.enable ? "currently enabled" : "currently disabled"}.`, - description: banned ? "API rolls are banned from being used in this guild.\n\nThis will not be reversed." : `API rolls are ${active ? "allowed" : "blocked from being used"} in this guild.` - }] + title: `The Artificer's API is ${config.api.enable ? 'currently enabled' : 'currently disabled'}.`, + description: banned ? 'API rolls are banned from being used in this guild.\n\nThis will not be reversed.' : `API rolls are ${active ? 'allowed' : 'blocked from being used'} in this guild.`, + }], }); export const generateApiSuccess = (args: string) => ({ embeds: [{ color: successColor, - title: `API rolls have successfully been ${args} for this guild.` - }] + title: `API rolls have successfully been ${args} for this guild.`, + }], }); export const generateDMFailed = (user: string) => ({ embeds: [{ color: failColor, title: `WARNING: ${user} could not be messaged.`, - description: "If this issue persists, make sure direct messages are allowed from this server." - }] + description: 'If this issue persists, make sure direct messages are allowed from this server.', + }], }); export const generateApiKeyEmail = (email: string, key: string) => ({ @@ -498,13 +559,15 @@ export const generateApiKeyEmail = (email: string, key: string) => ({ color: infoColor1, fields: [ { - name: "Send to:", - value: email - }, { - name: "Subject:", - value: "Artificer API Key" - }, { - name: "Body:", + name: 'Send to:', + value: email, + }, + { + name: 'Subject:', + value: 'Artificer API Key', + }, + { + name: 'Body:', value: `Hello Artificer API User, Welcome aboard The Artificer's API. You can find full details about the API on the GitHub: https://github.com/Burn-E99/TheArtificer @@ -514,10 +577,10 @@ Your API Key is: ${key} Guard this well, as there is zero tolerance for API abuse. Welcome aboard, -The Artificer Developer - Ean Milligan` - } - ] - }] +The Artificer Developer - Ean Milligan`, + }, + ], + }], }); export const generateApiDeleteEmail = (email: string, deleteCode: string) => ({ @@ -526,13 +589,15 @@ export const generateApiDeleteEmail = (email: string, deleteCode: string) => ({ color: infoColor1, fields: [ { - name: "Send to:", - value: email - }, { - name: "Subject:", - value: "Artificer API Delete Code" - }, { - name: "Body:", + name: 'Send to:', + value: email, + }, + { + name: 'Subject:', + value: 'Artificer API Delete Code', + }, + { + name: 'Body:', value: `Hello Artificer API User, I am sorry to see you go. If you would like, please respond to this email detailing what I could have done better. @@ -540,53 +605,58 @@ I am sorry to see you go. If you would like, please respond to this email detai As requested, here is your delete code: ${deleteCode} Sorry to see you go, -The Artificer Developer - Ean Milligan` - } - ] - }] +The Artificer Developer - Ean Milligan`, + }, + ], + }], }); export const generateRollError = (errorType: string, errorMsg: string) => ({ embeds: [{ color: failColor, - title: "Roll command encountered the following error:", + title: 'Roll command encountered the following error:', fields: [{ name: errorType, - value: `${errorMsg}\n\nPlease try again. If the error is repeated, please report the issue using the \`${config.prefix}report\` command.` - }] - }] + value: `${errorMsg}\n\nPlease try again. If the error is repeated, please report the issue using the \`${config.prefix}report\` command.`, + }], + }], }); export const generateCountDetails = (counts: CountDetails) => ({ embeds: [{ color: infoColor1, - title: "Roll Count Details:", + title: 'Roll Count Details:', fields: [ { - name: "Total Rolls:", + name: 'Total Rolls:', details: `${counts.total}`, - inline: true - }, { - name: "Successful Rolls:", + inline: true, + }, + { + name: 'Successful Rolls:', details: `${counts.successful}`, - inline: true - }, { - name: "Failed Rolls:", + inline: true, + }, + { + name: 'Failed Rolls:', details: `${counts.failed}`, - inline: true - }, { - name: "Rerolled Dice:", + inline: true, + }, + { + name: 'Rerolled Dice:', details: `${counts.rerolled}`, - inline: true - }, { - name: "Dropped Dice:", + inline: true, + }, + { + name: 'Dropped Dice:', details: `${counts.dropped}`, - inline: true - }, { - name: "Exploded Dice:", + inline: true, + }, + { + name: 'Exploded Dice:', details: `${counts.exploded}`, - inline: true - } - ] - }] + inline: true, + }, + ], + }], }); diff --git a/src/db.ts b/src/db.ts index c77d32a..65ef64b 100644 --- a/src/db.ts +++ b/src/db.ts @@ -1,15 +1,15 @@ -import config from "../config.ts"; -import { Client } from "../deps.ts"; -import { LOCALMODE } from "../flags.ts"; +import config from '../config.ts'; +import { Client } from '../deps.ts'; +import { LOCALMODE } from '../flags.ts'; export const dbClient = await new Client().connect({ hostname: LOCALMODE ? config.db.localhost : config.db.host, port: config.db.port, db: config.db.name, username: config.db.username, - password: config.db.password + password: config.db.password, }); export const queries = { - insertRollLogCmd: (api: number, error: number) => `INSERT INTO roll_log(input,result,resultid,api,error) values(?,?,?,${api},${error})` + insertRollLogCmd: (api: number, error: number) => `INSERT INTO roll_log(input,result,resultid,api,error) values(?,?,?,${api},${error})`, }; diff --git a/src/intervals.ts b/src/intervals.ts index 2bcd9a4..8930c9a 100644 --- a/src/intervals.ts +++ b/src/intervals.ts @@ -6,18 +6,19 @@ import { // Discordeno deps - cache, cacheHandlers, - + cache, + cacheHandlers, + log, // Log4Deno deps - LT, log -} from "../deps.ts"; + LT, +} from '../deps.ts'; -import config from "../config.ts"; +import config from '../config.ts'; // getRandomStatus() returns status as string // Gets a new random status for the bot const getRandomStatus = async (): Promise => { - let status = ""; + let status = ''; switch (Math.floor((Math.random() * 4) + 1)) { case 1: status = `${config.prefix}help for commands`; @@ -29,29 +30,29 @@ const getRandomStatus = async (): Promise => { status = `${config.prefix}info to learn more`; break; default: { - const cachedCount = await cacheHandlers.size("guilds") + const cachedCount = await cacheHandlers.size('guilds'); status = `Rolling dice for ${cachedCount + cache.dispatchedGuildIds.size} servers`; break; } } - + return status; }; // updateListStatistics(bot ID, current guild count) returns nothing // Sends the current server count to all bot list sites we are listed on const updateListStatistics = (botID: bigint, serverCount: number): void => { - config.botLists.forEach(async e => { - log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`) + config.botLists.forEach(async (e) => { + log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`); if (e.enabled) { const tempHeaders = new Headers(); tempHeaders.append(e.headers[0].header, e.headers[0].value); - tempHeaders.append("Content-Type", "application/json"); + tempHeaders.append('Content-Type', 'application/json'); // ?{} is a template used in config, just need to replace it with the real value - const response = await fetch(e.apiUrl.replace("?{bot_id}", botID.toString()), { - "method": 'POST', - "headers": tempHeaders, - "body": JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number + const response = await fetch(e.apiUrl.replace('?{bot_id}', botID.toString()), { + 'method': 'POST', + 'headers': tempHeaders, + 'body': JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()), // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number }); log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); } diff --git a/src/mod.d.ts b/src/mod.d.ts index c99581b..8ec1f65 100644 --- a/src/mod.d.ts +++ b/src/mod.d.ts @@ -2,23 +2,23 @@ // EmojiConf is used as a structure for the emojis stored in config.ts export type EmojiConf = { - name: string, - aliases: Array, - id: string, - animated: boolean, - deleteSender: boolean + name: string; + aliases: Array; + id: string; + animated: boolean; + deleteSender: boolean; }; // RollModifiers is the structure to keep track of the decorators applied to a roll command export type RollModifiers = { - noDetails: boolean, - superNoDetails: boolean, - spoiler: string, - maxRoll: boolean, - nominalRoll: boolean, - gmRoll: boolean, - gms: string[], - order: string, - valid: boolean, - count: boolean + noDetails: boolean; + superNoDetails: boolean; + spoiler: string; + maxRoll: boolean; + nominalRoll: boolean; + gmRoll: boolean; + gms: string[]; + order: string; + valid: boolean; + count: boolean; }; diff --git a/src/solver/_index.ts b/src/solver/_index.ts index 0eb901a..d8a4c4d 100644 --- a/src/solver/_index.ts +++ b/src/solver/_index.ts @@ -1,5 +1,5 @@ -import { parseRoll } from "./parser.ts"; +import { parseRoll } from './parser.ts'; export default { - parseRoll + parseRoll, }; diff --git a/src/solver/parser.ts b/src/solver/parser.ts index c204553..8142621 100644 --- a/src/solver/parser.ts +++ b/src/solver/parser.ts @@ -1,26 +1,27 @@ import { + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; + LT, +} from '../../deps.ts'; -import config from "../../config.ts"; +import config from '../../config.ts'; -import { RollModifiers } from "../mod.d.ts"; -import { SolvedStep, SolvedRoll, ReturnData } from "./solver.d.ts"; -import { compareTotalRolls, escapeCharacters } from "./rollUtils.ts"; -import { formatRoll } from "./rollFormatter.ts"; -import { fullSolver } from "./solver.ts"; +import { RollModifiers } from '../mod.d.ts'; +import { ReturnData, SolvedRoll, SolvedStep } from './solver.d.ts'; +import { compareTotalRolls, escapeCharacters } from './rollUtils.ts'; +import { formatRoll } from './rollFormatter.ts'; +import { fullSolver } from './solver.ts'; // parseRoll(fullCmd, modifiers) // parseRoll handles converting fullCmd into a computer readable format for processing, and finally executes the solving export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll => { const returnmsg = { error: false, - errorMsg: "", - errorCode: "", - line1: "", - line2: "", - line3: "" + errorMsg: '', + errorCode: '', + line1: '', + line2: '', + line3: '', }; // Whole function lives in a try-catch to allow safe throwing of errors on purpose @@ -37,22 +38,22 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll const [tempConf, tempFormat] = sepRolls[i].split(config.postfix); // Remove all spaces from the operation config and split it by any operator (keeping the operator in mathConf for fullSolver to do math on) - const mathConf: (string | number | SolvedStep)[] = <(string | number | SolvedStep)[]>tempConf.replace(/ /g, "").split(/([-+()*/%^])/g); + const mathConf: (string | number | SolvedStep)[] = <(string | number | SolvedStep)[]> tempConf.replace(/ /g, '').split(/([-+()*/%^])/g); // Verify there are equal numbers of opening and closing parenthesis by adding 1 for opening parens and subtracting 1 for closing parens let parenCnt = 0; - mathConf.forEach(e => { + mathConf.forEach((e) => { log(LT.LOG, `Parsing roll ${fullCmd} | Checking parenthesis balance ${e}`); - if (e === "(") { + if (e === '(') { parenCnt++; - } else if (e === ")") { + } else if (e === ')') { parenCnt--; } }); // If the parenCnt is not 0, then we do not have balanced parens and need to error out now if (parenCnt !== 0) { - throw new Error("UnbalancedParens"); + throw new Error('UnbalancedParens'); } // Evaluate all rolls into stepSolve format and all numbers into floats @@ -68,35 +69,35 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll } else if (/([0123456789])/g.test(mathConf[i].toString())) { // If there is a number somewhere in mathconf[i] but there are also other characters preventing it from parsing correctly as a number, it should be a dice roll, parse it as such (if it for some reason is not a dice roll, formatRoll/roll will handle it) mathConf[i] = formatRoll(mathConf[i].toString(), modifiers.maxRoll, modifiers.nominalRoll); - } else if (mathConf[i].toString().toLowerCase() === "e") { + } else if (mathConf[i].toString().toLowerCase() === 'e') { // If the operand is the constant e, create a SolvedStep for it mathConf[i] = { total: Math.E, - details: "*e*", + details: '*e*', containsCrit: false, - containsFail: false + containsFail: false, }; - } else if (mathConf[i].toString().toLowerCase() === "pi" || mathConf[i].toString().toLowerCase() == "𝜋") { + } else if (mathConf[i].toString().toLowerCase() === 'pi' || mathConf[i].toString().toLowerCase() == '𝜋') { // If the operand is the constant pi, create a SolvedStep for it mathConf[i] = { total: Math.PI, - details: "𝜋", + details: '𝜋', containsCrit: false, - containsFail: false + containsFail: false, }; - } else if (mathConf[i].toString().toLowerCase() === "pie") { + } else if (mathConf[i].toString().toLowerCase() === 'pie') { // If the operand is pie, pi*e, create a SolvedStep for e and pi (and the multiplication symbol between them) mathConf[i] = { total: Math.PI, - details: "𝜋", + details: '𝜋', containsCrit: false, - containsFail: false + containsFail: false, }; - mathConf.splice((i + 1), 0, ...["*", { + mathConf.splice(i + 1, 0, ...['*', { total: Math.E, - details: "*e*", + details: '*e*', containsCrit: false, - containsFail: false + containsFail: false, }]); } } @@ -111,51 +112,51 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll rollDetails: tempSolved.details, containsCrit: tempSolved.containsCrit, containsFail: tempSolved.containsFail, - initConfig: tempConf + initConfig: tempConf, }); } // Parsing/Solving done, time to format the output for Discord // Remove any floating spaces from fullCmd - if (fullCmd[fullCmd.length - 1] === " ") { - fullCmd = fullCmd.substring(0, (fullCmd.length - 1)); + if (fullCmd[fullCmd.length - 1] === ' ') { + fullCmd = fullCmd.substring(0, fullCmd.length - 1); } // Escape any | and ` chars in fullCmd to prevent spoilers and code blocks from acting up - fullCmd = escapeCharacters(fullCmd, "|"); - fullCmd = fullCmd.replace(/`/g, ""); + fullCmd = escapeCharacters(fullCmd, '|'); + fullCmd = fullCmd.replace(/`/g, ''); - let line1 = ""; - let line2 = ""; - let line3 = ""; + let line1 = ''; + let line2 = ''; + let line3 = ''; // If maximiseRoll or nominalRoll are on, mark the output as such, else use default formatting if (modifiers.maxRoll) { line1 = ` requested the theoretical maximum of: \`${config.prefix}${fullCmd}\``; - line2 = "Theoretical Maximum Results: "; + line2 = 'Theoretical Maximum Results: '; } else if (modifiers.nominalRoll) { line1 = ` requested the theoretical nominal of: \`${config.prefix}${fullCmd}\``; - line2 = "Theoretical Nominal Results: "; - } else if (modifiers.order === "a") { + line2 = 'Theoretical Nominal Results: '; + } else if (modifiers.order === 'a') { line1 = ` requested the following rolls to be ordered from least to greatest: \`${config.prefix}${fullCmd}\``; - line2 = "Results: "; + line2 = 'Results: '; tempReturnData.sort(compareTotalRolls); - } else if (modifiers.order === "d") { + } else if (modifiers.order === 'd') { line1 = ` requested the following rolls to be ordered from greatest to least: \`${config.prefix}${fullCmd}\``; - line2 = "Results: "; + line2 = 'Results: '; tempReturnData.sort(compareTotalRolls); tempReturnData.reverse(); } else { line1 = ` rolled: \`${config.prefix}${fullCmd}\``; - line2 = "Results: "; + line2 = 'Results: '; } // Fill out all of the details and results now - tempReturnData.forEach(e => { + tempReturnData.forEach((e) => { log(LT.LOG, `Parsing roll ${fullCmd} | Making return text ${JSON.stringify(e)}`); - let preFormat = ""; - let postFormat = ""; + let preFormat = ''; + let postFormat = ''; // If the roll containted a crit success or fail, set the formatting around it if (e.containsCrit) { @@ -168,108 +169,107 @@ export const parseRoll = (fullCmd: string, modifiers: RollModifiers): SolvedRoll } // Populate line2 (the results) and line3 (the details) with their data - if (modifiers.order === "") { - line2 += `${preFormat}${e.rollTotal}${postFormat}${escapeCharacters(e.rollPostFormat, "|*_~`")}`; + if (modifiers.order === '') { + line2 += `${preFormat}${e.rollTotal}${postFormat}${escapeCharacters(e.rollPostFormat, '|*_~`')}`; } else { // If order is on, turn rolls into csv without formatting line2 += `${preFormat}${e.rollTotal}${postFormat}, `; } - line2 = line2.replace(/\*\*\*\*/g, "** **").replace(/____/g, "__ __").replace(/~~~~/g, "~~ ~~"); + line2 = line2.replace(/\*\*\*\*/g, '** **').replace(/____/g, '__ __').replace(/~~~~/g, '~~ ~~'); line3 += `\`${e.initConfig}\` = ${e.rollDetails} = ${preFormat}${e.rollTotal}${postFormat}\n`; }); // If order is on, remove trailing ", " - if (modifiers.order !== "") { - line2 = line2.substring(0, (line2.length - 2)); + if (modifiers.order !== '') { + line2 = line2.substring(0, line2.length - 2); } // Fill in the return block returnmsg.line1 = line1; returnmsg.line2 = line2; returnmsg.line3 = line3; - } catch (solverError) { // Welp, the unthinkable happened, we hit an error // Split on _ for the error messages that have more info than just their name - const [errorName, errorDetails] = solverError.message.split("_"); + const [errorName, errorDetails] = solverError.message.split('_'); - let errorMsg = ""; + let errorMsg = ''; // Translate the errorName to a specific errorMsg switch (errorName) { - case "YouNeedAD": - errorMsg = "Formatting Error: Missing die size and count config"; + case 'YouNeedAD': + errorMsg = 'Formatting Error: Missing die size and count config'; break; - case "FormattingError": - errorMsg = "Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll"; + case 'FormattingError': + errorMsg = 'Formatting Error: Cannot use Keep and Drop at the same time, remove all but one and repeat roll'; break; - case "NoMaxWithDash": - errorMsg = "Formatting Error: CritScore range specified without a maximum, remove - or add maximum to correct"; + case 'NoMaxWithDash': + errorMsg = 'Formatting Error: CritScore range specified without a maximum, remove - or add maximum to correct'; break; - case "UnknownOperation": + case 'UnknownOperation': errorMsg = `Error: Unknown Operation ${errorDetails}`; - if (errorDetails === "-") { - errorMsg += "\nNote: Negative numbers are not supported"; - } else if (errorDetails === " ") { + if (errorDetails === '-') { + errorMsg += '\nNote: Negative numbers are not supported'; + } else if (errorDetails === ' ') { errorMsg += `\nNote: Every roll must be closed by ${config.postfix}`; } break; - case "NoZerosAllowed": - errorMsg = "Formatting Error: "; + case 'NoZerosAllowed': + errorMsg = 'Formatting Error: '; switch (errorDetails) { - case "base": - errorMsg += "Die Size and Die Count"; + case 'base': + errorMsg += 'Die Size and Die Count'; break; - case "drop": - errorMsg += "Drop (d or dl)"; + case 'drop': + errorMsg += 'Drop (d or dl)'; break; - case "keep": - errorMsg += "Keep (k or kh)"; + case 'keep': + errorMsg += 'Keep (k or kh)'; break; - case "dropHigh": - errorMsg += "Drop Highest (dh)"; + case 'dropHigh': + errorMsg += 'Drop Highest (dh)'; break; - case "keepLow": - errorMsg += "Keep Lowest (kl)"; + case 'keepLow': + errorMsg += 'Keep Lowest (kl)'; break; - case "reroll": - errorMsg += "Reroll (r)"; + case 'reroll': + errorMsg += 'Reroll (r)'; break; - case "critScore": - errorMsg += "Crit Score (cs)"; + case 'critScore': + errorMsg += 'Crit Score (cs)'; break; default: errorMsg += `Unhandled - ${errorDetails}`; break; } - errorMsg += " cannot be zero"; + errorMsg += ' cannot be zero'; break; - case "CritScoreMinGtrMax": - errorMsg = "Formatting Error: CritScore maximum cannot be greater than minimum, check formatting and flip min/max"; + case 'CritScoreMinGtrMax': + errorMsg = 'Formatting Error: CritScore maximum cannot be greater than minimum, check formatting and flip min/max'; break; - case "MaxLoopsExceeded": - errorMsg = "Error: Roll is too complex or reaches infinity"; + case 'MaxLoopsExceeded': + errorMsg = 'Error: Roll is too complex or reaches infinity'; break; - case "UnbalancedParens": - errorMsg = "Formatting Error: At least one of the equations contains unbalanced parenthesis"; + case 'UnbalancedParens': + errorMsg = 'Formatting Error: At least one of the equations contains unbalanced parenthesis'; break; - case "EMDASNotNumber": - errorMsg = "Error: One or more operands is not a number"; + case 'EMDASNotNumber': + errorMsg = 'Error: One or more operands is not a number'; break; - case "ConfWhat": - errorMsg = "Error: Not all values got processed, please report the command used"; + case 'ConfWhat': + errorMsg = 'Error: Not all values got processed, please report the command used'; break; - case "OperatorWhat": - errorMsg = "Error: Something really broke with the Operator, try again"; + case 'OperatorWhat': + errorMsg = 'Error: Something really broke with the Operator, try again'; break; - case "OperandNaN": - errorMsg = "Error: One or more operands reached NaN, check input"; + case 'OperandNaN': + errorMsg = 'Error: One or more operands reached NaN, check input'; break; - case "UndefinedStep": - errorMsg = "Error: Roll became undefined, one or more operands are not a roll or a number, check input"; + case 'UndefinedStep': + errorMsg = 'Error: Roll became undefined, one or more operands are not a roll or a number, check input'; break; default: log(LT.ERROR, `Undangled Error: ${errorName}, ${errorDetails}`); diff --git a/src/solver/rollFormatter.ts b/src/solver/rollFormatter.ts index f39c013..dc2d212 100644 --- a/src/solver/rollFormatter.ts +++ b/src/solver/rollFormatter.ts @@ -1,27 +1,28 @@ import { + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; + LT, +} from '../../deps.ts'; -import { roll } from "./roller.ts"; -import { SolvedStep } from "./solver.d.ts"; +import { roll } from './roller.ts'; +import { SolvedStep } from './solver.d.ts'; // formatRoll(rollConf, maximiseRoll, nominalRoll) returns one SolvedStep // formatRoll handles creating and formatting the completed rolls into the SolvedStep format export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll: boolean): SolvedStep => { let tempTotal = 0; - let tempDetails = "["; + let tempDetails = '['; let tempCrit = false; let tempFail = false; // Generate the roll, passing flags thru const tempRollSet = roll(rollConf, maximiseRoll, nominalRoll); - + // Loop thru all parts of the roll to document everything that was done to create the total roll - tempRollSet.forEach(e => { + tempRollSet.forEach((e) => { log(LT.LOG, `Formatting roll ${rollConf} | ${JSON.stringify(e)}`); - let preFormat = ""; - let postFormat = ""; + let preFormat = ''; + let postFormat = ''; if (!e.dropped && !e.rerolled) { // If the roll was not dropped or rerolled, add it to the stepTotal and flag the critHit/critFail @@ -54,13 +55,13 @@ export const formatRoll = (rollConf: string, maximiseRoll: boolean, nominalRoll: tempDetails += `${preFormat}${e.roll}${postFormat} + `; }); // After the looping is done, remove the extra " + " from the details and cap it with the closing ] - tempDetails = tempDetails.substring(0, (tempDetails.length - 3)); - tempDetails += "]"; + tempDetails = tempDetails.substring(0, tempDetails.length - 3); + tempDetails += ']'; return { total: tempTotal, details: tempDetails, containsCrit: tempCrit, - containsFail: tempFail + containsFail: tempFail, }; }; diff --git a/src/solver/rollUtils.ts b/src/solver/rollUtils.ts index 64888eb..8059f35 100644 --- a/src/solver/rollUtils.ts +++ b/src/solver/rollUtils.ts @@ -1,9 +1,10 @@ import { + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; + LT, +} from '../../deps.ts'; -import { RollSet, ReturnData } from "./solver.d.ts"; +import { ReturnData, RollSet } from './solver.d.ts'; // MAXLOOPS determines how long the bot will attempt a roll // Default is 5000000 (5 million), which results in at most a 10 second delay before the bot calls the roll infinite or too complex @@ -60,7 +61,7 @@ export const escapeCharacters = (str: string, esc: string): string => { for (let i = 0; i < esc.length; i++) { log(LT.LOG, `Escaping character ${esc[i]} | ${str}, ${esc}`); // Create a new regex to look for that char that needs replaced and escape it - const temprgx = new RegExp(`[${esc[i]}]`, "g"); + const temprgx = new RegExp(`[${esc[i]}]`, 'g'); str = str.replace(temprgx, `\\${esc[i]}`); } return str; diff --git a/src/solver/roller.ts b/src/solver/roller.ts index d46eed4..3f42e43 100644 --- a/src/solver/roller.ts +++ b/src/solver/roller.ts @@ -1,10 +1,11 @@ import { + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; + LT, +} from '../../deps.ts'; -import { RollSet } from "./solver.d.ts"; -import { MAXLOOPS, genRoll, compareRolls, compareOrigidx } from "./rollUtils.ts"; +import { RollSet } from './solver.d.ts'; +import { compareOrigidx, compareRolls, genRoll, MAXLOOPS } from './rollUtils.ts'; // roll(rollStr, maximiseRoll, nominalRoll) returns RollSet // roll parses and executes the rollStr, if needed it will also make the roll the maximum or average @@ -19,7 +20,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollStr = rollStr.toLowerCase(); // Split the roll on the die size (and the drop if its there) - const dpts = rollStr.split("d"); + const dpts = rollStr.split('d'); // Initialize the configuration to store the parsed data const rollConf = { @@ -27,47 +28,47 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea dieSize: 0, drop: { on: false, - count: 0 + count: 0, }, keep: { on: false, - count: 0 + count: 0, }, dropHigh: { on: false, - count: 0 + count: 0, }, keepLow: { on: false, - count: 0 + count: 0, }, reroll: { on: false, once: false, - nums: [] + nums: [], }, critScore: { on: false, - range: [] + range: [], }, critFail: { on: false, - range: [] + range: [], }, exploding: { on: false, once: false, - nums: [] - } + nums: [], + }, }; // If the dpts is not long enough, throw error if (dpts.length < 2) { - throw new Error("YouNeedAD"); + throw new Error('YouNeedAD'); } // Fill out the die count, first item will either be an int or empty string, short circuit execution will take care of replacing the empty string with a 1 - const tempDC = (dpts.shift() || "1").replace(/\D/g,''); + const tempDC = (dpts.shift() || '1').replace(/\D/g, ''); rollConf.dieCount = parseInt(tempDC); // Finds the end of the die size/beginnning of the additional options @@ -77,7 +78,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea } // Rejoin all remaining parts - let remains = dpts.join("d"); + let remains = dpts.join('d'); // Get the die size out of the remains and into the rollConf rollConf.dieSize = parseInt(remains.slice(0, afterDieIdx)); remains = remains.slice(afterDieIdx); @@ -88,7 +89,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Finish parsing the roll if (remains.length > 0) { // Determine if the first item is a drop, and if it is, add the d back in - if (remains.search(/\D/) !== 0 || remains.indexOf("l") === 0 || remains.indexOf("h") === 0) { + if (remains.search(/\D/) !== 0 || remains.indexOf('l') === 0 || remains.indexOf('h') === 0) { remains = `d${remains}`; } @@ -113,42 +114,42 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Switch on rule name switch (tSep) { - case "dl": - case "d": + case 'dl': + case 'd': // Configure Drop (Lowest) rollConf.drop.on = true; rollConf.drop.count = tNum; break; - case "kh": - case "k": + case 'kh': + case 'k': // Configure Keep (Highest) rollConf.keep.on = true; rollConf.keep.count = tNum; break; - case "dh": + case 'dh': // Configure Drop (Highest) rollConf.dropHigh.on = true; rollConf.dropHigh.count = tNum; break; - case "kl": + case 'kl': // Configure Keep (Lowest) rollConf.keepLow.on = true; rollConf.keepLow.count = tNum; break; - case "ro": - case "ro=": + case 'ro': + case 'ro=': rollConf.reroll.once = true; // falls through as ro/ro= functions the same as r/r= in this context - case "r": - case "r=": + case 'r': + case 'r=': // Configure Reroll (this can happen multiple times) rollConf.reroll.on = true; rollConf.reroll.nums.push(tNum); break; - case "ro>": + case 'ro>': rollConf.reroll.once = true; // falls through as ro> functions the same as r> in this context - case "r>": + case 'r>': // Configure reroll for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.reroll.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { @@ -156,10 +157,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.reroll.nums.push(i); } break; - case "ro<": + case 'ro<': rollConf.reroll.once = true; // falls through as ro< functions the same as r< in this context - case "r<": + case 'r<': // Configure reroll for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.reroll.on = true; for (let i = 1; i <= tNum; i++) { @@ -167,13 +168,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.reroll.nums.push(i); } break; - case "cs": - case "cs=": + case 'cs': + case 'cs=': // Configure CritScore for one number (this can happen multiple times) rollConf.critScore.on = true; rollConf.critScore.range.push(tNum); break; - case "cs>": + case 'cs>': // Configure CritScore for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.critScore.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { @@ -181,7 +182,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.critScore.range.push(i); } break; - case "cs<": + case 'cs<': // Configure CritScore for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.critScore.on = true; for (let i = 0; i <= tNum; i++) { @@ -189,13 +190,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.critScore.range.push(i); } break; - case "cf": - case "cf=": + case 'cf': + case 'cf=': // Configure CritFail for one number (this can happen multiple times) rollConf.critFail.on = true; rollConf.critFail.range.push(tNum); break; - case "cf>": + case 'cf>': // Configure CritFail for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.critFail.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { @@ -203,7 +204,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.critFail.range.push(i); } break; - case "cf<": + case 'cf<': // Configure CritFail for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.critFail.on = true; for (let i = 0; i <= tNum; i++) { @@ -211,10 +212,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.critFail.range.push(i); } break; - case "!o": + case '!o': rollConf.exploding.once = true; // falls through as !o functions the same as ! in this context - case "!": + case '!': // Configure Exploding rollConf.exploding.on = true; if (afterNumIdx > 0) { @@ -225,18 +226,18 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea afterNumIdx = 1; } break; - case "!o=": + case '!o=': rollConf.exploding.once = true; // falls through as !o= functions the same as != in this context - case "!=": + case '!=': // Configure Exploding (this can happen multiple times) rollConf.exploding.on = true; rollConf.exploding.nums.push(tNum); break; - case "!o>": + case '!o>': rollConf.exploding.once = true; // falls through as !o> functions the same as !> in this context - case "!>": + case '!>': // Configure Exploding for all numbers greater than or equal to tNum (this could happen multiple times, but why) rollConf.exploding.on = true; for (let i = tNum; i <= rollConf.dieSize; i++) { @@ -244,10 +245,10 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rollConf.exploding.nums.push(i); } break; - case "!o<": + case '!o<': rollConf.exploding.once = true; // falls through as !o< functions the same as !< in this context - case "!<": + case '!<': // Configure Exploding for all numbers less than or equal to tNum (this could happen multiple times, but why) rollConf.exploding.on = true; for (let i = 1; i <= tNum; i++) { @@ -266,36 +267,36 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea // Verify the parse, throwing errors for every invalid config if (rollConf.dieCount < 0) { - throw new Error("NoZerosAllowed_base"); + throw new Error('NoZerosAllowed_base'); } if (rollConf.dieCount === 0 || rollConf.dieSize === 0) { - throw new Error("NoZerosAllowed_base"); + throw new Error('NoZerosAllowed_base'); } // Since only one drop or keep option can be active, count how many are active to throw the right error let dkdkCnt = 0; - [rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach(e => { + [rollConf.drop.on, rollConf.keep.on, rollConf.dropHigh.on, rollConf.keepLow.on].forEach((e) => { log(LT.LOG, `Handling roll ${rollStr} | Checking if drop/keep is on ${e}`); if (e) { dkdkCnt++; } }); if (dkdkCnt > 1) { - throw new Error("FormattingError_dk"); + throw new Error('FormattingError_dk'); } if (rollConf.drop.on && rollConf.drop.count === 0) { - throw new Error("NoZerosAllowed_drop"); + throw new Error('NoZerosAllowed_drop'); } if (rollConf.keep.on && rollConf.keep.count === 0) { - throw new Error("NoZerosAllowed_keep"); + throw new Error('NoZerosAllowed_keep'); } if (rollConf.dropHigh.on && rollConf.dropHigh.count === 0) { - throw new Error("NoZerosAllowed_dropHigh"); + throw new Error('NoZerosAllowed_dropHigh'); } if (rollConf.keepLow.on && rollConf.keepLow.count === 0) { - throw new Error("NoZerosAllowed_keepLow"); + throw new Error('NoZerosAllowed_keepLow'); } if (rollConf.reroll.on && rollConf.reroll.nums.indexOf(0) >= 0) { - throw new Error("NoZerosAllowed_reroll"); + throw new Error('NoZerosAllowed_reroll'); } // Roll the roll @@ -331,7 +332,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea rerolled: false, exploding: false, critHit: false, - critFail: false + critFail: false, }; // Begin counting the number of loops to prevent from getting into an infinite loop @@ -342,7 +343,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea log(LT.LOG, `Handling roll ${rollStr} | Initial rolling ${i} of ${JSON.stringify(rollConf)}`); // If loopCount gets too high, stop trying to calculate infinity if (loopCount > MAXLOOPS) { - throw new Error("MaxLoopsExceeded"); + throw new Error('MaxLoopsExceeded'); } // Copy the template to fill out for this iteration @@ -356,13 +357,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (rollConf.critScore.on && rollConf.critScore.range.indexOf(rolling.roll) >= 0) { rolling.critHit = true; } else if (!rollConf.critScore.on) { - rolling.critHit = (rolling.roll === rollConf.dieSize); + rolling.critHit = rolling.roll === rollConf.dieSize; } // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1 if (rollConf.critFail.on && rollConf.critFail.range.indexOf(rolling.roll) >= 0) { rolling.critFail = true; } else if (!rollConf.critFail.on) { - rolling.critFail = (rolling.roll === 1); + rolling.critFail = rolling.roll === 1; } // Push the newly created roll and loop again @@ -376,7 +377,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea log(LT.LOG, `Handling roll ${rollStr} | Handling rerolling and exploding ${JSON.stringify(rollSet[i])}`); // If loopCount gets too high, stop trying to calculate infinity if (loopCount > MAXLOOPS) { - throw new Error("MaxLoopsExceeded"); + throw new Error('MaxLoopsExceeded'); } // If we need to reroll this roll, flag its been replaced and... @@ -393,21 +394,24 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (rollConf.critScore.on && rollConf.critScore.range.indexOf(newRoll.roll) >= 0) { newRoll.critHit = true; } else if (!rollConf.critScore.on) { - newRoll.critHit = (newRoll.roll === rollConf.dieSize); + newRoll.critHit = newRoll.roll === rollConf.dieSize; } // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1 if (rollConf.critFail.on && rollConf.critFail.range.indexOf(newRoll.roll) >= 0) { newRoll.critFail = true; } else if (!rollConf.critFail.on) { - newRoll.critFail = (newRoll.roll === 1); + newRoll.critFail = newRoll.roll === 1; } // Slot this new roll in after the current iteration so it can be processed in the next loop rollSet.splice(i + 1, 0, newRoll); - } else if (rollConf.exploding.on && !rollSet[i].rerolled && (rollConf.exploding.nums.length ? rollConf.exploding.nums.indexOf(rollSet[i].roll) >= 0 : rollSet[i].critHit) && (!rollConf.exploding.once || !rollSet[i].exploding)) { + } else if ( + rollConf.exploding.on && !rollSet[i].rerolled && (rollConf.exploding.nums.length ? rollConf.exploding.nums.indexOf(rollSet[i].roll) >= 0 : rollSet[i].critHit) && + (!rollConf.exploding.once || !rollSet[i].exploding) + ) { // If we have exploding.nums set, use those to determine the exploding range, and make sure if !o is on, make sure we don't repeatedly explode // If it exploded, we keep both, so no flags need to be set - + // Copy the template to fill out for this iteration const newRoll = JSON.parse(JSON.stringify(templateRoll)); // If maximiseRoll is on, set the roll to the dieSize, else if nominalRoll is on, set the roll to the average roll of dieSize, else generate a new random roll @@ -419,13 +423,13 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (rollConf.critScore.on && rollConf.critScore.range.indexOf(newRoll.roll) >= 0) { newRoll.critHit = true; } else if (!rollConf.critScore.on) { - newRoll.critHit = (newRoll.roll === rollConf.dieSize); + newRoll.critHit = newRoll.roll === rollConf.dieSize; } // If critFail arg is on, check if the roll should be a fail, if its off, check if the roll matches 1 if (rollConf.critFail.on && rollConf.critFail.range.indexOf(newRoll.roll) >= 0) { newRoll.critFail = true; } else if (!rollConf.critFail.on) { - newRoll.critFail = (newRoll.roll === 1); + newRoll.critFail = newRoll.roll === 1; } // Slot this new roll in after the current iteration so it can be processed in the next loop @@ -444,7 +448,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea for (let i = 0; i < rollSet.length; i++) { // If loopCount gets too high, stop trying to calculate infinity if (loopCount > MAXLOOPS) { - throw new Error("MaxLoopsExceeded"); + throw new Error('MaxLoopsExceeded'); } log(LT.LOG, `Handling roll ${rollStr} | Setting originalIdx on ${JSON.stringify(rollSet[i])}`); @@ -477,9 +481,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea if (dropCount < 0) { dropCount = 0; } - } - - // For inverted drop and keep, order must be flipped to greatest to least before the simple subtraction can determine how many to drop + } // For inverted drop and keep, order must be flipped to greatest to least before the simple subtraction can determine how many to drop // Protections are in to prevent the dropCount from going below 0 or more than the valid rolls to drop else if (rollConf.dropHigh.on) { rollSet.reverse(); @@ -500,7 +502,7 @@ export const roll = (rollStr: string, maximiseRoll: boolean, nominalRoll: boolea while (dropCount > 0 && i < rollSet.length) { // If loopCount gets too high, stop trying to calculate infinity if (loopCount > MAXLOOPS) { - throw new Error("MaxLoopsExceeded"); + throw new Error('MaxLoopsExceeded'); } log(LT.LOG, `Handling roll ${rollStr} | Dropping dice ${dropCount} ${JSON.stringify(rollSet[i])}`); diff --git a/src/solver/solver.d.ts b/src/solver/solver.d.ts index a088266..3c96f5e 100644 --- a/src/solver/solver.d.ts +++ b/src/solver/solver.d.ts @@ -2,49 +2,49 @@ // RollSet is used to preserve all information about a calculated roll export type RollSet = { - origidx: number, - roll: number, - dropped: boolean, - rerolled: boolean, - exploding: boolean, - critHit: boolean, - critFail: boolean + origidx: number; + roll: number; + dropped: boolean; + rerolled: boolean; + exploding: boolean; + critHit: boolean; + critFail: boolean; }; // SolvedStep is used to preserve information while math is being performed on the roll export type SolvedStep = { - total: number, - details: string, - containsCrit: boolean, - containsFail: boolean + total: number; + details: string; + containsCrit: boolean; + containsFail: boolean; }; // ReturnData is the temporary internal type used before getting turned into SolvedRoll export type ReturnData = { - rollTotal: number, - rollPostFormat: string, - rollDetails: string, - containsCrit: boolean, - containsFail: boolean, - initConfig: string + rollTotal: number; + rollPostFormat: string; + rollDetails: string; + containsCrit: boolean; + containsFail: boolean; + initConfig: string; }; // SolvedRoll is the complete solved and formatted roll, or the error said roll created export type SolvedRoll = { - error: boolean, - errorMsg: string, - errorCode: string, - line1: string, - line2: string, - line3: string + error: boolean; + errorMsg: string; + errorCode: string; + line1: string; + line2: string; + line3: string; }; // CountDetails is the object holding the count data for creating the Count Embed export type CountDetails = { - total: number, - successful: number, - failed: number, - rerolled: number, - dropped: number, - exploded: number + total: number; + successful: number; + failed: number; + rerolled: number; + dropped: number; + exploded: number; }; diff --git a/src/solver/solver.ts b/src/solver/solver.ts index 9b97f9b..6c7eaa3 100644 --- a/src/solver/solver.ts +++ b/src/solver/solver.ts @@ -5,22 +5,23 @@ */ import { + log, // Log4Deno deps - LT, log -} from "../../deps.ts"; + LT, +} from '../../deps.ts'; -import { SolvedStep } from "./solver.d.ts"; +import { SolvedStep } from './solver.d.ts'; // fullSolver(conf, wrapDetails) returns one condensed SolvedStep // fullSolver is a function that recursively solves the full roll and math export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: boolean): SolvedStep => { // Initialize PEMDAS - const signs = ["^", "*", "/", "%", "+", "-"]; + const signs = ['^', '*', '/', '%', '+', '-']; const stepSolve = { total: 0, - details: "", + details: '', containsCrit: false, - containsFail: false + containsFail: false, }; // If entering with a single number, note it now @@ -30,10 +31,10 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: } // Evaluate all parenthesis - while (conf.indexOf("(") > -1) { + while (conf.indexOf('(') > -1) { log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for (`); // Get first open parenthesis - const openParen = conf.indexOf("("); + const openParen = conf.indexOf('('); let closeParen = -1; let nextParen = 0; @@ -41,12 +42,12 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: for (let i = openParen; i < conf.length; i++) { log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Looking for matching ) openIdx: ${openParen} checking: ${i}`); // If we hit an open, add one (this includes the openParen we start with), if we hit a close, subtract one - if (conf[i] === "(") { + if (conf[i] === '(') { nextParen++; - } else if (conf[i] === ")") { + } else if (conf[i] === ')') { nextParen--; } - + // When nextParen reaches 0 again, we will have found the matching closing parenthesis and can safely exit the for loop if (nextParen === 0) { closeParen = i; @@ -56,11 +57,11 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: // Make sure we did find the correct closing paren, if not, error out now if (closeParen === -1 || closeParen < openParen) { - throw new Error("UnbalancedParens"); + throw new Error('UnbalancedParens'); } // Replace the itemes between openParen and closeParen (including the parens) with its solved equilvalent by calling the solver on the items between openParen and closeParen (excluding the parens) - conf.splice(openParen, (closeParen + 1), fullSolver(conf.slice((openParen + 1), closeParen), true)); + conf.splice(openParen, closeParen + 1, fullSolver(conf.slice(openParen + 1, closeParen), true)); // Determing if we need to add in a multiplication sign to handle implicit multiplication (like "(4)2" = 8) // insertedMult flags if there was a multiplication sign inserted before the parens @@ -68,20 +69,20 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: // Check if a number was directly before openParen and slip in the "*" if needed if (((openParen - 1) > -1) && (signs.indexOf(conf[openParen - 1].toString()) === -1)) { insertedMult = true; - conf.splice(openParen, 0, "*"); + conf.splice(openParen, 0, '*'); } // Check if a number is directly after closeParen and slip in the "*" if needed if (!insertedMult && (((openParen + 1) < conf.length) && (signs.indexOf(conf[openParen + 1].toString()) === -1))) { - conf.splice((openParen + 1), 0, "*"); + conf.splice(openParen + 1, 0, '*'); } else if (insertedMult && (((openParen + 2) < conf.length) && (signs.indexOf(conf[openParen + 2].toString()) === -1))) { // insertedMult is utilized here to let us account for an additional item being inserted into the array (the "*" from before openParn) - conf.splice((openParen + 2), 0, "*"); + conf.splice(openParen + 2, 0, '*'); } } // Evaluate all EMMDAS by looping thru each teir of operators (exponential is the higehest teir, addition/subtraction the lowest) - const allCurOps = [["^"], ["*", "/", "%"], ["+", "-"]]; - allCurOps.forEach(curOps => { + const allCurOps = [['^'], ['*', '/', '%'], ['+', '-']]; + allCurOps.forEach((curOps) => { log(LT.LOG, `Evaluating roll ${JSON.stringify(conf)} | Evaluating ${JSON.stringify(curOps)}`); // Iterate thru all operators/operands in the conf for (let i = 0; i < conf.length; i++) { @@ -96,15 +97,15 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: let oper2 = NaN; const subStepSolve = { total: NaN, - details: "", + details: '', containsCrit: false, - containsFail: false + containsFail: false, }; // Flag to prevent infinte loop when dealing with negative numbers (such as [[-1+20]]) let shouldDecrement = true; // If operand1 is a SolvedStep, populate our subStepSolve with its details and crit/fail flags - if (typeof operand1 === "object") { + if (typeof operand1 === 'object') { oper1 = operand1.total; subStepSolve.details = `${operand1.details}\\${conf[i]}`; subStepSolve.containsCrit = operand1.containsCrit; @@ -122,7 +123,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: } // If operand2 is a SolvedStep, populate our subStepSolve with its details without overriding what operand1 filled in - if (typeof operand2 === "object") { + if (typeof operand2 === 'object') { oper2 = operand2.total; subStepSolve.details += operand2.details; subStepSolve.containsCrit = subStepSolve.containsCrit || operand2.containsCrit; @@ -135,42 +136,42 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: // Make sure neither operand is NaN before continuing if (isNaN(oper1) || isNaN(oper2)) { - throw new Error("OperandNaN"); + throw new Error('OperandNaN'); } // Verify a second time that both are numbers before doing math, throwing an error if necessary - if ((typeof oper1 === "number") && (typeof oper2 === "number")) { + if ((typeof oper1 === 'number') && (typeof oper2 === 'number')) { // Finally do the operator on the operands, throw an error if the operator is not found switch (conf[i]) { - case "^": + case '^': subStepSolve.total = Math.pow(oper1, oper2); break; - case "*": + case '*': subStepSolve.total = oper1 * oper2; break; - case "/": + case '/': subStepSolve.total = oper1 / oper2; break; - case "%": + case '%': subStepSolve.total = oper1 % oper2; break; - case "+": + case '+': subStepSolve.total = oper1 + oper2; break; - case "-": + case '-': subStepSolve.total = oper1 - oper2; break; default: - throw new Error("OperatorWhat"); + throw new Error('OperatorWhat'); } } else { - throw new Error("EMDASNotNumber"); + throw new Error('EMDASNotNumber'); } // Determine if we actually did math or just smashed a - sign onto a number if (shouldDecrement) { // Replace the two operands and their operator with our subStepSolve - conf.splice((i - 1), 3, subStepSolve); + conf.splice(i - 1, 3, subStepSolve); // Because we are messing around with the array we are iterating thru, we need to back up one idx to make sure every operator gets processed i--; } else { @@ -184,17 +185,17 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: // If we somehow have more than one item left in conf at this point, something broke, throw an error if (conf.length > 1) { log(LT.LOG, `ConfWHAT? ${JSON.stringify(conf)}`); - throw new Error("ConfWhat"); - } else if (singleNum && (typeof (conf[0]) === "number")) { + throw new Error('ConfWhat'); + } else if (singleNum && (typeof (conf[0]) === 'number')) { // If we are only left with a number, populate the stepSolve with it stepSolve.total = conf[0]; stepSolve.details = conf[0].toString(); } else { // Else fully populate the stepSolve with what was computed - stepSolve.total = (conf[0]).total; - stepSolve.details = (conf[0]).details; - stepSolve.containsCrit = (conf[0]).containsCrit; - stepSolve.containsFail = (conf[0]).containsFail; + stepSolve.total = ( conf[0]).total; + stepSolve.details = ( conf[0]).details; + stepSolve.containsCrit = ( conf[0]).containsCrit; + stepSolve.containsFail = ( conf[0]).containsFail; } // If this was a nested call, add on parens around the details to show what math we've done @@ -204,7 +205,7 @@ export const fullSolver = (conf: (string | number | SolvedStep)[], wrapDetails: // If our total has reached undefined for some reason, error out now if (stepSolve.total === undefined) { - throw new Error("UndefinedStep"); + throw new Error('UndefinedStep'); } return stepSolve; diff --git a/src/utils.ts b/src/utils.ts index 270c4d9..ad7b493 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,8 +6,8 @@ import { // Discordeno deps - sendMessage -} from "../deps.ts"; + sendMessage, +} from '../deps.ts'; // ask(prompt) returns string // ask prompts the user at command line for message @@ -18,7 +18,7 @@ const ask = async (question: string, stdin = Deno.stdin, stdout = Deno.stdout): await stdout.write(new TextEncoder().encode(question)); // Read console's input into answer - const n = await stdin.read(buf); + const n = await stdin.read(buf); const answer = new TextDecoder().decode(buf.subarray(0, n)); return answer.trim(); @@ -31,64 +31,55 @@ const cmdPrompt = async (logChannel: bigint, botName: string): Promise => while (!done) { // Get a command and its args - const fullCmd = await ask("cmd> "); + const fullCmd = await ask('cmd> '); // Split the args off of the command and prep the command - const args = fullCmd.split(" "); + const args = fullCmd.split(' '); const command = args.shift()?.toLowerCase(); // All commands below here // exit or e // Fully closes the bot - if (command === "exit" || command === "e") { + if (command === 'exit' || command === 'e') { console.log(`${botName} Shutting down.\n\nGoodbye.`); done = true; Deno.exit(0); - } - - // stop + } // stop // Closes the CLI only, leaving the bot running truly headless - else if (command === "stop") { + else if (command === 'stop') { console.log(`Closing ${botName} CLI. Bot will continue to run.\n\nGoodbye.`); done = true; - } - - // m [channel] [message] + } // m [channel] [message] // Sends [message] to specified [channel] - else if (command === "m") { + else if (command === 'm') { try { - const channelId = args.shift() || ""; - const message = args.join(" "); + const channelId = args.shift() || ''; + const message = args.join(' '); - sendMessage(BigInt(channelId), message).catch(reason => { + sendMessage(BigInt(channelId), message).catch((reason) => { console.error(reason); }); - } - catch (e) { + } catch (e) { console.error(e); } - } - - // ml [message] + } // ml [message] // Sends a message to the specified log channel - else if (command === "ml") { - const message = args.join(" "); + else if (command === 'ml') { + const message = args.join(' '); - sendMessage(logChannel, message).catch(reason => { + sendMessage(logChannel, message).catch((reason) => { console.error(reason); }); - } - - // help or h + } // help or h // Shows a basic help menu - else if (command === "help" || command === "h") { - console.log(`${botName} CLI Help:\n\nAvailable Commands:\n exit - closes bot\n stop - closes the CLI\n m [ChannelID] [messgae] - sends message to specific ChannelID as the bot\n ml [message] sends a message to the specified botlog\n help - this message`); - } - - // Unhandled commands die here + else if (command === 'help' || command === 'h') { + console.log( + `${botName} CLI Help:\n\nAvailable Commands:\n exit - closes bot\n stop - closes the CLI\n m [ChannelID] [messgae] - sends message to specific ChannelID as the bot\n ml [message] sends a message to the specified botlog\n help - this message`, + ); + } // Unhandled commands die here else { - console.log("undefined command"); + console.log('undefined command'); } } };