diff --git a/db/initialize.ts b/db/initialize.ts index b6175ac..88faf02 100644 --- a/db/initialize.ts +++ b/db/initialize.ts @@ -2,10 +2,11 @@ // DATA WILL BE LOST IF DB ALREADY EXISTS, RUN AT OWN RISK import config from '../config.ts'; -import { dbClient } from '../src/db.ts'; +import dbClient from '../src/db/client.ts'; console.log('Attempting to create DB'); await dbClient.execute(`CREATE SCHEMA IF NOT EXISTS ${config.db.name};`); +console.log('test'); await dbClient.execute(`USE ${config.db.name}`); console.log('DB created'); diff --git a/db/populateDefaults.ts b/db/populateDefaults.ts index 192a625..82b87e3 100644 --- a/db/populateDefaults.ts +++ b/db/populateDefaults.ts @@ -1,28 +1,47 @@ // This file will populate the tables with default values import config from '../config.ts'; -import { dbClient } from '../src/db.ts'; +import dbClient from '../src/db/client.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('Failed to insert into database', e); }); console.log('Inesrtion done'); console.log('Attempting to insert default commands into command_cnt'); -const commands = ['ping', 'rip', 'rollhelp', 'help', 'info', 'version', 'report', 'stats', 'roll', 'emojis', 'api', 'privacy', 'mention', 'audit', 'heatmap', 'rollDecorators', 'opt-out', 'opt-in']; +const commands = [ + 'ping', + 'rip', + 'rollhelp', + 'help', + 'info', + 'version', + 'report', + 'stats', + 'roll', + 'emojis', + 'api', + 'privacy', + 'mention', + 'audit', + 'heatmap', + 'rollDecorators', + 'opt-out', + 'opt-in', +]; for (const command of commands) { - await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [command]).catch((e) => { - console.log(`Failed to insert ${command} into database`, e); - }); + await dbClient.execute('INSERT INTO command_cnt(command) values(?)', [command]).catch((e) => { + console.log(`Failed to insert ${command} into database`, e); + }); } console.log('Insertion done'); console.log('Attempting to insert default hours into roll_time_heatmap'); for (let i = 0; i <= 23; i++) { - await dbClient.execute('INSERT INTO roll_time_heatmap(hour) values(?)', [i]).catch((e) => { - console.log(`Failed to insert hour ${i} into database`, e); - }); + await dbClient.execute('INSERT INTO roll_time_heatmap(hour) values(?)', [i]).catch((e) => { + console.log(`Failed to insert hour ${i} into database`, e); + }); } console.log('Insertion done'); diff --git a/mod.ts b/mod.ts index 9982d70..3bf89c9 100644 --- a/mod.ts +++ b/mod.ts @@ -7,25 +7,26 @@ import config from './config.ts'; import { DEBUG, DEVMODE, LOCALMODE } from './flags.ts'; import { - // Discordeno deps - botId, - cache, - DiscordActivityTypes, - DiscordenoGuild, - DiscordenoMessage, - editBotNickname, - editBotStatus, - initLog, - Intents, - // Log4Deno deps - log, - LT, - // Discordeno deps - sendMessage, - startBot, + // Discordeno deps + botId, + cache, + DiscordActivityTypes, + DiscordenoGuild, + DiscordenoMessage, + editBotNickname, + editBotStatus, + initLog, + Intents, + // Log4Deno deps + log, + LT, + // Discordeno deps + sendMessage, + startBot, } from './deps.ts'; import api from './src/api.ts'; -import { dbClient, ignoreList } from './src/db.ts'; +import dbClient from './src/db/client.ts'; +import { ignoreList } from './src/db/common.ts'; import commands from './src/commands/_index.ts'; import intervals from './src/intervals.ts'; import { successColor, warnColor } from './src/commandUtils.ts'; @@ -36,283 +37,302 @@ initLog('logs', DEBUG); // Start up the Discord Bot startBot({ - token: LOCALMODE ? config.localtoken : config.token, - intents: [Intents.GuildMessages, Intents.DirectMessages, Intents.Guilds], - eventHandlers: { - ready: () => { - log(LT.INFO, `${config.name} Logged in!`); - editBotStatus({ - activities: [{ - name: 'Booting up . . .', - type: DiscordActivityTypes.Game, - createdAt: new Date().getTime(), - }], - status: 'online', - }); + token: LOCALMODE ? config.localtoken : config.token, + intents: [Intents.GuildMessages, Intents.DirectMessages, Intents.Guilds], + eventHandlers: { + ready: () => { + log(LT.INFO, `${config.name} Logged in!`); + editBotStatus({ + activities: [ + { + name: 'Booting up . . .', + type: DiscordActivityTypes.Game, + createdAt: new Date().getTime(), + }, + ], + status: 'online', + }); - // Interval to rotate the status text every 30 seconds to show off more commands - setInterval(async () => { - 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(), - }], - status: 'online', - }); - } catch (e) { - log(LT.ERROR, `Failed to update status: ${JSON.stringify(e)}`); - } - }, 30000); + // Interval to rotate the status text every 30 seconds to show off more commands + setInterval(async () => { + 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(), + }, + ], + status: 'online', + }); + } catch (e) { + log(LT.ERROR, `Failed to update status: ${JSON.stringify(e)}`); + } + }, 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'); - intervals.updateListStatistics(botId, cache.guilds.size + cache.dispatchedGuildIds.size); - }, 86400000); + // 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'); + intervals.updateListStatistics(botId, cache.guilds.size + cache.dispatchedGuildIds.size); + }, 86400000); - // Interval to update hourlyRates every hour - setInterval(() => { - log(LT.LOG, 'Updating all command hourlyRates'); - intervals.updateHourlyRates(); - }, 3600000); + // Interval to update hourlyRates every hour + setInterval(() => { + log(LT.LOG, 'Updating all command hourlyRates'); + intervals.updateHourlyRates(); + }, 3600000); - // Interval to update heatmap.png every hour - setInterval(() => { - log(LT.LOG, 'Updating heatmap.png'); - intervals.updateHeatmapPng(); - }, 3600000); + // Interval to update heatmap.png every hour + setInterval(() => { + log(LT.LOG, 'Updating heatmap.png'); + intervals.updateHeatmapPng(); + }, 3600000); - // 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 + cache.dispatchedGuildIds.size); - intervals.updateHourlyRates(); - intervals.updateHeatmapPng(); - editBotStatus({ - activities: [{ - name: 'Booting Complete', - type: DiscordActivityTypes.Game, - createdAt: new Date().getTime(), - }], - status: 'online', - }); - sendMessage(config.logChannel, { - embeds: [{ - title: `${config.name} is now Online`, - color: successColor, - fields: [ - { - name: 'Version:', - value: `${config.version}`, - inline: true, - }, - ], - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:88', 'Startup', e)); - }, 1000); - }, - guildCreate: (guild: DiscordenoGuild) => { - log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`); - sendMessage(config.logChannel, { - embeds: [{ - title: 'New Guild Joined!', - color: successColor, - fields: [ - { - name: 'Name:', - value: `${guild.name}`, - inline: true, - }, - { - name: 'Id:', - value: `${guild.id}`, - inline: true, - }, - { - name: 'Member Count:', - value: `${guild.memberCount}`, - inline: true, - }, - ], - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:95', 'Join Guild', e)); - }, - guildDelete: (guild: DiscordenoGuild) => { - log(LT.LOG, `Handling leaving guild ${JSON.stringify(guild)}`); - sendMessage(config.logChannel, { - embeds: [{ - title: 'Removed from Guild', - color: warnColor, - fields: [ - { - name: 'Name:', - value: `${guild.name}`, - inline: true, - }, - { - name: 'Id:', - value: `${guild.id}`, - inline: true, - }, - { - name: 'Member Count:', - value: `${guild.memberCount}`, - inline: true, - }, - ], - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:99', 'Leave Guild', e)); - dbClient.execute('DELETE FROM allowed_guilds WHERE guildid = ? AND banned = 0', [guild.id]).catch((e) => utils.commonLoggers.dbError('mod.ts:100', 'delete from', e)); - }, - debug: DEVMODE ? (dmsg) => log(LT.LOG, `Debug Message | ${JSON.stringify(dmsg)}`) : undefined, - messageCreate: (message: DiscordenoMessage) => { - // Ignore all other bots - if (message.isBot) return; + // 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 + cache.dispatchedGuildIds.size); + intervals.updateHourlyRates(); + intervals.updateHeatmapPng(); + editBotStatus({ + activities: [ + { + name: 'Booting Complete', + type: DiscordActivityTypes.Game, + createdAt: new Date().getTime(), + }, + ], + status: 'online', + }); + sendMessage(config.logChannel, { + embeds: [ + { + title: `${config.name} is now Online`, + color: successColor, + fields: [ + { + name: 'Version:', + value: `${config.version}`, + inline: true, + }, + ], + }, + ], + }).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:88', 'Startup', e)); + }, 1000); + }, + guildCreate: (guild: DiscordenoGuild) => { + log(LT.LOG, `Handling joining guild ${JSON.stringify(guild)}`); + sendMessage(config.logChannel, { + embeds: [ + { + title: 'New Guild Joined!', + color: successColor, + fields: [ + { + name: 'Name:', + value: `${guild.name}`, + inline: true, + }, + { + name: 'Id:', + value: `${guild.id}`, + inline: true, + }, + { + name: 'Member Count:', + value: `${guild.memberCount}`, + inline: true, + }, + ], + }, + ], + }).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:95', 'Join Guild', e)); + }, + guildDelete: (guild: DiscordenoGuild) => { + log(LT.LOG, `Handling leaving guild ${JSON.stringify(guild)}`); + sendMessage(config.logChannel, { + embeds: [ + { + title: 'Removed from Guild', + color: warnColor, + fields: [ + { + name: 'Name:', + value: `${guild.name}`, + inline: true, + }, + { + name: 'Id:', + value: `${guild.id}`, + inline: true, + }, + { + name: 'Member Count:', + value: `${guild.memberCount}`, + inline: true, + }, + ], + }, + ], + }).catch((e: Error) => utils.commonLoggers.messageSendError('mod.ts:99', 'Leave Guild', e)); + dbClient + .execute('DELETE FROM allowed_guilds WHERE guildid = ? AND banned = 0', [guild.id]) + .catch((e) => utils.commonLoggers.dbError('mod.ts:100', 'delete from', e)); + }, + debug: DEVMODE ? (dmsg) => log(LT.LOG, `Debug Message | ${JSON.stringify(dmsg)}`) : undefined, + messageCreate: (message: DiscordenoMessage) => { + // Ignore all other bots + if (message.isBot) return; - // Ignore users who requested to be ignored - if (ignoreList.includes(message.authorId) && (!message.content.startsWith(`${config.prefix}opt-in`) || message.guildId !== 0n)) return; + // Ignore users who requested to be ignored + if (ignoreList.includes(message.authorId) && (!message.content.startsWith(`${config.prefix}opt-in`) || message.guildId !== 0n)) return; - // Ignore all messages that are not commands - if (message.content.indexOf(config.prefix) !== 0) { - // Handle @bot messages - if (message.mentionedUserIds[0] === botId && (message.content.trim().startsWith(`<@${botId}>`) || message.content.trim().startsWith(`<@!${botId}>`))) { - commands.handleMentions(message); - } + // Ignore all messages that are not commands + if (message.content.indexOf(config.prefix) !== 0) { + // Handle @bot messages + if (message.mentionedUserIds[0] === botId && (message.content.trim().startsWith(`<@${botId}>`) || message.content.trim().startsWith(`<@!${botId}>`))) { + commands.handleMentions(message); + } - // return as we are done handling this command - return; - } + // return as we are done handling this command + return; + } - log(LT.LOG, `Handling ${config.prefix}command message: ${JSON.stringify(message)}`); + log(LT.LOG, `Handling ${config.prefix}command message: ${JSON.stringify(message)}`); - // Split into standard command + args format - const args = message.content.slice(config.prefix.length).trim().split(/[ \n]+/g); - const command = args.shift()?.toLowerCase(); + // Split into standard command + args format + const args = message.content + .slice(config.prefix.length) + .trim() + .split(/[ \n]+/g); + const command = args.shift()?.toLowerCase(); - // All commands below here + // All commands below here - switch (command) { - case 'opt-out': - case 'ignore-me': - // [[opt-out or [[ignore-me - // Tells the bot to add you to the ignore list. - commands.optOut(message); - break; - case 'opt-in': - // [[opt-in - // Tells the bot to remove you from the ignore list. - commands.optIn(message); - break; - case 'ping': - // [[ping - // Its a ping test, what else do you want. - commands.ping(message); - break; - case 'rip': - case 'memory': - // [[rip [[memory - // Displays a short message I wanted to include - commands.rip(message); - break; - case 'rollhelp': - case 'rh': - case 'hr': - case '??': - // [[rollhelp or [[rh or [[hr or [[?? - // Help command specifically for the roll command - commands.rollHelp(message); - break; - case 'rolldecorators': - case 'rd': - case 'dr': - case '???': - // [[rollDecorators or [[rd or [[dr or [[??? - // Help command specifically for the roll command decorators - commands.rollDecorators(message); - break; - case 'help': - case 'h': - case '?': - // [[help or [[h or [[? - // Help command, prints from help file - commands.help(message); - break; - case 'info': - case 'i': - // [[info or [[i - // Info command, prints short desc on bot and some links - commands.info(message); - break; - case 'privacy': - // [[privacy - // Privacy command, prints short desc on bot's privacy policy - commands.privacy(message); - break; - case 'version': - case 'v': - // [[version or [[v - // Returns version of the bot - commands.version(message); - break; - case 'report': - case 'r': - // [[report or [[r (command that failed) - // Manually report a failed roll - commands.report(message, args); - break; - case 'stats': - case 's': - // [[stats or [[s - // Displays stats on the bot - commands.stats(message); - break; - case 'api': - // [[api arg - // API sub commands - commands.api(message, args); - break; - case 'audit': - // [[audit arg - // Audit sub commands - commands.audit(message, args); - break; - case 'heatmap': - case 'hm': - // [[heatmap or [[hm - // Audit sub commands - commands.heatmap(message); - break; - default: - // Non-standard commands - if (command?.startsWith('xdy')) { - // [[xdydz (aka someone copy pasted the template as a roll) - // Help command specifically for the roll command - commands.rollHelp(message); - } else if (command && (`${command}${args.join('')}`).indexOf(config.postfix) > -1) { - // [[roll]] - // Dice rolling commence! - commands.roll(message, args, command); - } else if (command) { - // [[emoji or [[emojialias - // Check if the unhandled command is an emoji request - commands.emoji(message, command); - } - break; - } - }, - }, + switch (command) { + case 'opt-out': + case 'ignore-me': + // [[opt-out or [[ignore-me + // Tells the bot to add you to the ignore list. + commands.optOut(message); + break; + case 'opt-in': + // [[opt-in + // Tells the bot to remove you from the ignore list. + commands.optIn(message); + break; + case 'ping': + // [[ping + // Its a ping test, what else do you want. + commands.ping(message); + break; + case 'rip': + case 'memory': + // [[rip [[memory + // Displays a short message I wanted to include + commands.rip(message); + break; + case 'rollhelp': + case 'rh': + case 'hr': + case '??': + // [[rollhelp or [[rh or [[hr or [[?? + // Help command specifically for the roll command + commands.rollHelp(message); + break; + case 'rolldecorators': + case 'rd': + case 'dr': + case '???': + // [[rollDecorators or [[rd or [[dr or [[??? + // Help command specifically for the roll command decorators + commands.rollDecorators(message); + break; + case 'help': + case 'h': + case '?': + // [[help or [[h or [[? + // Help command, prints from help file + commands.help(message); + break; + case 'info': + case 'i': + // [[info or [[i + // Info command, prints short desc on bot and some links + commands.info(message); + break; + case 'privacy': + // [[privacy + // Privacy command, prints short desc on bot's privacy policy + commands.privacy(message); + break; + case 'version': + case 'v': + // [[version or [[v + // Returns version of the bot + commands.version(message); + break; + case 'report': + case 'r': + // [[report or [[r (command that failed) + // Manually report a failed roll + commands.report(message, args); + break; + case 'stats': + case 's': + // [[stats or [[s + // Displays stats on the bot + commands.stats(message); + break; + case 'api': + // [[api arg + // API sub commands + commands.api(message, args); + break; + case 'audit': + // [[audit arg + // Audit sub commands + commands.audit(message, args); + break; + case 'heatmap': + case 'hm': + // [[heatmap or [[hm + // Audit sub commands + commands.heatmap(message); + break; + default: + // Non-standard commands + if (command?.startsWith('xdy')) { + // [[xdydz (aka someone copy pasted the template as a roll) + // Help command specifically for the roll command + commands.rollHelp(message); + } else if (command && `${command}${args.join('')}`.indexOf(config.postfix) > -1) { + // [[roll]] + // Dice rolling commence! + commands.roll(message, args, command); + } else if (command) { + // [[emoji or [[emojialias + // Check if the unhandled command is an emoji request + commands.emoji(message, command); + } + break; + } + }, + }, }); // Start up the command prompt for debug usage if (DEBUG) { - utils.cmdPrompt(config.logChannel, config.name); + utils.cmdPrompt(config.logChannel, config.name); } // Start up the API for rolling from third party apps (like excel macros) if (config.api.enable) { - api.start(); + api.start(); } diff --git a/src/api.ts b/src/api.ts index bd0549e..9b01e76 100644 --- a/src/api.ts +++ b/src/api.ts @@ -6,217 +6,219 @@ import config from '../config.ts'; import { - // Log4Deno deps - log, - LT, + // Log4Deno deps + log, + LT, } from '../deps.ts'; -import { dbClient } from './db.ts'; +import dbClient from './db/client.ts'; import endpoints from './endpoints/_index.ts'; import stdResp from './endpoints/stdResponses.ts'; // start() returns nothing // start initializes and runs the entire API for the bot const start = async (): Promise => { - const server = Deno.listen({ port: config.api.port }); - log(LT.INFO, `HTTP api running at: http://localhost:${config.api.port}/`); + const server = Deno.listen({ port: config.api.port }); + log(LT.INFO, `HTTP api running at: http://localhost:${config.api.port}/`); - // rateLimitTime holds all users with the last time they started a rate limit timer - const rateLimitTime = new Map(); - // rateLimitCnt holds the number of times the user has called the api in the current rate limit timer - const rateLimitCnt = new Map(); + // rateLimitTime holds all users with the last time they started a rate limit timer + const rateLimitTime = new Map(); + // rateLimitCnt holds the number of times the user has called the api in the current rate limit timer + const rateLimitCnt = new Map(); - // Catching every request made to the server - for await (const conn of server) { - (async () => { - const httpConn = Deno.serveHttp(conn); - for await (const requestEvent of httpConn) { - const request = requestEvent.request; - log(LT.LOG, `Handling request: ${JSON.stringify(request.headers)} | ${JSON.stringify(request.method)} | ${JSON.stringify(request.url)}`); - // Check if user is authenticated to be using this API - let authenticated = false; - let rateLimited = false; - let updateRateLimitTime = false; - let apiUserid = 0n; - let apiUseridStr = ''; - let apiUserEmail = ''; - let apiUserDelCode = ''; + // Catching every request made to the server + for await (const conn of server) { + (async () => { + const httpConn = Deno.serveHttp(conn); + for await (const requestEvent of httpConn) { + const request = requestEvent.request; + log(LT.LOG, `Handling request: ${JSON.stringify(request.headers)} | ${JSON.stringify(request.method)} | ${JSON.stringify(request.url)}`); + // Check if user is authenticated to be using this API + let authenticated = false; + let rateLimited = false; + let updateRateLimitTime = false; + let apiUserid = 0n; + let apiUseridStr = ''; + let apiUserEmail = ''; + let apiUserDelCode = ''; - // Check the requests 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')]); + // Check the requests 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'), + ]); - // If only one user returned, is not banned, and is currently active, mark as authenticated - if (dbApiQuery.length === 1) { - apiUserid = BigInt(dbApiQuery[0].userid); - apiUserEmail = dbApiQuery[0].email; - apiUserDelCode = dbApiQuery[0].deleteCode; - authenticated = true; + // If only one user returned, is not banned, and is currently active, mark as authenticated + if (dbApiQuery.length === 1) { + apiUserid = BigInt(dbApiQuery[0].userid); + apiUserEmail = dbApiQuery[0].email; + apiUserDelCode = dbApiQuery[0].deleteCode; + authenticated = true; - // Rate limiting inits - apiUseridStr = apiUserid.toString(); - const apiTimeNow = new Date().getTime(); + // Rate limiting inits + apiUseridStr = apiUserid.toString(); + const apiTimeNow = new Date().getTime(); - // Check if user has sent a request recently - if (rateLimitTime.has(apiUseridStr) && (((rateLimitTime.get(apiUseridStr) || 0) + config.api.rateLimitTime) > apiTimeNow)) { - // Get current count - const currentCnt = rateLimitCnt.get(apiUseridStr) || 0; - if (currentCnt < config.api.rateLimitCnt) { - // Limit not yet exceeded, update count - rateLimitCnt.set(apiUseridStr, currentCnt + 1); - } else { - // Limit exceeded, prevent API use - rateLimited = true; - } - } else { - // Update the maps - updateRateLimitTime = true; - rateLimitCnt.set(apiUseridStr, 1); - } - } - } + // Check if user has sent a request recently + if (rateLimitTime.has(apiUseridStr) && (rateLimitTime.get(apiUseridStr) || 0) + config.api.rateLimitTime > apiTimeNow) { + // Get current count + const currentCnt = rateLimitCnt.get(apiUseridStr) || 0; + if (currentCnt < config.api.rateLimitCnt) { + // Limit not yet exceeded, update count + rateLimitCnt.set(apiUseridStr, currentCnt + 1); + } else { + // Limit exceeded, prevent API use + rateLimited = true; + } + } else { + // Update the maps + updateRateLimitTime = true; + rateLimitCnt.set(apiUseridStr, 1); + } + } + } - if (!rateLimited) { - // Get path and query as a string - const [urlPath, tempQ] = request.url.split('?'); - const path = urlPath.split('api')[1]; + if (!rateLimited) { + // Get path and query as a string + const [urlPath, tempQ] = request.url.split('?'); + const path = urlPath.split('api')[1]; - // Turn the query into a map (if it exists) - const query = new Map(); - if (tempQ !== undefined) { - tempQ.split('&').forEach((e: string) => { - log(LT.LOG, `Parsing request query ${request} ${e}`); - const [option, params] = e.split('='); - query.set(option.toLowerCase(), params); - }); - } + // Turn the query into a map (if it exists) + const query = new Map(); + if (tempQ !== undefined) { + tempQ.split('&').forEach((e: string) => { + log(LT.LOG, `Parsing request query ${request} ${e}`); + const [option, params] = e.split('='); + query.set(option.toLowerCase(), params); + }); + } - if (path) { - if (authenticated) { - // Handle the authenticated request - switch (request.method) { - case 'GET': - switch (path.toLowerCase()) { - case '/key': - case '/key/': - endpoints.get.apiKeyAdmin(requestEvent, query, apiUserid); - break; - case '/channel': - case '/channel/': - endpoints.get.apiChannel(requestEvent, query, apiUserid); - break; - case '/roll': - case '/roll/': - endpoints.get.apiRoll(requestEvent, query, apiUserid); - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.NotFound('Auth Get')); - break; - } - break; - case 'POST': - switch (path.toLowerCase()) { - case '/channel/add': - case '/channel/add/': - endpoints.post.apiChannelAdd(requestEvent, query, apiUserid); - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.NotFound('Auth Post')); - break; - } - break; - case 'PUT': - switch (path.toLowerCase()) { - case '/key/ban': - case '/key/ban/': - case '/key/unban': - case '/key/unban/': - case '/key/activate': - case '/key/activate/': - case '/key/deactivate': - case '/key/deactivate/': - endpoints.put.apiKeyManage(requestEvent, query, apiUserid, path); - break; - case '/channel/ban': - case '/channel/ban/': - case '/channel/unban': - case '/channel/unban/': - endpoints.put.apiChannelManageBan(requestEvent, query, apiUserid, path); - break; - case '/channel/activate': - case '/channel/activate/': - case '/channel/deactivate': - case '/channel/deactivate/': - endpoints.put.apiChannelManageActive(requestEvent, query, apiUserid, path); - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.NotFound('Auth Put')); - break; - } - break; - case 'DELETE': - switch (path.toLowerCase()) { - case '/key/delete': - case '/key/delete/': - endpoints.delete.apiKeyDelete(requestEvent, query, apiUserid, apiUserEmail, apiUserDelCode); - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.NotFound('Auth Del')); - break; - } - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.MethodNotAllowed('Auth')); - break; - } + if (path) { + if (authenticated) { + // Handle the authenticated request + switch (request.method) { + case 'GET': + switch (path.toLowerCase()) { + case '/key': + case '/key/': + endpoints.get.apiKeyAdmin(requestEvent, query, apiUserid); + break; + case '/channel': + case '/channel/': + endpoints.get.apiChannel(requestEvent, query, apiUserid); + break; + case '/roll': + case '/roll/': + endpoints.get.apiRoll(requestEvent, query, apiUserid); + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.NotFound('Auth Get')); + break; + } + break; + case 'POST': + switch (path.toLowerCase()) { + case '/channel/add': + case '/channel/add/': + endpoints.post.apiChannelAdd(requestEvent, query, apiUserid); + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.NotFound('Auth Post')); + break; + } + break; + case 'PUT': + switch (path.toLowerCase()) { + case '/key/ban': + case '/key/ban/': + case '/key/unban': + case '/key/unban/': + case '/key/activate': + case '/key/activate/': + case '/key/deactivate': + case '/key/deactivate/': + endpoints.put.apiKeyManage(requestEvent, query, apiUserid, path); + break; + case '/channel/ban': + case '/channel/ban/': + case '/channel/unban': + case '/channel/unban/': + endpoints.put.apiChannelManageBan(requestEvent, query, apiUserid, path); + break; + case '/channel/activate': + case '/channel/activate/': + case '/channel/deactivate': + case '/channel/deactivate/': + endpoints.put.apiChannelManageActive(requestEvent, query, apiUserid, path); + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.NotFound('Auth Put')); + break; + } + break; + case 'DELETE': + switch (path.toLowerCase()) { + case '/key/delete': + case '/key/delete/': + endpoints.delete.apiKeyDelete(requestEvent, query, apiUserid, apiUserEmail, apiUserDelCode); + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.NotFound('Auth Del')); + break; + } + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.MethodNotAllowed('Auth')); + break; + } - // Update rate limit details - if (updateRateLimitTime) { - const apiTimeNow = new Date().getTime(); - rateLimitTime.set(apiUseridStr, apiTimeNow); - } - } else if (!authenticated) { - // Handle the unathenticated request - switch (request.method) { - case 'GET': - switch (path.toLowerCase()) { - case '/key': - case '/key/': - endpoints.get.apiKey(requestEvent, query); - break; - case '/heatmap.png': - endpoints.get.heatmapPng(requestEvent); - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.NotFound('NoAuth Get')); - break; - } - break; - default: - // Alert API user that they messed up - requestEvent.respondWith(stdResp.MethodNotAllowed('NoAuth')); - break; - } - } - } else { - requestEvent.respondWith(stdResp.Forbidden('What are you trying to do?')); - } - } else if (authenticated && rateLimited) { - // Alert API user that they are doing this too often - requestEvent.respondWith(stdResp.TooManyRequests('Slow down, servers are expensive and this bot is free to use.')); - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('Why are you here?')); - } - } - })(); - } + // Update rate limit details + if (updateRateLimitTime) { + const apiTimeNow = new Date().getTime(); + rateLimitTime.set(apiUseridStr, apiTimeNow); + } + } else if (!authenticated) { + // Handle the unathenticated request + switch (request.method) { + case 'GET': + switch (path.toLowerCase()) { + case '/key': + case '/key/': + endpoints.get.apiKey(requestEvent, query); + break; + case '/heatmap.png': + endpoints.get.heatmapPng(requestEvent); + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.NotFound('NoAuth Get')); + break; + } + break; + default: + // Alert API user that they messed up + requestEvent.respondWith(stdResp.MethodNotAllowed('NoAuth')); + break; + } + } + } else { + requestEvent.respondWith(stdResp.Forbidden('What are you trying to do?')); + } + } else if (authenticated && rateLimited) { + // Alert API user that they are doing this too often + requestEvent.respondWith(stdResp.TooManyRequests('Slow down, servers are expensive and this bot is free to use.')); + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('Why are you here?')); + } + } + })(); + } }; export default { start }; diff --git a/src/commands/apiCmd.ts b/src/commands/apiCmd.ts index 88cd481..2b556d2 100644 --- a/src/commands/apiCmd.ts +++ b/src/commands/apiCmd.ts @@ -1,74 +1,84 @@ -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - hasGuildPermissions, + // Discordeno deps + DiscordenoMessage, + hasGuildPermissions, } from '../../deps.ts'; import apiCommands from './apiCmd/_index.ts'; import { failColor } from '../commandUtils.ts'; import utils from '../utils.ts'; export const api = async (message: DiscordenoMessage, args: string[]) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('api')).catch((e) => utils.commonLoggers.dbError('apiCmd.ts:16', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('api')).catch((e) => utils.commonLoggers.dbError('apiCmd.ts:16', 'call sproc INC_CNT on', e)); - // Local apiArg in lowercase - const apiArg = (args[0] || 'help').toLowerCase(); + // Local apiArg in lowercase + 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({ - embeds: [{ - color: failColor, - title: 'API commands are only available in guilds.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('apiCmd.ts:30', message, e)); - return; - } + // Alert users who DM the bot that this command is for guilds only + if (message.guildId === 0n) { + message + .send({ + embeds: [ + { + color: failColor, + title: 'API commands are only available in guilds.', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('apiCmd.ts:30', message, e)); + return; + } - // Makes sure the user is authenticated to run the API command - if (await hasGuildPermissions(message.authorId, message.guildId, ['ADMINISTRATOR'])) { - switch (apiArg) { - case 'help': - case 'h': - // [[api help - // Shows API help details - apiCommands.help(message); - break; - case 'allow': - case 'block': - case 'enable': - case 'disable': - // [[api allow/block - // Lets a guild admin allow or ban API rolls from happening in said guild - apiCommands.allowBlock(message, apiArg); - break; - case 'delete': - // [[api delete - // Lets a guild admin delete their server from the database - apiCommands.deleteGuild(message); - break; - case 'status': - // [[api status - // Lets a guild admin check the status of API rolling in said guild - apiCommands.status(message); - break; - case 'show-warn': - case 'hide-warn': - // [[api show-warn/hide-warn - // Lets a guild admin decide if the API warning should be shown on messages from the API - apiCommands.showHideWarn(message, apiArg); - break; - default: - break; - } - } else { - message.send({ - 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).', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('apiCmd.ts:77', message, e)); - } + // Makes sure the user is authenticated to run the API command + if (await hasGuildPermissions(message.authorId, message.guildId, ['ADMINISTRATOR'])) { + switch (apiArg) { + case 'help': + case 'h': + // [[api help + // Shows API help details + apiCommands.help(message); + break; + case 'allow': + case 'block': + case 'enable': + case 'disable': + // [[api allow/block + // Lets a guild admin allow or ban API rolls from happening in said guild + apiCommands.allowBlock(message, apiArg); + break; + case 'delete': + // [[api delete + // Lets a guild admin delete their server from the database + apiCommands.deleteGuild(message); + break; + case 'status': + // [[api status + // Lets a guild admin check the status of API rolling in said guild + apiCommands.status(message); + break; + case 'show-warn': + case 'hide-warn': + // [[api show-warn/hide-warn + // Lets a guild admin decide if the API warning should be shown on messages from the API + apiCommands.showHideWarn(message, apiArg); + break; + default: + break; + } + } else { + message + .send({ + 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).', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('apiCmd.ts:77', message, e)); + } }; diff --git a/src/commands/apiCmd/allowBlock.ts b/src/commands/apiCmd/allowBlock.ts index 620e07f..162d57e 100644 --- a/src/commands/apiCmd/allowBlock.ts +++ b/src/commands/apiCmd/allowBlock.ts @@ -1,42 +1,52 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../../deps.ts'; import { generateApiFailed, generateApiSuccess } from '../../commandUtils.ts'; import utils from '../../utils.ts'; export const allowBlock = async (message: DiscordenoMessage, apiArg: string) => { - let errorOutInitial = false; - const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { - utils.commonLoggers.dbError('allowBlock.ts:15', 'query', e0); - message.send(generateApiFailed(apiArg)).catch((e: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:16', message, e)); - errorOutInitial = true; - }); - if (errorOutInitial) return; + let errorOutInitial = false; + const guildQuery = await dbClient + .query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]) + .catch((e0) => { + utils.commonLoggers.dbError('allowBlock.ts:15', 'query', e0); + message.send(generateApiFailed(apiArg)).catch((e: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:16', message, e)); + errorOutInitial = true; + }); + if (errorOutInitial) return; - let errorOut = false; - 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) => { - utils.commonLoggers.dbError('allowBlock:26', 'insert into', e0); - message.send(generateApiFailed(apiArg)).catch((e: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:27', message, e)); - errorOut = true; - }, - ); - } 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) => { - utils.commonLoggers.dbError('allowBlock.ts:35', 'update', e0); - message.send(generateApiFailed(apiArg)).catch((e: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:36', message, e)); - errorOut = true; - }, - ); - } - if (errorOut) return; + let errorOut = false; + 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) => { + utils.commonLoggers.dbError('allowBlock:26', 'insert into', e0); + message.send(generateApiFailed(apiArg)).catch((e: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:27', message, e)); + errorOut = true; + }); + } 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) => { + utils.commonLoggers.dbError('allowBlock.ts:35', 'update', e0); + message.send(generateApiFailed(apiArg)).catch((e: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:36', message, e)); + errorOut = true; + }); + } + if (errorOut) 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: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:44', message, e)); + // 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: Error) => utils.commonLoggers.messageSendError('allowBlock.ts:44', message, e)); }; diff --git a/src/commands/apiCmd/deleteGuild.ts b/src/commands/apiCmd/deleteGuild.ts index 3cfcf17..db17754 100644 --- a/src/commands/apiCmd/deleteGuild.ts +++ b/src/commands/apiCmd/deleteGuild.ts @@ -1,31 +1,39 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../../deps.ts'; import { failColor, successColor } from '../../commandUtils.ts'; import utils from '../../utils.ts'; export const deleteGuild = async (message: DiscordenoMessage) => { - let errorOut = false; - await dbClient.execute(`DELETE FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { - utils.commonLoggers.dbError('deleteGuild.ts:15', 'query', e0); - message.send({ - embeds: [{ - color: failColor, - title: 'Failed to delete this guild from the database.', - description: 'If this issue persists, please report this to the developers.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('deleteGuild.ts:22', message, e)); - errorOut = true; - }); - if (errorOut) return; + let errorOut = false; + await dbClient.execute(`DELETE FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { + utils.commonLoggers.dbError('deleteGuild.ts:15', 'query', e0); + message + .send({ + embeds: [ + { + color: failColor, + title: 'Failed to delete this guild from the database.', + description: 'If this issue persists, please report this to the developers.', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('deleteGuild.ts:22', message, e)); + errorOut = true; + }); + if (errorOut) return; - // We won't get here if there's any errors, so we know it has bee successful, so report as such - message.send({ - embeds: [{ - color: successColor, - title: 'This guild\'s API setting has been removed from The Artifier\'s Database.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('deleteGuild.ts:33', message, e)); + // We won't get here if there's any errors, so we know it has bee successful, so report as such + message + .send({ + embeds: [ + { + color: successColor, + title: "This guild's API setting has been removed from The Artifier's Database.", + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('deleteGuild.ts:33', message, e)); }; diff --git a/src/commands/apiCmd/showHideWarn.ts b/src/commands/apiCmd/showHideWarn.ts index 3e9cbbc..3124866 100644 --- a/src/commands/apiCmd/showHideWarn.ts +++ b/src/commands/apiCmd/showHideWarn.ts @@ -1,38 +1,48 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../../deps.ts'; import { generateApiFailed, generateApiSuccess } from '../../commandUtils.ts'; import utils from '../../utils.ts'; export const showHideWarn = async (message: DiscordenoMessage, apiArg: string) => { - let errorOutInitial = false; - const guildQuery = await dbClient.query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { - utils.commonLoggers.dbError('showHideWarn.ts:15', 'query', e0); - message.send(generateApiFailed(`${apiArg} on`)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:16', message, e)); - errorOutInitial = true; - }); - if (errorOutInitial) return; + let errorOutInitial = false; + const guildQuery = await dbClient + .query(`SELECT guildid, channelid FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]) + .catch((e0) => { + utils.commonLoggers.dbError('showHideWarn.ts:15', 'query', e0); + message.send(generateApiFailed(`${apiArg} on`)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:16', message, e)); + errorOutInitial = true; + }); + if (errorOutInitial) return; - let errorOut = false; - 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) => { - utils.commonLoggers.dbError('showHideWarn.ts:25', 'insert inot', e0); - message.send(generateApiFailed(`${apiArg} on`)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:26', message, e)); - errorOut = true; - }); - } 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) => { - utils.commonLoggers.dbError('showHideWarn.ts:32', 'update', e0); - message.send(generateApiFailed(`${apiArg} on`)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:33', message, e)); - errorOut = true; - }); - } - if (errorOut) return; + let errorOut = false; + 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) => { + utils.commonLoggers.dbError('showHideWarn.ts:25', 'insert inot', e0); + message.send(generateApiFailed(`${apiArg} on`)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:26', message, e)); + errorOut = true; + }); + } 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) => { + utils.commonLoggers.dbError('showHideWarn.ts:32', 'update', e0); + message.send(generateApiFailed(`${apiArg} on`)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:33', message, e)); + errorOut = true; + }); + } + if (errorOut) 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)).catch((e: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:40', message, e)); + // 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: Error) => utils.commonLoggers.messageSendError('showHideWarn.ts:40', message, e)); }; diff --git a/src/commands/apiCmd/status.ts b/src/commands/apiCmd/status.ts index 7615983..70147a1 100644 --- a/src/commands/apiCmd/status.ts +++ b/src/commands/apiCmd/status.ts @@ -1,37 +1,43 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../../deps.ts'; import { failColor, generateApiStatus } from '../../commandUtils.ts'; import utils from '../../utils.ts'; export const status = async (message: DiscordenoMessage) => { - // Get status of guild from the db - let errorOut = false; - const guildQuery = await dbClient.query(`SELECT active, banned FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]).catch((e0) => { - utils.commonLoggers.dbError('status.ts:16', 'query', e0); - message.send({ - embeds: [{ - color: failColor, - title: 'Failed to check API rolls status for this guild.', - description: 'If this issue persists, please report this to the developers.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('status.ts:23', message, e)); - errorOut = true; - }); - if (errorOut) return; + // Get status of guild from the db + let errorOut = false; + const guildQuery = await dbClient + .query(`SELECT active, banned FROM allowed_guilds WHERE guildid = ? AND channelid = ?`, [message.guildId, message.channelId]) + .catch((e0) => { + utils.commonLoggers.dbError('status.ts:16', 'query', e0); + message + .send({ + embeds: [ + { + color: failColor, + title: 'Failed to check API rolls status for this guild.', + description: 'If this issue persists, please report this to the developers.', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('status.ts:23', message, e)); + errorOut = true; + }); + if (errorOut) return; - // Check if we got an item back or not - 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: Error) => utils.commonLoggers.messageSendError('status.ts:32', message, e)); - } else { - message.send(generateApiStatus(false, guildQuery[0].active)).catch((e: Error) => utils.commonLoggers.messageSendError('status.ts:34', message, e)); - } - } else { - // Guild is not in DB, therefore they are blocked - message.send(generateApiStatus(false, false)).catch((e: Error) => utils.commonLoggers.messageSendError('status.ts:38', message, e)); - } + // Check if we got an item back or not + 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: Error) => utils.commonLoggers.messageSendError('status.ts:32', message, e)); + } else { + message.send(generateApiStatus(false, guildQuery[0].active)).catch((e: Error) => utils.commonLoggers.messageSendError('status.ts:34', message, e)); + } + } else { + // Guild is not in DB, therefore they are blocked + message.send(generateApiStatus(false, false)).catch((e: Error) => utils.commonLoggers.messageSendError('status.ts:38', message, e)); + } }; diff --git a/src/commands/audit.ts b/src/commands/audit.ts index 306cb96..af27a7b 100644 --- a/src/commands/audit.ts +++ b/src/commands/audit.ts @@ -1,48 +1,53 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import auditCommands from './auditCmd/_index.ts'; import { failColor } from '../commandUtils.ts'; import utils from '../utils.ts'; export const audit = async (message: DiscordenoMessage, args: string[]) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('audit')).catch((e) => utils.commonLoggers.dbError('audit.ts:16', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('audit')).catch((e) => utils.commonLoggers.dbError('audit.ts:16', 'call sproc INC_CNT on', e)); - // Local apiArg in lowercase - const auditArg = (args[0] || 'help').toLowerCase(); + // Local apiArg in lowercase + const auditArg = (args[0] || 'help').toLowerCase(); - // Makes sure the user is authenticated to run the API command - if (message.authorId === config.api.admin) { - switch (auditArg) { - case 'help': - case 'h': - // [[audit help or [[audit h - // Shows API help details - auditCommands.auditHelp(message); - break; - case 'db': - // [[audit db - // Shows current DB table sizes - auditCommands.auditDB(message); - break; - case 'guilds': - // [[audit guilds - // Shows breakdown of guilds and detials on them - auditCommands.auditGuilds(message); - break; - default: - break; - } - } else { - message.send({ - embeds: [{ - color: failColor, - title: `Audit commands are powerful and can only be used by ${config.name}'s owner.`, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('audit.ts:51', message, e)); - } + // Makes sure the user is authenticated to run the API command + if (message.authorId === config.api.admin) { + switch (auditArg) { + case 'help': + case 'h': + // [[audit help or [[audit h + // Shows API help details + auditCommands.auditHelp(message); + break; + case 'db': + // [[audit db + // Shows current DB table sizes + auditCommands.auditDB(message); + break; + case 'guilds': + // [[audit guilds + // Shows breakdown of guilds and detials on them + auditCommands.auditGuilds(message); + break; + default: + break; + } + } else { + message + .send({ + embeds: [ + { + color: failColor, + title: `Audit commands are powerful and can only be used by ${config.name}'s owner.`, + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('audit.ts:51', message, e)); + } }; diff --git a/src/commands/auditCmd/auditDB.ts b/src/commands/auditCmd/auditDB.ts index a4927fd..d58c1b5 100644 --- a/src/commands/auditCmd/auditDB.ts +++ b/src/commands/auditCmd/auditDB.ts @@ -1,42 +1,44 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // Discordeno deps - DiscordenoMessage, - EmbedField, + // Discordeno deps + DiscordenoMessage, + EmbedField, } from '../../../deps.ts'; import { infoColor2 } from '../../commandUtils.ts'; import { compilingStats } from '../../commonEmbeds.ts'; import utils from '../../utils.ts'; export const auditDB = async (message: DiscordenoMessage) => { - try { - const m = await message.send(compilingStats); + try { + const m = await message.send(compilingStats); - // Get DB statistics - const auditQuery = await dbClient.query(`SELECT * FROM db_size;`).catch((e) => utils.commonLoggers.dbError('auditDB.ts:19', 'query', e)); + // Get DB statistics + const auditQuery = await dbClient.query(`SELECT * FROM db_size;`).catch((e) => utils.commonLoggers.dbError('auditDB.ts:19', 'query', e)); - // Turn all tables into embed fields, currently only properly will handle 25 tables, but we'll fix that when artificer gets 26 tables - const embedFields: Array = []; - auditQuery.forEach((row: any) => { - embedFields.push({ - name: `${row.table}`, - value: `**Size:** ${row.size} MB + // Turn all tables into embed fields, currently only properly will handle 25 tables, but we'll fix that when artificer gets 26 tables + const embedFields: Array = []; + auditQuery.forEach((row: any) => { + embedFields.push({ + name: `${row.table}`, + value: `**Size:** ${row.size} MB **Rows:** ${row.rows}`, - inline: true, - }); - }); + inline: true, + }); + }); - // Send the results - m.edit({ - embeds: [{ - color: infoColor2, - title: 'Database Audit', - description: 'Lists all tables with their current size and row count.', - timestamp: new Date().toISOString(), - fields: embedFields, - }], - }).catch((e: Error) => utils.commonLoggers.messageEditError('auditDB.ts:43', message, e)); - } catch (e) { - utils.commonLoggers.messageSendError('auditDB.ts:45', message, e); - } + // Send the results + m.edit({ + embeds: [ + { + color: infoColor2, + title: 'Database Audit', + description: 'Lists all tables with their current size and row count.', + timestamp: new Date().toISOString(), + fields: embedFields, + }, + ], + }).catch((e: Error) => utils.commonLoggers.messageEditError('auditDB.ts:43', message, e)); + } catch (e) { + utils.commonLoggers.messageSendError('auditDB.ts:45', message, e); + } }; diff --git a/src/commands/emoji.ts b/src/commands/emoji.ts index e348fa6..7f338fe 100644 --- a/src/commands/emoji.ts +++ b/src/commands/emoji.ts @@ -1,11 +1,12 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - // Log4Deno deps - log, - LT, + // Discordeno deps + DiscordenoMessage, + // Log4Deno deps + log, + LT, } from '../../deps.ts'; import { EmojiConf } from '../mod.d.ts'; import utils from '../utils.ts'; @@ -13,28 +14,30 @@ import utils from '../utils.ts'; const allEmojiAliases: string[] = []; config.emojis.forEach((emji: EmojiConf) => { - allEmojiAliases.push(...emji.aliases); + allEmojiAliases.push(...emji.aliases); }); export const emoji = (message: DiscordenoMessage, command: string) => { - // shortcut - if (allEmojiAliases.indexOf(command)) { - // Start looping thru the possible emojis - config.emojis.some((emji: EmojiConf) => { - log(LT.LOG, `Checking if command was emoji ${JSON.stringify(emji)}`); - // If a match gets found - if (emji.aliases.indexOf(command || '') > -1) { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('emojis')).catch((e) => utils.commonLoggers.dbError('emojis.ts:28', 'call sproc INC_CNT on', e)); + // shortcut + if (allEmojiAliases.indexOf(command)) { + // Start looping thru the possible emojis + config.emojis.some((emji: EmojiConf) => { + log(LT.LOG, `Checking if command was emoji ${JSON.stringify(emji)}`); + // If a match gets found + if (emji.aliases.indexOf(command || '') > -1) { + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('emojis')).catch((e) => utils.commonLoggers.dbError('emojis.ts:28', 'call sproc INC_CNT on', e)); - // Send the needed emoji - message.send(`<${emji.animated ? 'a' : ''}:${emji.name}:${emji.id}>`).catch((e: Error) => utils.commonLoggers.messageSendError('emoji.ts:33', message, e)); - // And attempt to delete if needed - if (emji.deleteSender) { - message.delete().catch((e: Error) => utils.commonLoggers.messageDeleteError('emoji.ts:36', message, e)); - } - return true; - } - }); - } + // Send the needed emoji + message + .send(`<${emji.animated ? 'a' : ''}:${emji.name}:${emji.id}>`) + .catch((e: Error) => utils.commonLoggers.messageSendError('emoji.ts:33', message, e)); + // And attempt to delete if needed + if (emji.deleteSender) { + message.delete().catch((e: Error) => utils.commonLoggers.messageDeleteError('emoji.ts:36', message, e)); + } + return true; + } + }); + } }; diff --git a/src/commands/handleMentions.ts b/src/commands/handleMentions.ts index 1413fa1..7ef3c38 100644 --- a/src/commands/handleMentions.ts +++ b/src/commands/handleMentions.ts @@ -1,31 +1,38 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - // Log4Deno deps - log, - LT, + // Discordeno deps + DiscordenoMessage, + // Log4Deno deps + log, + LT, } from '../../deps.ts'; import { infoColor1 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const handleMentions = (message: DiscordenoMessage) => { - log(LT.LOG, `Handling @mention message: ${JSON.stringify(message)}`); + log(LT.LOG, `Handling @mention message: ${JSON.stringify(message)}`); - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('mention')).catch((e) => utils.commonLoggers.dbError('handleMentions.ts:17', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('mention')).catch((e) => utils.commonLoggers.dbError('handleMentions.ts:17', 'call sproc INC_CNT on', e)); - message.send({ - 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\`. + message + .send({ + 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\`. Want me to ignore you? Simply run \`${config.prefix}opt-out\` and ${config.name} will no longer read your messages or respond to you.`, - }], - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('handleMentions.ts:30', message, e)); + }, + ], + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('handleMentions.ts:30', message, e)); }; diff --git a/src/commands/heatmap.ts b/src/commands/heatmap.ts index f33abfa..26393b7 100644 --- a/src/commands/heatmap.ts +++ b/src/commands/heatmap.ts @@ -1,7 +1,8 @@ -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import config from '../../config.ts'; import { failColor, infoColor2 } from '../commandUtils.ts'; @@ -9,33 +10,41 @@ import utils from '../utils.ts'; import intervals from '../intervals.ts'; export const heatmap = async (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('heatmap')).catch((e) => utils.commonLoggers.dbError('heatmap.ts:14', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('heatmap')).catch((e) => utils.commonLoggers.dbError('heatmap.ts:14', 'call sproc INC_CNT on', e)); - if (config.api.enable) { - message.send({ - embeds: [{ - title: 'Roll Heatmap', - description: `Over time, this image will show a nice pattern of when rolls are requested the most. + if (config.api.enable) { + message + .send({ + embeds: [ + { + title: 'Roll Heatmap', + description: `Over time, this image will show a nice pattern of when rolls are requested the most. Least Rolls: ${intervals.getMinRollCnt()} Most Rolls: ${intervals.getMaxRollCnt()}`, - footer: { - text: 'Data is shown in US Eastern Time. | This heatmap uses data starting 6/26/2022.', - }, - color: infoColor2, - image: { - url: `${config.api.publicDomain}api/heatmap.png`, - }, - }], - }).catch((e) => utils.commonLoggers.messageSendError('heatmap.ts:21', message, e)); - } else { - message.send({ - embeds: [{ - title: 'Roll Heatmap Disabled', - description: 'This command requires the bot\'s API to be enabled. If you are the host of this bot, check your `config.ts` file to enable it.', - color: failColor, - }], - }).catch((e) => utils.commonLoggers.messageSendError('heatmap.ts:21', message, e)); - } + footer: { + text: 'Data is shown in US Eastern Time. | This heatmap uses data starting 6/26/2022.', + }, + color: infoColor2, + image: { + url: `${config.api.publicDomain}api/heatmap.png`, + }, + }, + ], + }) + .catch((e) => utils.commonLoggers.messageSendError('heatmap.ts:21', message, e)); + } else { + message + .send({ + embeds: [ + { + title: 'Roll Heatmap Disabled', + description: "This command requires the bot's API to be enabled. If you are the host of this bot, check your `config.ts` file to enable it.", + color: failColor, + }, + ], + }) + .catch((e) => utils.commonLoggers.messageSendError('heatmap.ts:21', message, e)); + } }; diff --git a/src/commands/help.ts b/src/commands/help.ts index 19ec8b0..a6bd98b 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,98 +1,102 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor2 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const help = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('help')).catch((e) => utils.commonLoggers.dbError('htlp.ts:15', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('help')).catch((e) => utils.commonLoggers.dbError('htlp.ts:15', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [{ - color: infoColor2, - title: 'The Artificer\'s Available Commands:', - fields: [ - { - name: `\`${config.prefix}?\``, - 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, - }, - { - name: `\`${config.prefix}rollDecorators\` or \`${config.prefix}???\``, - value: `Details on how to use decorators on the roll command`, - 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, - }, - { - name: `\`${config.prefix}ping\``, - 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, - }, - { - name: `\`${config.prefix}privacy\``, - value: 'Prints some information about the Privacy Policy', - inline: true, - }, - { - name: `\`${config.prefix}version\``, - value: 'Prints the bots version', - inline: true, - }, - { - name: `\`${config.prefix}popcat\``, - value: 'Popcat', - inline: true, - }, - { - name: `\`${config.prefix}report [text]\``, - value: 'Report a command that failed to run', - inline: true, - }, - { - name: `\`${config.prefix}stats\``, - value: 'Statistics on the bot', - inline: true, - }, - { - name: `\`${config.prefix}heatmap\``, - value: 'Heatmap of when the roll command is run the most', - inline: true, - }, - { - name: `\`${config.prefix}opt-out\` or \`${config.prefix}ignore-me\``, - value: 'Adds you to an ignore list so the bot will never respond to you', - inline: true, - }, - { - name: `\`${config.prefix}opt-in\` **Available via DM ONLY**`, - value: 'Removes you from the ignore list', - 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, - }, - ], - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('help.ts:82', message, e)); + message + .send({ + embeds: [ + { + color: infoColor2, + title: "The Artificer's Available Commands:", + fields: [ + { + name: `\`${config.prefix}?\``, + 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, + }, + { + name: `\`${config.prefix}rollDecorators\` or \`${config.prefix}???\``, + value: `Details on how to use decorators on the roll command`, + 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, + }, + { + name: `\`${config.prefix}ping\``, + 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, + }, + { + name: `\`${config.prefix}privacy\``, + value: 'Prints some information about the Privacy Policy', + inline: true, + }, + { + name: `\`${config.prefix}version\``, + value: 'Prints the bots version', + inline: true, + }, + { + name: `\`${config.prefix}popcat\``, + value: 'Popcat', + inline: true, + }, + { + name: `\`${config.prefix}report [text]\``, + value: 'Report a command that failed to run', + inline: true, + }, + { + name: `\`${config.prefix}stats\``, + value: 'Statistics on the bot', + inline: true, + }, + { + name: `\`${config.prefix}heatmap\``, + value: 'Heatmap of when the roll command is run the most', + inline: true, + }, + { + name: `\`${config.prefix}opt-out\` or \`${config.prefix}ignore-me\``, + value: 'Adds you to an ignore list so the bot will never respond to you', + inline: true, + }, + { + name: `\`${config.prefix}opt-in\` **Available via DM ONLY**`, + value: 'Removes you from the ignore list', + 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, + }, + ], + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('help.ts:82', message, e)); }; diff --git a/src/commands/info.ts b/src/commands/info.ts index f723cc4..c66dcf2 100644 --- a/src/commands/info.ts +++ b/src/commands/info.ts @@ -1,24 +1,29 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor2 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const info = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('info')).catch((e) => utils.commonLoggers.dbError('info.ts:12', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('info')).catch((e) => utils.commonLoggers.dbError('info.ts:12', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [{ - color: infoColor2, - title: `${config.name}, a Discord bot that specializing in rolling dice and calculating math`, - description: `${config.name} is developed by Ean AKA Burn_E99. + message + .send({ + embeds: [ + { + color: infoColor2, + title: `${config.name}, a Discord bot that specializing in rolling dice and calculating math`, + description: `${config.name} is developed by Ean AKA Burn_E99. Additional information can be found on my website [here](https://discord.burne99.com/TheArtificer/). Want to check out my source code? Check it out [here](https://github.com/Burn-E99/TheArtificer). Need help with this bot? Join my support server [here](https://discord.gg/peHASXMZYv).`, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('info.ts:23', message, e)); + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('info.ts:23', message, e)); }; diff --git a/src/commands/optIn.ts b/src/commands/optIn.ts index cf9429c..bc270fa 100644 --- a/src/commands/optIn.ts +++ b/src/commands/optIn.ts @@ -1,39 +1,48 @@ import config from '../../config.ts'; -import { dbClient, ignoreList, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries, ignoreList } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { failColor, successColor } from '../commandUtils.ts'; import utils from '../utils.ts'; export const optIn = async (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('opt-out')).catch((e) => utils.commonLoggers.dbError('optIn.ts:11', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('opt-out')).catch((e) => utils.commonLoggers.dbError('optIn.ts:11', 'call sproc INC_CNT on', e)); - const idIdx = ignoreList.indexOf(message.authorId); - if (idIdx !== -1) { - try { - ignoreList.splice(idIdx, 1); - await dbClient.execute('DELETE FROM ignore_list WHERE userid = ?', [message.authorId]); + const idIdx = ignoreList.indexOf(message.authorId); + if (idIdx !== -1) { + try { + ignoreList.splice(idIdx, 1); + await dbClient.execute('DELETE FROM ignore_list WHERE userid = ?', [message.authorId]); - message.reply({ - embeds: [{ - color: successColor, - title: `${config.name} will now respond to you again.`, - description: `If you want ${config.name} to ignore to you again, please run the following command: + message + .reply({ + embeds: [ + { + color: successColor, + title: `${config.name} will now respond to you again.`, + description: `If you want ${config.name} to ignore to you again, please run the following command: \`${config.prefix}opt-out\``, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('optIn.ts:27', message, e)); - } catch (err) { - message.reply({ - embeds: [{ - color: failColor, - title: 'Opt-In failed', - description: 'Please try the command again. If the issue persists, please join the support server, linked in my About Me section.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('optIn.ts:27', message, e)); - } - } + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('optIn.ts:27', message, e)); + } catch (err) { + message + .reply({ + embeds: [ + { + color: failColor, + title: 'Opt-In failed', + description: 'Please try the command again. If the issue persists, please join the support server, linked in my About Me section.', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('optIn.ts:27', message, e)); + } + } }; diff --git a/src/commands/optOut.ts b/src/commands/optOut.ts index 0d3164f..1a39375 100644 --- a/src/commands/optOut.ts +++ b/src/commands/optOut.ts @@ -1,36 +1,45 @@ import config from '../../config.ts'; -import { dbClient, ignoreList, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries, ignoreList } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { failColor, successColor } from '../commandUtils.ts'; import utils from '../utils.ts'; export const optOut = async (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('opt-out')).catch((e) => utils.commonLoggers.dbError('optOut.ts:11', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('opt-out')).catch((e) => utils.commonLoggers.dbError('optOut.ts:11', 'call sproc INC_CNT on', e)); - try { - ignoreList.push(message.authorId); - await dbClient.execute('INSERT INTO ignore_list(userid) values(?)', [message.authorId]); + try { + ignoreList.push(message.authorId); + await dbClient.execute('INSERT INTO ignore_list(userid) values(?)', [message.authorId]); - message.reply({ - embeds: [{ - color: successColor, - title: `${config.name} will no longer respond to you.`, - description: `If you want ${config.name} to respond to you again, please DM ${config.name} the following command: + message + .reply({ + embeds: [ + { + color: successColor, + title: `${config.name} will no longer respond to you.`, + description: `If you want ${config.name} to respond to you again, please DM ${config.name} the following command: \`${config.prefix}opt-in\``, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('optOut.ts:25', message, e)); - } catch (err) { - message.reply({ - embeds: [{ - color: failColor, - title: 'Opt-Out failed', - description: `Please try the command again. If the issue persists, please report this using the \`${config.prefix}report opt-out failed\` command.`, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('optOut.ts:33', message, e)); - } + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('optOut.ts:25', message, e)); + } catch (err) { + message + .reply({ + embeds: [ + { + color: failColor, + title: 'Opt-Out failed', + description: `Please try the command again. If the issue persists, please report this using the \`${config.prefix}report opt-out failed\` command.`, + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('optOut.ts:33', message, e)); + } }; diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 39cf5d2..67d1890 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,20 +1,21 @@ -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { generatePing } from '../commandUtils.ts'; import utils from '../utils.ts'; export const ping = async (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('ping')).catch((e) => utils.commonLoggers.dbError('ping.ts:14', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('ping')).catch((e) => utils.commonLoggers.dbError('ping.ts:14', 'call sproc INC_CNT on', e)); - // Calculates ping between sending a message and editing it, giving a nice round-trip latency. - try { - const m = await message.send(generatePing(-1)); - m.edit(generatePing(m.timestamp - message.timestamp)); - } catch (e) { - utils.commonLoggers.messageSendError('ping.ts:23', message, e); - } + // Calculates ping between sending a message and editing it, giving a nice round-trip latency. + try { + const m = await message.send(generatePing(-1)); + m.edit(generatePing(m.timestamp - message.timestamp)); + } catch (e) { + utils.commonLoggers.messageSendError('ping.ts:23', message, e); + } }; diff --git a/src/commands/privacy.ts b/src/commands/privacy.ts index e5b6d66..9d3806a 100644 --- a/src/commands/privacy.ts +++ b/src/commands/privacy.ts @@ -1,31 +1,37 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor1 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const privacy = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('privacy')).catch((e) => utils.commonLoggers.dbError('privacy.ts:15', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('privacy')).catch((e) => utils.commonLoggers.dbError('privacy.ts:15', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [{ - color: infoColor1, - 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. + message + .send({ + embeds: [ + { + color: infoColor1, + 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. 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). Want me to ignore you? Simply run \`${config.prefix}opt-out\` and ${config.name} will no longer read your messages or respond to you.`, - }], - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('privacy.ts:33', message, e)); + }, + ], + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('privacy.ts:33', message, e)); }; diff --git a/src/commands/report.ts b/src/commands/report.ts index 4fc39e9..d70541f 100644 --- a/src/commands/report.ts +++ b/src/commands/report.ts @@ -1,34 +1,43 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - // Discordeno deps - sendMessage, + // Discordeno deps + DiscordenoMessage, + // Discordeno deps + sendMessage, } from '../../deps.ts'; import { failColor, generateReport, successColor } from '../commandUtils.ts'; import utils from '../utils.ts'; export const report = (message: DiscordenoMessage, args: string[]) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('report')).catch((e) => utils.commonLoggers.dbError('report.ts:17', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('report')).catch((e) => utils.commonLoggers.dbError('report.ts:17', 'call sproc INC_CNT on', e)); - if (args.join(' ')) { - sendMessage(config.reportChannel, generateReport(args.join(' '))).catch((e: Error) => utils.commonLoggers.messageSendError('report.ts:22', message, e)); - message.send({ - 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).`, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('report.ts:29', message, e)); - } else { - message.send({ - 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.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('report.ts:37', message, e)); - } + if (args.join(' ')) { + sendMessage(config.reportChannel, generateReport(args.join(' '))).catch((e: Error) => utils.commonLoggers.messageSendError('report.ts:22', message, e)); + message + .send({ + 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).`, + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('report.ts:29', message, e)); + } else { + message + .send({ + 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.', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('report.ts:37', message, e)); + } }; diff --git a/src/commands/rip.ts b/src/commands/rip.ts index 5f25759..e2c88f2 100644 --- a/src/commands/rip.ts +++ b/src/commands/rip.ts @@ -1,22 +1,27 @@ -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor2 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const rip = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('rip')).catch((e) => utils.commonLoggers.dbError('rip.ts:14', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('rip')).catch((e) => utils.commonLoggers.dbError('rip.ts:14', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [{ - color: infoColor2, - title: 'The Artificer was built in memory of my Grandmother, Babka', - description: `With much love, Ean + message + .send({ + embeds: [ + { + color: infoColor2, + title: 'The Artificer was built in memory of my Grandmother, Babka', + description: `With much love, Ean December 21, 2020`, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('rip.ts:26', message, e)); + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('rip.ts:26', message, e)); }; diff --git a/src/commands/roll.ts b/src/commands/roll.ts index e04eb97..98fa4aa 100644 --- a/src/commands/roll.ts +++ b/src/commands/roll.ts @@ -1,12 +1,13 @@ import config from '../../config.ts'; import { DEVMODE } from '../../flags.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - // Log4Deno deps - log, - LT, + // Discordeno deps + DiscordenoMessage, + // Log4Deno deps + log, + LT, } from '../../deps.ts'; import { rollingEmbed, warnColor } from '../commandUtils.ts'; import rollFuncs from './roll/_index.ts'; @@ -15,49 +16,51 @@ import { QueuedRoll } from '../mod.d.ts'; import utils from '../utils.ts'; export const roll = async (message: DiscordenoMessage, args: string[], command: string) => { - // Light telemetry to see how many times a command is being run - const currDateTime = new Date(); - dbClient.execute(queries.callIncCnt('roll')).catch((e) => utils.commonLoggers.dbError('roll.ts:20', 'call sproc INC_CNT on', e)); - dbClient.execute(queries.callIncHeatmap(currDateTime)).catch((e) => utils.commonLoggers.dbError('roll.ts:21', 'update', e)); + // Light telemetry to see how many times a command is being run + const currDateTime = new Date(); + dbClient.execute(queries.callIncCnt('roll')).catch((e) => utils.commonLoggers.dbError('roll.ts:20', 'call sproc INC_CNT on', e)); + dbClient.execute(queries.callIncHeatmap(currDateTime)).catch((e) => utils.commonLoggers.dbError('roll.ts:21', 'update', e)); - // If DEVMODE is on, only allow this command to be used in the devServer - if (DEVMODE && message.guildId !== config.devServer) { - message.send({ - embeds: [{ - color: warnColor, - title: 'Command is in development, please try again later.', - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('roll.ts:30', message, e)); - return; - } + // If DEVMODE is on, only allow this command to be used in the devServer + if (DEVMODE && message.guildId !== config.devServer) { + message + .send({ + embeds: [ + { + color: warnColor, + title: 'Command is in development, please try again later.', + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('roll.ts:30', message, e)); + return; + } - // 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(' ')}`; + // 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 m = await message.reply(rollingEmbed); + const m = await message.reply(rollingEmbed); - // Get modifiers from command - const modifiers = rollFuncs.getModifiers(m, args, command, originalCommand); + // Get modifiers from command + const modifiers = rollFuncs.getModifiers(m, args, command, originalCommand); - // Return early if the modifiers were invalid - if (!modifiers.valid) { - return; - } + // Return early if the modifiers were invalid + if (!modifiers.valid) { + return; + } - // 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 = message.content.substring(2); + // 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 = message.content.substring(2); - queueRoll( - { - apiRoll: false, - dd: { m, message }, - rollCmd, - modifiers, - originalCommand, - }, - ); - } catch (e) { - log(LT.ERROR, `Undandled Error: ${JSON.stringify(e)}`); - } + queueRoll({ + apiRoll: false, + dd: { m, message }, + rollCmd, + modifiers, + originalCommand, + }); + } catch (e) { + log(LT.ERROR, `Undandled Error: ${JSON.stringify(e)}`); + } }; diff --git a/src/commands/roll/getModifiers.ts b/src/commands/roll/getModifiers.ts index 5edd86e..d310dde 100644 --- a/src/commands/roll/getModifiers.ts +++ b/src/commands/roll/getModifiers.ts @@ -1,117 +1,130 @@ import config from '../../../config.ts'; import { DEVMODE } from '../../../flags.ts'; -import { dbClient, queries } from '../../db.ts'; +import dbClient from '../../db/client.ts'; +import { queries } from '../../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - // Log4Deno deps - log, - LT, + // Discordeno deps + DiscordenoMessage, + // Log4Deno deps + log, + LT, } from '../../../deps.ts'; import { generateRollError } from '../../commandUtils.ts'; import { RollModifiers } from '../../mod.d.ts'; import utils from '../../utils.ts'; export const getModifiers = (m: DiscordenoMessage, args: string[], command: string, originalCommand: string): RollModifiers => { - const errorType = 'Modifiers invalid:'; - const modifiers: RollModifiers = { - noDetails: false, - superNoDetails: false, - spoiler: '', - maxRoll: false, - nominalRoll: false, - gmRoll: false, - gms: [], - order: '', - valid: false, - count: false, - apiWarn: '', - }; + const errorType = 'Modifiers invalid:'; + const modifiers: RollModifiers = { + noDetails: false, + superNoDetails: false, + spoiler: '', + maxRoll: false, + nominalRoll: false, + gmRoll: false, + gms: [], + order: '', + valid: false, + count: false, + apiWarn: '', + }; - // 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}`); - let defaultCase = false; - switch (args[i].toLowerCase()) { - case '-c': - modifiers.count = true; - break; - case '-nd': - modifiers.noDetails = true; - break; - case '-snd': - modifiers.superNoDetails = true; - break; - case '-s': - modifiers.spoiler = '||'; - break; - case '-m': - modifiers.maxRoll = true; - break; - case '-n': - modifiers.nominalRoll = true; - break; - case '-gm': - modifiers.gmRoll = true; + // 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}`); + let defaultCase = false; + switch (args[i].toLowerCase()) { + case '-c': + modifiers.count = true; + break; + case '-nd': + modifiers.noDetails = true; + break; + case '-snd': + modifiers.superNoDetails = true; + break; + case '-s': + modifiers.spoiler = '||'; + break; + case '-m': + modifiers.maxRoll = true; + break; + case '-n': + modifiers.nominalRoll = true; + break; + 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('<@')) { - 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); - } - 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')).catch((e) => utils.commonLoggers.messageEditError('getModifiers.ts:66', m, e)); + // -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('<@')) { + 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); + } + 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')).catch((e) => + utils.commonLoggers.messageEditError('getModifiers.ts:66', m, e) + ); - 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) => utils.commonLoggers.dbError('getModifiers.ts:72', 'insert into', e)); - } - return modifiers; - } - break; - case '-o': - // Shift the -o out of the array so the next item is the direction - args.splice(i, 1); + 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) => utils.commonLoggers.dbError('getModifiers.ts:72', 'insert into', e)); + } + return modifiers; + } + break; + 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 -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')).catch((e) => utils.commonLoggers.messageEditError('getModifiers.ts:81', m, e)); + 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')).catch((e) => + utils.commonLoggers.messageEditError('getModifiers.ts:81', m, e) + ); - 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) => utils.commonLoggers.dbError('getModifiers.ts:89', 'insert into', e)); - } - return modifiers; - } + 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) => utils.commonLoggers.dbError('getModifiers.ts:89', 'insert into', e)); + } + return modifiers; + } - modifiers.order = args[i].toLowerCase()[0]; - break; - default: - // Default case should not mess with the array - defaultCase = true; - break; - } + modifiers.order = args[i].toLowerCase()[0]; + break; + default: + // Default case should not mess with the array + defaultCase = true; + break; + } - if (!defaultCase) { - args.splice(i, 1); - i--; - } - } + if (!defaultCase) { + args.splice(i, 1); + i--; + } + } - // 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')).catch((e) => utils.commonLoggers.messageEditError('getModifiers.ts:106', m, e)); + // 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')).catch((e) => + utils.commonLoggers.messageEditError('getModifiers.ts:106', m, e) + ); - 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) => utils.commonLoggers.dbError('getModifiers.ts:120', 'insert into', e)); - } - return modifiers; - } + 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) => utils.commonLoggers.dbError('getModifiers.ts:120', 'insert into', e)); + } + return modifiers; + } - modifiers.valid = true; - return modifiers; + modifiers.valid = true; + return modifiers; }; diff --git a/src/commands/rollDecorators.ts b/src/commands/rollDecorators.ts index a1dfb25..d77b35a 100644 --- a/src/commands/rollDecorators.ts +++ b/src/commands/rollDecorators.ts @@ -1,71 +1,75 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor2 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const rollDecorators = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('rollDecorators')).catch((e) => utils.commonLoggers.dbError('rollHelp.ts:15', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('rollDecorators')).catch((e) => utils.commonLoggers.dbError('rollHelp.ts:15', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [ - { - color: infoColor2, - title: 'Roll Command Decorators:', - description: `This command also has some useful decorators that can used. These decorators simply need to be placed after all rolls in the message. + message + .send({ + embeds: [ + { + color: infoColor2, + title: 'Roll Command Decorators:', + description: `This command also has some useful decorators that can used. These decorators simply need to be placed after all rolls in the message. Examples: \`${config.prefix}d20${config.postfix} -nd\`, \`${config.prefix}d20${config.postfix} -nd -s\`, \`${config.prefix}d20${config.postfix} ${config.prefix}d20${config.postfix} ${config.prefix}d20${config.postfix} -o a\``, - fields: [ - { - name: '`-nd` - No Details', - value: 'Suppresses all details of the requested roll', - inline: true, - }, - { - name: '`-snd` - Super No Details', - value: 'Suppresses all details of the requested roll and hides no details message', - inline: true, - }, - { - name: '`-s` - Spoiler', - value: 'Spoilers all details of the requested roll', - inline: true, - }, - { - name: '`-m` - Maximize Roll', - value: 'Rolls the theoretical maximum roll, cannot be used with -n', - inline: true, - }, - { - name: '`-n` - Nominal Roll', - value: 'Rolls the theoretical nominal roll, cannot be used with -m', - inline: true, - }, - { - name: '`-gm @user1 @user2 @usern` - GM Roll', - value: 'Rolls the requested roll in GM mode, suppressing all publicly shown results and details and sending the results directly to the specified GMs', - inline: true, - }, - { - name: '`-c` - Count Rolls', - value: 'Shows the Count Embed, containing the count of successful rolls, failed rolls, rerolls, drops, and explosions', - inline: true, - }, - { - name: '`-o [direction]` - Order Roll', - value: `Rolls the requested roll and orders the results in the requested direction + fields: [ + { + name: '`-nd` - No Details', + value: 'Suppresses all details of the requested roll', + inline: true, + }, + { + name: '`-snd` - Super No Details', + value: 'Suppresses all details of the requested roll and hides no details message', + inline: true, + }, + { + name: '`-s` - Spoiler', + value: 'Spoilers all details of the requested roll', + inline: true, + }, + { + name: '`-m` - Maximize Roll', + value: 'Rolls the theoretical maximum roll, cannot be used with -n', + inline: true, + }, + { + name: '`-n` - Nominal Roll', + value: 'Rolls the theoretical nominal roll, cannot be used with -m', + inline: true, + }, + { + name: '`-gm @user1 @user2 @usern` - GM Roll', + value: + 'Rolls the requested roll in GM mode, suppressing all publicly shown results and details and sending the results directly to the specified GMs', + inline: true, + }, + { + name: '`-c` - Count Rolls', + value: 'Shows the Count Embed, containing the count of successful rolls, failed rolls, rerolls, drops, and explosions', + inline: true, + }, + { + name: '`-o [direction]` - Order Roll', + value: `Rolls the requested roll and orders the results in the requested direction Available directions: \`a\` - Ascending (least to greatest) \`d\` - Descending (greatest to least)`, - inline: true, - }, - ], - }, - ], - }).catch((e: Error) => utils.commonLoggers.messageSendError('rollHelp.ts:247', message, e)); + inline: true, + }, + ], + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('rollHelp.ts:247', message, e)); }; diff --git a/src/commands/rollHelp.ts b/src/commands/rollHelp.ts index 23b6802..0d56380 100644 --- a/src/commands/rollHelp.ts +++ b/src/commands/rollHelp.ts @@ -1,262 +1,275 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor1, infoColor2, successColor } from '../commandUtils.ts'; import utils from '../utils.ts'; export const rollHelp = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('rollhelp')).catch((e) => utils.commonLoggers.dbError('rollHelp.ts:15', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('rollhelp')).catch((e) => utils.commonLoggers.dbError('rollHelp.ts:15', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [ - { - color: infoColor1, - 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. + message + .send({ + embeds: [ + { + color: infoColor1, + 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. The Artificer supports most of the [Roll20 formatting](https://artificer.eanm.dev/roll20). More details and examples can be found [here](https://artificer.eanm.dev/roll20). Run \`[[???\` or \`[[rollDecorators\` for details on the roll decorators.`, - }, - { - color: infoColor2, - title: 'Roll20 Dice Options:', - 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 + }, + { + color: infoColor2, + title: 'Roll20 Dice Options:', + 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 Additionally, replace \`x\` with \`F\` to roll Fate dice`, - 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: '`!p` [Optional]', - value: 'Penetrating Explosion, rolls one `dy` for each crit success, but subtracts one from each resulting explosion', - inline: true, - }, - { - name: '`!!` [Optional]', - value: 'Compounding Explosion, rolls one `dy` for each crit success, but adds the resulting explosion to the die that caused this explosion', - 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, - }, - ], - }, - { - color: infoColor2, - fields: [ - { - 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: 'Penetrating Explosion on `u` and greater, rolls one `dy` for each die that lands on `u` or greater, but subtracts one from each resulting explosion', - inline: true, - }, - { - name: '`!pu` [Optional]', - value: 'Compounding Explosion on `u` and greater, rolls one `dy` for each die that lands on `u` or greater, but adds the resulting explosion to the die that caused this explosion', - 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: '`!p` [Optional]', + value: 'Penetrating Explosion, rolls one `dy` for each crit success, but subtracts one from each resulting explosion', + inline: true, + }, + { + name: '`!!` [Optional]', + value: 'Compounding Explosion, rolls one `dy` for each crit success, but adds the resulting explosion to the die that caused this explosion', + 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, + }, + ], + }, + { + color: infoColor2, + fields: [ + { + 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: + 'Penetrating Explosion on `u` and greater, rolls one `dy` for each die that lands on `u` or greater, but subtracts one from each resulting explosion', + inline: true, + }, + { + name: '`!pu` [Optional]', + value: + 'Compounding Explosion on `u` and greater, rolls one `dy` for each die that lands on `u` or greater, but adds the resulting explosion to the die that caused this explosion', + inline: true, + }, + { + name: '`!! utils.commonLoggers.messageSendError('rollHelp.ts:247', message, e)); + inline: true, + }, + ], + }, + { + color: successColor, + title: 'Results Formatting:', + description: 'The results have some formatting applied on them to provide details on what happened during this roll.', + fields: [ + { + name: 'Bold', + value: 'Critical successes will be **bolded**.', + inline: true, + }, + { + name: 'Underline', + value: 'Critical fails will be __underlined__.', + inline: true, + }, + { + name: 'Strikethrough', + value: 'Rolls that were dropped or rerolled ~~crossed out~~.', + inline: true, + }, + { + name: 'Exclamation mark (`!`)', + value: 'Rolls that were caused by an explosion have an exclamation mark (`!`) after them.', + inline: true, + }, + ], + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('rollHelp.ts:247', message, e)); }; diff --git a/src/commands/stats.ts b/src/commands/stats.ts index 9aeb3af..47a791c 100644 --- a/src/commands/stats.ts +++ b/src/commands/stats.ts @@ -1,36 +1,49 @@ -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - cache, - cacheHandlers, - DiscordenoMessage, + // Discordeno deps + cache, + cacheHandlers, + DiscordenoMessage, } from '../../deps.ts'; import { generateStats } from '../commandUtils.ts'; import { compilingStats } from '../commonEmbeds.ts'; import utils from '../utils.ts'; export const stats = async (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('stats')).catch((e) => utils.commonLoggers.dbError('stats.ts:14', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('stats')).catch((e) => utils.commonLoggers.dbError('stats.ts:14', 'call sproc INC_CNT on', e)); - try { - const m = await message.send(compilingStats); + try { + const m = await message.send(compilingStats); - // Calculate how many times commands have been run - const rollQuery = await dbClient.query(`SELECT count, hourlyRate FROM command_cnt WHERE command = "roll";`).catch((e) => utils.commonLoggers.dbError('stats.ts:23', 'query', e)); - const totalQuery = await dbClient.query(`SELECT SUM(count) as count, SUM(hourlyRate) as hourlyRate FROM command_cnt;`).catch((e) => utils.commonLoggers.dbError('stats.ts:24', 'query', e)); - const rolls = BigInt(rollQuery[0].count); - const rollRate = parseFloat(rollQuery[0].hourlyRate); - const total = BigInt(totalQuery[0].count); - const totalRate = parseFloat(totalQuery[0].hourlyRate); + // Calculate how many times commands have been run + const rollQuery = await dbClient + .query(`SELECT count, hourlyRate FROM command_cnt WHERE command = "roll";`) + .catch((e) => utils.commonLoggers.dbError('stats.ts:23', 'query', e)); + const totalQuery = await dbClient + .query(`SELECT SUM(count) as count, SUM(hourlyRate) as hourlyRate FROM command_cnt;`) + .catch((e) => utils.commonLoggers.dbError('stats.ts:24', 'query', e)); + const rolls = BigInt(rollQuery[0].count); + const rollRate = parseFloat(rollQuery[0].hourlyRate); + const total = BigInt(totalQuery[0].count); + const totalRate = parseFloat(totalQuery[0].hourlyRate); - 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, rollRate, totalRate - rollRate)).catch(( - e: Error, - ) => utils.commonLoggers.messageEditError('stats.ts:38', m, e)); - } catch (e) { - utils.commonLoggers.messageSendError('stats.ts:41', message, 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, + rollRate, + totalRate - rollRate + ) + ).catch((e: Error) => utils.commonLoggers.messageEditError('stats.ts:38', m, e)); + } catch (e) { + utils.commonLoggers.messageSendError('stats.ts:41', message, e); + } }; diff --git a/src/commands/version.ts b/src/commands/version.ts index 35ae95a..74289ab 100644 --- a/src/commands/version.ts +++ b/src/commands/version.ts @@ -1,20 +1,25 @@ import config from '../../config.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, + // Discordeno deps + DiscordenoMessage, } from '../../deps.ts'; import { infoColor1 } from '../commandUtils.ts'; import utils from '../utils.ts'; export const version = (message: DiscordenoMessage) => { - // Light telemetry to see how many times a command is being run - dbClient.execute(queries.callIncCnt('version')).catch((e) => utils.commonLoggers.dbError('version.ts:15', 'call sproc INC_CNT on', e)); + // Light telemetry to see how many times a command is being run + dbClient.execute(queries.callIncCnt('version')).catch((e) => utils.commonLoggers.dbError('version.ts:15', 'call sproc INC_CNT on', e)); - message.send({ - embeds: [{ - color: infoColor1, - title: `My current version is ${config.version}`, - }], - }).catch((e: Error) => utils.commonLoggers.messageSendError('version.ts:24', message, e)); + message + .send({ + embeds: [ + { + color: infoColor1, + title: `My current version is ${config.version}`, + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageSendError('version.ts:24', message, e)); }; diff --git a/src/db.ts b/src/db.ts deleted file mode 100644 index 322c108..0000000 --- a/src/db.ts +++ /dev/null @@ -1,30 +0,0 @@ -import config from '../config.ts'; -import { Client } from '../deps.ts'; -import { LOCALMODE } from '../flags.ts'; - -type UserIdObj = { - userid: bigint; -}; - -export const dbClient = await new Client().connect({ - hostname: LOCALMODE ? config.db.localhost : config.db.host, - port: config.db.port, - db: config.db.name, - username: config.db.username, - password: config.db.password, -}); - -// List of userIds who have requested that the bot ignore them -export const ignoreList: Array = []; -const dbIgnoreList = await dbClient.query('SELECT * FROM ignore_list'); -dbIgnoreList.forEach((userIdObj: UserIdObj) => { - ignoreList.push(userIdObj.userid); -}); - -export const weekDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; - -export const queries = { - insertRollLogCmd: (api: number, error: number) => `INSERT INTO roll_log(input,result,resultid,api,error) values(?,?,?,${api},${error})`, - callIncCnt: (cmdName: string) => `CALL INC_CNT("${cmdName}");`, - callIncHeatmap: (dateObj: Date) => `CALL INC_HEATMAP("${weekDays[dateObj.getDay()]}", ${dateObj.getHours()});`, -}; diff --git a/src/db/client.ts b/src/db/client.ts new file mode 100644 index 0000000..d7c782c --- /dev/null +++ b/src/db/client.ts @@ -0,0 +1,14 @@ +import config from '../../config.ts'; +import { Client } from '../../deps.ts'; +import { LOCALMODE } from '../../flags.ts'; + +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, + debug: true, +}); + +export default dbClient; diff --git a/src/db/common.ts b/src/db/common.ts new file mode 100644 index 0000000..d47ba02 --- /dev/null +++ b/src/db/common.ts @@ -0,0 +1,20 @@ +import dbClient from './client.ts'; + +type UserIdObj = { + userid: bigint; +}; + +// List of userIds who have requested that the bot ignore them +export const ignoreList: Array = []; +const dbIgnoreList = await dbClient.query('SELECT * FROM ignore_list'); +dbIgnoreList.forEach((userIdObj: UserIdObj) => { + ignoreList.push(userIdObj.userid); +}); + +export const weekDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; + +export const queries = { + insertRollLogCmd: (api: number, error: number) => `INSERT INTO roll_log(input,result,resultid,api,error) values(?,?,?,${api},${error})`, + callIncCnt: (cmdName: string) => `CALL INC_CNT("${cmdName}");`, + callIncHeatmap: (dateObj: Date) => `CALL INC_HEATMAP("${weekDays[dateObj.getDay()]}", ${dateObj.getHours()});`, +}; diff --git a/src/endpoints/deletes/apiKeyDelete.ts b/src/endpoints/deletes/apiKeyDelete.ts index c8752e8..c88bab6 100644 --- a/src/endpoints/deletes/apiKeyDelete.ts +++ b/src/endpoints/deletes/apiKeyDelete.ts @@ -1,83 +1,89 @@ import config from '../../../config.ts'; -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // nanoid deps - nanoid, - // Discordeno deps - sendMessage, + // nanoid deps + nanoid, + // Discordeno deps + sendMessage, } from '../../../deps.ts'; import { generateApiDeleteEmail } from '../../commandUtils.ts'; import utils from '../../utils.ts'; import stdResp from '../stdResponses.ts'; -export const apiKeyDelete = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt, apiUserEmail: string, apiUserDelCode: string) => { - 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; +export const apiKeyDelete = async ( + requestEvent: Deno.RequestEvent, + query: Map, + apiUserid: BigInt, + apiUserEmail: string, + apiUserDelCode: string +) => { + 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) => { - utils.commonLoggers.dbError('apiKeyDelete.ts:25', 'insert into', e); - requestEvent.respondWith(stdResp.InternalServerError('Channel Clean Failed.')); - erroredOut = true; - }); - if (erroredOut) { - return; - } + await dbClient.execute('DELETE FROM allowed_channels WHERE userid = ?', [apiUserid]).catch((e) => { + utils.commonLoggers.dbError('apiKeyDelete.ts:25', 'insert into', e); + requestEvent.respondWith(stdResp.InternalServerError('Channel Clean Failed.')); + erroredOut = true; + }); + if (erroredOut) { + return; + } - await dbClient.execute('DELETE FROM all_keys WHERE userid = ?', [apiUserid]).catch((e) => { - utils.commonLoggers.dbError('apiKeyDelete.ts:34', 'delete from', e); - requestEvent.respondWith(stdResp.InternalServerError('Delete Key Failed.')); - erroredOut = true; - }); - if (erroredOut) { - return; - } else { - // Send OK as response to indicate key deletion was successful - requestEvent.respondWith(stdResp.OK('You have been removed from the DB, Goodbye.')); - return; - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('Invalid Delete Code.')); - } - } else { - // User does not have their delete code yet, so we need to generate one and email it to them - const deleteCode = await nanoid(10); + await dbClient.execute('DELETE FROM all_keys WHERE userid = ?', [apiUserid]).catch((e) => { + utils.commonLoggers.dbError('apiKeyDelete.ts:34', 'delete from', e); + requestEvent.respondWith(stdResp.InternalServerError('Delete Key Failed.')); + erroredOut = true; + }); + if (erroredOut) { + return; + } else { + // Send OK as response to indicate key deletion was successful + requestEvent.respondWith(stdResp.OK('You have been removed from the DB, Goodbye.')); + return; + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('Invalid Delete Code.')); + } + } else { + // User does not have their delete code yet, so we need to generate one and email it to them + const deleteCode = await nanoid(10); - let erroredOut = false; + let erroredOut = false; - // Execute the DB modification - await dbClient.execute('UPDATE all_keys SET deleteCode = ? WHERE userid = ?', [deleteCode, apiUserid]).catch((e) => { - utils.commonLoggers.dbError('apiKeyDelete.ts:57', 'update', e); - requestEvent.respondWith(stdResp.InternalServerError('Delete Code Failed')); - erroredOut = true; - }); - if (erroredOut) { - return; - } + // Execute the DB modification + await dbClient.execute('UPDATE all_keys SET deleteCode = ? WHERE userid = ?', [deleteCode, apiUserid]).catch((e) => { + utils.commonLoggers.dbError('apiKeyDelete.ts:57', 'update', e); + requestEvent.respondWith(stdResp.InternalServerError('Delete Code Failed')); + erroredOut = true; + }); + if (erroredOut) { + return; + } - // "Send" the email - await sendMessage(config.api.email, generateApiDeleteEmail(apiUserEmail, deleteCode)).catch(() => { - requestEvent.respondWith(stdResp.InternalServerError('Failed to send email.')); - erroredOut = true; - }); - if (erroredOut) { - return; - } else { - // Send API key as response - requestEvent.respondWith(stdResp.FailedDependency('Please look for an email containing a Delete Key and run this query again with said key.')); - return; - } - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('You can only delete your own key.')); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // "Send" the email + await sendMessage(config.api.email, generateApiDeleteEmail(apiUserEmail, deleteCode)).catch(() => { + requestEvent.respondWith(stdResp.InternalServerError('Failed to send email.')); + erroredOut = true; + }); + if (erroredOut) { + return; + } else { + // Send API key as response + requestEvent.respondWith(stdResp.FailedDependency('Please look for an email containing a Delete Key and run this query again with said key.')); + return; + } + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('You can only delete your own key.')); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/gets/apiChannel.ts b/src/endpoints/gets/apiChannel.ts index 98e9158..dd3a885 100644 --- a/src/endpoints/gets/apiChannel.ts +++ b/src/endpoints/gets/apiChannel.ts @@ -1,35 +1,35 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import stdResp from '../stdResponses.ts'; import utils from '../../utils.ts'; export const apiChannel = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt) => { - 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; + 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) => { - utils.commonLoggers.dbError('apiChannel.ts', 'query', e); - requestEvent.respondWith(stdResp.InternalServerError('Failed to get channels.')); - erroredOut = true; - }); + // Get all channels userid has authorized + const dbAllowedChannelQuery = await dbClient.query('SELECT * FROM allowed_channels WHERE userid = ?', [apiUserid]).catch((e) => { + utils.commonLoggers.dbError('apiChannel.ts', 'query', e); + requestEvent.respondWith(stdResp.InternalServerError('Failed to get channels.')); + erroredOut = true; + }); - if (erroredOut) { - return; - } else { - // Customized strinification to handle BigInts correctly - const returnChannels = JSON.stringify(dbAllowedChannelQuery, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)); - // Send channel list as response - requestEvent.respondWith(stdResp.OK(returnChannels)); - return; - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('You can only view your own channels.')); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + if (erroredOut) { + return; + } else { + // Customized strinification to handle BigInts correctly + const returnChannels = JSON.stringify(dbAllowedChannelQuery, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)); + // Send channel list as response + requestEvent.respondWith(stdResp.OK(returnChannels)); + return; + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('You can only view your own channels.')); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/gets/apiKey.ts b/src/endpoints/gets/apiKey.ts index 36d6c90..d3de715 100644 --- a/src/endpoints/gets/apiKey.ts +++ b/src/endpoints/gets/apiKey.ts @@ -1,52 +1,52 @@ import config from '../../../config.ts'; -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // nanoid deps - nanoid, - // Discordeno deps - sendMessage, + // nanoid deps + nanoid, + // Discordeno deps + sendMessage, } from '../../../deps.ts'; import { generateApiKeyEmail } from '../../commandUtils.ts'; import utils from '../../utils.ts'; import stdResp from '../stdResponses.ts'; export const apiKey = async (requestEvent: Deno.RequestEvent, query: Map) => { - 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); + 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); - // Flag to see if there is an error inside the catch - let erroredOut = false; + // Flag to see if there is an error inside the catch + 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) => { - utils.commonLoggers.dbError('apiKey.ts:27', 'insert into', e); - requestEvent.respondWith(stdResp.InternalServerError('Failed to store key.')); - erroredOut = true; - }, - ); + // 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) => { + utils.commonLoggers.dbError('apiKey.ts:27', 'insert into', e); + requestEvent.respondWith(stdResp.InternalServerError('Failed to store key.')); + erroredOut = true; + }); - // Exit this case now if catch errored - if (erroredOut) { - return; - } + // Exit this case now if catch errored + if (erroredOut) { + return; + } - // "Send" the email - await sendMessage(config.api.email, generateApiKeyEmail(query.get('email') || 'no email', newKey)).catch(() => { - requestEvent.respondWith(stdResp.InternalServerError('Failed to send email.')); - erroredOut = true; - }); + // "Send" the email + await sendMessage(config.api.email, generateApiKeyEmail(query.get('email') || 'no email', newKey)).catch(() => { + requestEvent.respondWith(stdResp.InternalServerError('Failed to send email.')); + erroredOut = true; + }); - if (erroredOut) { - return; - } else { - // Send basic OK to indicate key has been sent - requestEvent.respondWith(stdResp.OK('Email Sent.')); - return; - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + if (erroredOut) { + return; + } else { + // Send basic OK to indicate key has been sent + requestEvent.respondWith(stdResp.OK('Email Sent.')); + return; + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/gets/apiKeyAdmin.ts b/src/endpoints/gets/apiKeyAdmin.ts index 7390d26..6f0858e 100644 --- a/src/endpoints/gets/apiKeyAdmin.ts +++ b/src/endpoints/gets/apiKeyAdmin.ts @@ -1,42 +1,42 @@ import config from '../../../config.ts'; -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // nanoid deps - nanoid, + // nanoid deps + nanoid, } from '../../../deps.ts'; import stdResp from '../stdResponses.ts'; import utils from '../../utils.ts'; export const apiKeyAdmin = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt) => { - 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); + 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); - // Flag to see if there is an error inside the catch - let erroredOut = false; + // Flag to see if there is an error inside the catch + 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) => { - utils.commonLoggers.dbError('apiKeyAdmin.ts:24', 'insert into', e); - requestEvent.respondWith(stdResp.InternalServerError('Failed to store key.')); - erroredOut = true; - }); + // Insert new key/user pair into the db + await dbClient.execute('INSERT INTO all_keys(userid,apiKey) values(?,?)', [apiUserid, newKey]).catch((e) => { + utils.commonLoggers.dbError('apiKeyAdmin.ts:24', 'insert into', e); + requestEvent.respondWith(stdResp.InternalServerError('Failed to store key.')); + erroredOut = true; + }); - // Exit this case now if catch errored - if (erroredOut) { - return; - } else { - // Send API key as response - requestEvent.respondWith(stdResp.OK(JSON.stringify({ 'key': newKey, 'userid': query.get('user') }))); - return; - } - } else { - // Only allow the db admin to use this API - requestEvent.respondWith(stdResp.Forbidden(stdResp.Strings.restricted)); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // Exit this case now if catch errored + if (erroredOut) { + return; + } else { + // Send API key as response + requestEvent.respondWith(stdResp.OK(JSON.stringify({ key: newKey, userid: query.get('user') }))); + return; + } + } else { + // Only allow the db admin to use this API + requestEvent.respondWith(stdResp.Forbidden(stdResp.Strings.restricted)); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/gets/apiRoll.ts b/src/endpoints/gets/apiRoll.ts index 5b0e298..b93c392 100644 --- a/src/endpoints/gets/apiRoll.ts +++ b/src/endpoints/gets/apiRoll.ts @@ -1,11 +1,11 @@ import config from '../../../config.ts'; -import { dbClient, queries } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import { - // Discordeno deps - cache, - // Log4Deno deps - log, - LT, + // Discordeno deps + cache, + // Log4Deno deps + log, + LT, } from '../../../deps.ts'; import { QueuedRoll, RollModifiers } from '../../mod.d.ts'; import utils from '../../utils.ts'; @@ -15,107 +15,116 @@ import stdResp from '../stdResponses.ts'; const apiWarning = `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}>`; export const apiRoll = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt) => { - // 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')) { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.BadRequest('Cannot have both \'n\' and \'m\'.')); - return; - } + // 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')) { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.BadRequest("Cannot have both 'n' and 'm'.")); + return; + } - // Check if user is authenticated to use this endpoint - let authorized = false; - let hideWarn = false; + // Check if user is authenticated to use this endpoint + let authorized = false; + 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) { - // 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'), - ]); + // 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) { + // 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'), + ]); - // Make sure guild allows API rolls - if (dbGuildQuery.length === 1 && dbGuildQuery[0].active && !dbGuildQuery[0].banned) { - authorized = true; - hideWarn = dbGuildQuery[0].hidewarn; - } - } - } + // Make sure guild allows API rolls + if (dbGuildQuery.length === 1 && dbGuildQuery[0].active && !dbGuildQuery[0].banned) { + authorized = true; + hideWarn = dbGuildQuery[0].hidewarn; + } + } + } - if (authorized) { - // Rest of this command is in a try-catch to protect all sends/edits from erroring out - try { - // Make sure rollCmd is not undefined - let rollCmd = query.get('rollstr') || ''; - const originalCommand = query.get('rollstr'); + if (authorized) { + // Rest of this command is in a try-catch to protect all sends/edits from erroring out + try { + // Make sure rollCmd is not undefined + let rollCmd = query.get('rollstr') || ''; + const originalCommand = query.get('rollstr'); - if (rollCmd.length === 0) { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest('rollCmd is required.')); + if (rollCmd.length === 0) { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest('rollCmd is required.')); - // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'EmptyInput', null]).catch((e) => utils.commonLoggers.dbError('apiRoll.ts:65', 'insert', e)); - return; - } + // Always log API rolls for abuse detection + dbClient + .execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'EmptyInput', null]) + .catch((e) => utils.commonLoggers.dbError('apiRoll.ts:65', 'insert', e)); + return; + } - if (query.has('o') && (query.get('o')?.toLowerCase() !== 'd' && query.get('o')?.toLowerCase() !== 'a')) { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest('Order must be set to \'a\' or \'d\'.')); + if (query.has('o') && query.get('o')?.toLowerCase() !== 'd' && query.get('o')?.toLowerCase() !== 'a') { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest("Order must be set to 'a' or 'd'.")); - // Always log API rolls for abuse detection - dbClient.execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'BadOrder', null]).catch((e) => utils.commonLoggers.dbError('apiRoll.ts:66', 'insert', e)); - return; - } + // Always log API rolls for abuse detection + dbClient + .execute(queries.insertRollLogCmd(1, 1), [originalCommand, 'BadOrder', null]) + .catch((e) => utils.commonLoggers.dbError('apiRoll.ts:66', 'insert', e)); + return; + } - // 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, ' '); + // 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, ' '); - const modifiers: RollModifiers = { - noDetails: query.has('nd'), - superNoDetails: query.has('snd'), - spoiler: query.has('s') ? '||' : '', - maxRoll: query.has('m'), - nominalRoll: query.has('n'), - gmRoll: query.has('gms'), - gms: query.has('gms') ? (query.get('gms') || '').split(',') : [], - order: query.has('o') ? (query.get('o')?.toLowerCase() || '') : '', - count: query.has('c'), - valid: true, - apiWarn: hideWarn ? '' : apiWarning, - }; + const modifiers: RollModifiers = { + noDetails: query.has('nd'), + superNoDetails: query.has('snd'), + spoiler: query.has('s') ? '||' : '', + maxRoll: query.has('m'), + nominalRoll: query.has('n'), + gmRoll: query.has('gms'), + gms: query.has('gms') ? (query.get('gms') || '').split(',') : [], + order: query.has('o') ? query.get('o')?.toLowerCase() || '' : '', + count: query.has('c'), + valid: true, + apiWarn: hideWarn ? '' : apiWarning, + }; - // Parse the roll and get the return text - await queueRoll( - { - apiRoll: true, - api: { requestEvent, channelId: BigInt(query.get('channel') || '0'), userId: BigInt(query.get('user') || '') }, - rollCmd, - modifiers, - originalCommand, - }, - ); - } catch (err) { - // Handle any errors we missed - log(LT.ERROR, `Unhandled Error: ${JSON.stringify(err)}`); - requestEvent.respondWith(stdResp.InternalServerError('Something went wrong.')); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith( - stdResp.Forbidden( - `Verify you are a member of the guild you are sending this roll to. If you are, the ${config.name} may not have that registered, please send a message in the guild so ${config.name} can register this. This registration is temporary, so if you see this error again, just poke your server again.`, - ), - ); - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // Parse the roll and get the return text + await queueRoll({ + apiRoll: true, + api: { requestEvent, channelId: BigInt(query.get('channel') || '0'), userId: BigInt(query.get('user') || '') }, + rollCmd, + modifiers, + originalCommand, + }); + } catch (err) { + // Handle any errors we missed + log(LT.ERROR, `Unhandled Error: ${JSON.stringify(err)}`); + requestEvent.respondWith(stdResp.InternalServerError('Something went wrong.')); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith( + stdResp.Forbidden( + `Verify you are a member of the guild you are sending this roll to. If you are, the ${config.name} may not have that registered, please send a message in the guild so ${config.name} can register this. This registration is temporary, so if you see this error again, just poke your server again.` + ) + ); + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/gets/heatmapPng.ts b/src/endpoints/gets/heatmapPng.ts index 7a94672..c1c4d1c 100644 --- a/src/endpoints/gets/heatmapPng.ts +++ b/src/endpoints/gets/heatmapPng.ts @@ -1,19 +1,19 @@ import { - // httpd deps - Status, - STATUS_TEXT, + // httpd deps + STATUS_CODE, + STATUS_TEXT, } from '../../../deps.ts'; export const heatmapPng = async (requestEvent: Deno.RequestEvent) => { - const file = Deno.readFileSync('./src/endpoints/gets/heatmap.png'); - const imageHeaders = new Headers(); - imageHeaders.append('Content-Type', 'image/png'); - // Send basic OK to indicate key has been sent - requestEvent.respondWith( - new Response(file, { - status: Status.OK, - statusText: STATUS_TEXT[Status.OK], - headers: imageHeaders, - }), - ); + const file = Deno.readFileSync('./src/endpoints/gets/heatmap.png'); + const imageHeaders = new Headers(); + imageHeaders.append('Content-Type', 'image/png'); + // Send basic OK to indicate key has been sent + requestEvent.respondWith( + new Response(file, { + status: STATUS_CODE.OK, + statusText: STATUS_TEXT[STATUS_CODE.OK], + headers: imageHeaders, + }) + ); }; diff --git a/src/endpoints/posts/apiChannelAdd.ts b/src/endpoints/posts/apiChannelAdd.ts index 12a360b..c90ad42 100644 --- a/src/endpoints/posts/apiChannelAdd.ts +++ b/src/endpoints/posts/apiChannelAdd.ts @@ -1,34 +1,34 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import stdResp from '../stdResponses.ts'; import utils from '../../utils.ts'; export const apiChannelAdd = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt) => { - 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; + 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) => { - utils.commonLoggers.dbError('apiChannelAdd.ts:17', 'insert into', e); - requestEvent.respondWith(stdResp.InternalServerError('Failed to store channel.')); - erroredOut = true; - }); + // 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) => { + utils.commonLoggers.dbError('apiChannelAdd.ts:17', 'insert into', e); + requestEvent.respondWith(stdResp.InternalServerError('Failed to store channel.')); + erroredOut = true; + }); - // Exit this case now if catch errored - if (erroredOut) { - return; - } else { - // Send OK to indicate modification was successful - requestEvent.respondWith(stdResp.OK('Successfully added channel.')); - return; - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('You can only add channels to your key.')); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // Exit this case now if catch errored + if (erroredOut) { + return; + } else { + // Send OK to indicate modification was successful + requestEvent.respondWith(stdResp.OK('Successfully added channel.')); + return; + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('You can only add channels to your key.')); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/puts/apiChannelManageActive.ts b/src/endpoints/puts/apiChannelManageActive.ts index e0c4dd5..e04c02e 100644 --- a/src/endpoints/puts/apiChannelManageActive.ts +++ b/src/endpoints/puts/apiChannelManageActive.ts @@ -1,41 +1,44 @@ -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import stdResp from '../stdResponses.ts'; import utils from '../../utils.ts'; export const apiChannelManageActive = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt, path: string) => { - 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; + 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) { - value = 0; - } else { - value = 1; - } + // Determine value to set + 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) => { - utils.commonLoggers.dbError('apiChannelManageActive.ts:25', 'update', e); - requestEvent.respondWith(stdResp.InternalServerError('Failed to update channel.')); - erroredOut = true; - }); + // 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) => { + utils.commonLoggers.dbError('apiChannelManageActive.ts:25', 'update', e); + requestEvent.respondWith(stdResp.InternalServerError('Failed to update channel.')); + erroredOut = true; + }); - // Exit this case now if catch errored - if (erroredOut) { - return; - } else { - // Send API key as response - requestEvent.respondWith(stdResp.OK(`Successfully active to ${value}.`)); - return; - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('You can only manage your own channels.')); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // Exit this case now if catch errored + if (erroredOut) { + return; + } else { + // Send API key as response + requestEvent.respondWith(stdResp.OK(`Successfully active to ${value}.`)); + return; + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('You can only manage your own channels.')); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/puts/apiChannelManageBan.ts b/src/endpoints/puts/apiChannelManageBan.ts index e9f78fe..06978c3 100644 --- a/src/endpoints/puts/apiChannelManageBan.ts +++ b/src/endpoints/puts/apiChannelManageBan.ts @@ -1,45 +1,52 @@ import config from '../../../config.ts'; -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import stdResp from '../stdResponses.ts'; import utils from '../../utils.ts'; export const apiChannelManageBan = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt, path: string) => { - 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; + 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) { - value = 0; - } else { - value = 1; - } + // Determine value to set + 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) => { - utils.commonLoggers.dbError('apiChannelManageBan.ts:28', 'update', e); - requestEvent.respondWith(stdResp.InternalServerError('Failed to update channel.')); - erroredOut = true; - }); + // 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) => { + utils.commonLoggers.dbError('apiChannelManageBan.ts:28', 'update', e); + requestEvent.respondWith(stdResp.InternalServerError('Failed to update channel.')); + erroredOut = true; + }); - // Exit this case now if catch errored - if (erroredOut) { - return; - } else { - // Send OK to indicate modification was successful - requestEvent.respondWith(stdResp.OK(`Successfully active to ${value}.`)); - return; - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden(stdResp.Strings.restricted)); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // Exit this case now if catch errored + if (erroredOut) { + return; + } else { + // Send OK to indicate modification was successful + requestEvent.respondWith(stdResp.OK(`Successfully active to ${value}.`)); + return; + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden(stdResp.Strings.restricted)); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/puts/apiKeyManage.ts b/src/endpoints/puts/apiKeyManage.ts index ee23ae9..75d5fb7 100644 --- a/src/endpoints/puts/apiKeyManage.ts +++ b/src/endpoints/puts/apiKeyManage.ts @@ -1,51 +1,51 @@ import config from '../../../config.ts'; -import { dbClient } from '../../db.ts'; +import dbClient from '../../db/client.ts'; import stdResp from '../stdResponses.ts'; import utils from '../../utils.ts'; export const apiKeyManage = async (requestEvent: Deno.RequestEvent, query: Map, apiUserid: BigInt, path: string) => { - 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: string, - value: number, - erroredOut = false; + 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: string, + value: number, + erroredOut = false; - // Determine key to edit - if (path.toLowerCase().indexOf('ban') > 0) { - key = 'banned'; - } else { - key = 'active'; - } + // Determine key to edit + if (path.toLowerCase().indexOf('ban') > 0) { + key = 'banned'; + } else { + key = 'active'; + } - // Determine value to set - if (path.toLowerCase().indexOf('de') > 0 || path.toLowerCase().indexOf('un') > 0) { - value = 0; - } else { - value = 1; - } + // Determine value to set + 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) => { - utils.commonLoggers.dbError('apiKeyManage.ts', 'update', e); - requestEvent.respondWith(stdResp.InternalServerError(`Failed to ${key} to ${value}.`)); - erroredOut = true; - }); + // Execute the DB modification + await dbClient.execute('UPDATE all_keys SET ?? = ? WHERE userid = ?', [key, value, apiUserid]).catch((e) => { + utils.commonLoggers.dbError('apiKeyManage.ts', 'update', e); + requestEvent.respondWith(stdResp.InternalServerError(`Failed to ${key} to ${value}.`)); + erroredOut = true; + }); - // Exit this case now if catch errored - if (erroredOut) { - return; - } else { - // Send OK as response to indicate modification was successful - requestEvent.respondWith(stdResp.OK(`Successfully ${key} to ${value}.`)); - return; - } - } else { - // Alert API user that they shouldn't be doing this - requestEvent.respondWith(stdResp.Forbidden('You can only manage your own key.')); - } - } else { - // Alert API user that they messed up - requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); - } + // Exit this case now if catch errored + if (erroredOut) { + return; + } else { + // Send OK as response to indicate modification was successful + requestEvent.respondWith(stdResp.OK(`Successfully ${key} to ${value}.`)); + return; + } + } else { + // Alert API user that they shouldn't be doing this + requestEvent.respondWith(stdResp.Forbidden('You can only manage your own key.')); + } + } else { + // Alert API user that they messed up + requestEvent.respondWith(stdResp.BadRequest(stdResp.Strings.missingParams)); + } }; diff --git a/src/endpoints/stdResponses.ts b/src/endpoints/stdResponses.ts index fcf84b3..0a7c90c 100644 --- a/src/endpoints/stdResponses.ts +++ b/src/endpoints/stdResponses.ts @@ -1,23 +1,25 @@ import { - // httpd deps - Status, - STATUS_TEXT, + // httpd deps + StatusCode, + STATUS_CODE, + STATUS_TEXT, } from '../../deps.ts'; -const genericResponse = (customText: string, status: Status) => new Response(customText || STATUS_TEXT[status], { status: status, statusText: STATUS_TEXT[status] }); +const genericResponse = (customText: string, status: StatusCode) => + new Response(customText || STATUS_TEXT[status], { status: status, statusText: STATUS_TEXT[status] }); export default { - BadRequest: (customText: string) => genericResponse(customText, Status.BadRequest), - FailedDependency: (customText: string) => genericResponse(customText, Status.FailedDependency), - InternalServerError: (customText: string) => genericResponse(customText, Status.InternalServerError), - Forbidden: (customText: string) => genericResponse(customText, Status.Forbidden), - MethodNotAllowed: (customText: string) => genericResponse(customText, Status.MethodNotAllowed), - NotFound: (customText: string) => genericResponse(customText, Status.NotFound), - OK: (customText: string) => genericResponse(customText, Status.OK), - RequestTimeout: (customText: string) => genericResponse(customText, Status.RequestTimeout), - TooManyRequests: (customText: string) => genericResponse(customText, Status.TooManyRequests), - Strings: { - missingParams: 'Missing Parameters.', - restricted: 'This API is restricted.', - }, + BadRequest: (customText: string) => genericResponse(customText, STATUS_CODE.BadRequest), + FailedDependency: (customText: string) => genericResponse(customText, STATUS_CODE.FailedDependency), + InternalServerError: (customText: string) => genericResponse(customText, STATUS_CODE.InternalServerError), + Forbidden: (customText: string) => genericResponse(customText, STATUS_CODE.Forbidden), + MethodNotAllowed: (customText: string) => genericResponse(customText, STATUS_CODE.MethodNotAllowed), + NotFound: (customText: string) => genericResponse(customText, STATUS_CODE.NotFound), + OK: (customText: string) => genericResponse(customText, STATUS_CODE.OK), + RequestTimeout: (customText: string) => genericResponse(customText, STATUS_CODE.RequestTimeout), + TooManyRequests: (customText: string) => genericResponse(customText, STATUS_CODE.TooManyRequests), + Strings: { + missingParams: 'Missing Parameters.', + restricted: 'This API is restricted.', + }, }; diff --git a/src/intervals.ts b/src/intervals.ts index b0cc2d0..0607e18 100644 --- a/src/intervals.ts +++ b/src/intervals.ts @@ -5,66 +5,67 @@ */ import { - // Discordeno deps - cache, - cacheHandlers, - // imagescript dep - is, - // Log4Deno deps - log, - LT, + // Discordeno deps + cache, + cacheHandlers, + // imagescript dep + is, + // Log4Deno deps + log, + LT, } from '../deps.ts'; import { PastCommandCount } from './mod.d.ts'; -import { dbClient, weekDays } from './db.ts'; +import dbClient from './db/client.ts'; +import { weekDays } from './db/common.ts'; import utils from './utils.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 = ''; - switch (Math.floor((Math.random() * 4) + 1)) { - case 1: - status = `${config.prefix}help for commands`; - break; - case 2: - status = `Running V${config.version}`; - break; - case 3: - status = `${config.prefix}info to learn more`; - break; - default: { - const cachedCount = await cacheHandlers.size('guilds'); - status = `Rolling dice for ${cachedCount + cache.dispatchedGuildIds.size} servers`; - break; - } - } + let status = ''; + switch (Math.floor(Math.random() * 4 + 1)) { + case 1: + status = `${config.prefix}help for commands`; + break; + case 2: + status = `Running V${config.version}`; + break; + case 3: + status = `${config.prefix}info to learn more`; + break; + default: { + const cachedCount = await cacheHandlers.size('guilds'); + status = `Rolling dice for ${cachedCount + cache.dispatchedGuildIds.size} servers`; + break; + } + } - return status; + return status; }; // updateListStatistics(bot ID, current guild count) returns nothing, posts to botlists // 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) => { - try { - 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'); - // ?{} 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 - }); - log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); - } - } catch (err) { - log(LT.ERROR, `Failed to update statistics for ${e.name} | Error: ${err.name} - ${err.message}`) - } - }); + config.botLists.forEach(async (e) => { + try { + 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'); + // ?{} 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 + }); + log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`); + } + } catch (err) { + log(LT.ERROR, `Failed to update statistics for ${e.name} | Error: ${err.name} - ${err.message}`); + } + }); }; // Keep one week of data @@ -73,141 +74,141 @@ const previousHours: Array> = []; // updateHourlyRates() returns nothing, updates DB directly // Updates the hourlyRate for command usage const updateHourlyRates = async () => { - try { - const newestHour = await dbClient.query('SELECT command, count FROM command_cnt ORDER BY command;').catch((e) => utils.commonLoggers.dbError('intervals.ts:71', 'query', e)); - previousHours.push(newestHour); - if (previousHours.length > 1) { - const oldestHour = previousHours[0]; + try { + const newestHour = await dbClient + .query('SELECT command, count FROM command_cnt ORDER BY command;') + .catch((e) => utils.commonLoggers.dbError('intervals.ts:71', 'query', e)); + previousHours.push(newestHour); + if (previousHours.length > 1) { + const oldestHour = previousHours[0]; - const computedDiff: Array = []; - for (let i = 0; i < newestHour.length; i++) { - computedDiff.push({ - command: newestHour[i].command, - count: (newestHour[i].count - oldestHour[i].count), - }); - log(LT.LOG, `Updating hourlyRate | Computing diffs: ${JSON.stringify(computedDiff)}`); - } + const computedDiff: Array = []; + for (let i = 0; i < newestHour.length; i++) { + computedDiff.push({ + command: newestHour[i].command, + count: newestHour[i].count - oldestHour[i].count, + }); + log(LT.LOG, `Updating hourlyRate | Computing diffs: ${JSON.stringify(computedDiff)}`); + } - // Update DB - computedDiff.forEach(async (cmd) => { - log(LT.LOG, `Updating hourlyRate | Storing to DB: ${JSON.stringify(cmd)}`); - await dbClient.execute(`UPDATE command_cnt SET hourlyRate = ? WHERE command = ?`, [cmd.count / previousHours.length, cmd.command]).catch((e) => - utils.commonLoggers.dbError('intervals.ts:88', 'update', e) - ); - }); - } + // Update DB + computedDiff.forEach(async (cmd) => { + log(LT.LOG, `Updating hourlyRate | Storing to DB: ${JSON.stringify(cmd)}`); + await dbClient + .execute(`UPDATE command_cnt SET hourlyRate = ? WHERE command = ?`, [cmd.count / previousHours.length, cmd.command]) + .catch((e) => utils.commonLoggers.dbError('intervals.ts:88', 'update', e)); + }); + } - if (previousHours.length > hoursToKeep) { - previousHours.unshift(); - } - } catch (e) { - log(LT.ERROR, `Something went wrong in previousHours interval | Error: ${e.name} - ${e.message}`); - } + if (previousHours.length > hoursToKeep) { + previousHours.unshift(); + } + } catch (e) { + log(LT.ERROR, `Something went wrong in previousHours interval | Error: ${e.name} - ${e.message}`); + } }; // getPercentOfRange(min, max, val) returns number // Gets a percent value of where val lies in the min-max range const getPercentOfRange = (minVal: number, maxVal: number, val: number): number => { - const localMax = maxVal - minVal; - const localVal = val - minVal; + const localMax = maxVal - minVal; + const localVal = val - minVal; - return localVal / localMax; + return localVal / localMax; }; // Pixel locations in heatmap-base.png, pixel locations are 0 based // dayPixels holds the left and right (AKA X Coord) pixel locations for each col (ex: [leftPX, rightPX]) const dayPixels: Array> = [ - [72, 159], - [163, 260], - [264, 359], - [363, 497], - [501, 608], - [612, 686], - [690, 800], + [72, 159], + [163, 260], + [264, 359], + [363, 497], + [501, 608], + [612, 686], + [690, 800], ]; // hourPixels holds the top and bottom (AKA Y Coord) pixel locations for each row (ex: [topPX, botPX]) const hourPixels: Array> = [ - [29, 49], - [51, 72], - [74, 95], - [97, 118], - [120, 141], - [143, 164], - [166, 187], - [189, 209], - [211, 232], - [234, 254], - [256, 277], - [279, 299], - [301, 322], - [324, 345], - [347, 368], - [370, 391], - [393, 413], - [415, 436], - [438, 459], - [461, 482], - [484, 505], - [507, 528], - [530, 550], - [552, 572], + [29, 49], + [51, 72], + [74, 95], + [97, 118], + [120, 141], + [143, 164], + [166, 187], + [189, 209], + [211, 232], + [234, 254], + [256, 277], + [279, 299], + [301, 322], + [324, 345], + [347, 368], + [370, 391], + [393, 413], + [415, 436], + [438, 459], + [461, 482], + [484, 505], + [507, 528], + [530, 550], + [552, 572], ]; // updateHeatmap() returns nothing, creates new heatmap.png // Updates the heatmap image with latest data from the db let minRollCnt: number; let maxRollCnt: number; const updateHeatmapPng = async () => { - const baseHeatmap = Deno.readFileSync('./src/endpoints/gets/heatmap-base.png'); - const heatmap = await is.decode(baseHeatmap); - if (!(heatmap instanceof is.Image)) { - return; - } - // Get latest data from DB - const heatmapData = await dbClient.query('SELECT * FROM roll_time_heatmap ORDER BY hour;').catch((e) => utils.commonLoggers.dbError('intervals.ts:148', 'query', e)); + const baseHeatmap = Deno.readFileSync('./src/endpoints/gets/heatmap-base.png'); + const heatmap = await is.decode(baseHeatmap); + if (!(heatmap instanceof is.Image)) { + return; + } + // Get latest data from DB + const heatmapData = await dbClient + .query('SELECT * FROM roll_time_heatmap ORDER BY hour;') + .catch((e) => utils.commonLoggers.dbError('intervals.ts:148', 'query', e)); - minRollCnt = Infinity; - maxRollCnt = 0; - // determine min and max values - for (const hour of heatmapData) { - for (const day of weekDays) { - const rollCnt = hour[day]; - log(LT.LOG, `updateHeatmapPng | finding min/max | min: ${minRollCnt} max: ${maxRollCnt} curr: ${rollCnt}`); - if (rollCnt > maxRollCnt) { - maxRollCnt = rollCnt; - } - if (rollCnt < minRollCnt) { - minRollCnt = rollCnt; - } - } - } + minRollCnt = Infinity; + maxRollCnt = 0; + // determine min and max values + for (const hour of heatmapData) { + for (const day of weekDays) { + const rollCnt = hour[day]; + log(LT.LOG, `updateHeatmapPng | finding min/max | min: ${minRollCnt} max: ${maxRollCnt} curr: ${rollCnt}`); + if (rollCnt > maxRollCnt) { + maxRollCnt = rollCnt; + } + if (rollCnt < minRollCnt) { + minRollCnt = rollCnt; + } + } + } - // Apply values to image - for (let hour = 0; hour < heatmapData.length; hour++) { - for (let day = 0; day < weekDays.length; day++) { - log(LT.LOG, `updateHeatmapPng | putting ${weekDays[day]} ${hour}:00 into image`); - const percent = getPercentOfRange(minRollCnt, maxRollCnt, heatmapData[hour][weekDays[day]]); - heatmap.drawBox( - dayPixels[day][0] + 1, - hourPixels[hour][0] + 1, - dayPixels[day][1] - dayPixels[day][0] + 1, - hourPixels[hour][1] - hourPixels[hour][0] + 1, - is.Image.rgbToColor( - 255 * (1 - percent), - 255 * percent, - 0, - ), - ); - } - } + // Apply values to image + for (let hour = 0; hour < heatmapData.length; hour++) { + for (let day = 0; day < weekDays.length; day++) { + log(LT.LOG, `updateHeatmapPng | putting ${weekDays[day]} ${hour}:00 into image`); + const percent = getPercentOfRange(minRollCnt, maxRollCnt, heatmapData[hour][weekDays[day]]); + heatmap.drawBox( + dayPixels[day][0] + 1, + hourPixels[hour][0] + 1, + dayPixels[day][1] - dayPixels[day][0] + 1, + hourPixels[hour][1] - hourPixels[hour][0] + 1, + is.Image.rgbToColor(255 * (1 - percent), 255 * percent, 0) + ); + } + } - Deno.writeFileSync('./src/endpoints/gets/heatmap.png', await heatmap.encode()); + Deno.writeFileSync('./src/endpoints/gets/heatmap.png', await heatmap.encode()); }; export default { - getRandomStatus, - updateListStatistics, - updateHourlyRates, - updateHeatmapPng, - getMinRollCnt: () => minRollCnt, - getMaxRollCnt: () => maxRollCnt, + getRandomStatus, + updateListStatistics, + updateHourlyRates, + updateHeatmapPng, + getMinRollCnt: () => minRollCnt, + getMaxRollCnt: () => maxRollCnt, }; diff --git a/src/solver/rollQueue.ts b/src/solver/rollQueue.ts index 2d8f8db..88e0c6a 100644 --- a/src/solver/rollQueue.ts +++ b/src/solver/rollQueue.ts @@ -1,15 +1,16 @@ import config from '../../config.ts'; import { DEVMODE } from '../../flags.ts'; -import { dbClient, queries } from '../db.ts'; +import dbClient from '../db/client.ts'; +import { queries } from '../db/common.ts'; import { - // Discordeno deps - DiscordenoMessage, - // Log4Deno deps - log, - LT, - // Discordeno deps - sendDirectMessage, - sendMessage, + // Discordeno deps + DiscordenoMessage, + // Log4Deno deps + log, + LT, + // Discordeno deps + sendDirectMessage, + sendMessage, } from '../../deps.ts'; import { SolvedRoll } from '../solver/solver.d.ts'; import { QueuedRoll, RollModifiers } from '../mod.d.ts'; @@ -22,189 +23,208 @@ const rollQueue: Array = []; // Handle setting up and calling the rollWorker const handleRollWorker = async (rq: QueuedRoll) => { - currentWorkers++; + currentWorkers++; - // gmModifiers used to create gmEmbed (basically just turn off the gmRoll) - const gmModifiers = JSON.parse(JSON.stringify(rq.modifiers)); - gmModifiers.gmRoll = false; + // gmModifiers used to create gmEmbed (basically just turn off the gmRoll) + const gmModifiers = JSON.parse(JSON.stringify(rq.modifiers)); + gmModifiers.gmRoll = false; - const rollWorker = new Worker(new URL('../solver/rollWorker.ts', import.meta.url).href, { type: 'module' }); + const rollWorker = new Worker(new URL('../solver/rollWorker.ts', import.meta.url).href, { type: 'module' }); - const workerTimeout = setTimeout(async () => { - rollWorker.terminate(); - currentWorkers--; - if (rq.apiRoll) { - rq.api.requestEvent.respondWith(stdResp.RequestTimeout('Roll took too long to process, try breaking roll down into simpler parts')); - } else { - rq.dd.m.edit({ - embeds: [ - (await generateRollEmbed( - rq.dd.message.authorId, - { - error: true, - errorCode: 'TooComplex', - errorMsg: 'Error: Roll took too long to process, try breaking roll down into simpler parts', - }, - {}, - )).embed, - ], - }).catch((e) => utils.commonLoggers.messageEditError('rollQueue.ts:51', rq.dd.m, e)); - } - }, config.limits.workerTimeout); + const workerTimeout = setTimeout(async () => { + rollWorker.terminate(); + currentWorkers--; + if (rq.apiRoll) { + rq.api.requestEvent.respondWith(stdResp.RequestTimeout('Roll took too long to process, try breaking roll down into simpler parts')); + } else { + rq.dd.m + .edit({ + embeds: [ + ( + await generateRollEmbed( + rq.dd.message.authorId, + { + error: true, + errorCode: 'TooComplex', + errorMsg: 'Error: Roll took too long to process, try breaking roll down into simpler parts', + }, + {} + ) + ).embed, + ], + }) + .catch((e) => utils.commonLoggers.messageEditError('rollQueue.ts:51', rq.dd.m, e)); + } + }, config.limits.workerTimeout); - rollWorker.addEventListener('message', async (workerMessage) => { - if (workerMessage.data === 'ready') { - rollWorker.postMessage({ - rollCmd: rq.rollCmd, - modifiers: rq.modifiers, - }); - return; - } - let apiErroredOut = false; - try { - currentWorkers--; - clearTimeout(workerTimeout); - const returnmsg = workerMessage.data; - const pubEmbedDetails = await generateRollEmbed(rq.apiRoll ? rq.api.userId : rq.dd.message.authorId, returnmsg, rq.modifiers); - const gmEmbedDetails = await generateRollEmbed(rq.apiRoll ? rq.api.userId : rq.dd.message.authorId, returnmsg, gmModifiers); - const countEmbed = generateCountDetailsEmbed(returnmsg.counts); + rollWorker.addEventListener('message', async (workerMessage) => { + if (workerMessage.data === 'ready') { + rollWorker.postMessage({ + rollCmd: rq.rollCmd, + modifiers: rq.modifiers, + }); + return; + } + let apiErroredOut = false; + try { + currentWorkers--; + clearTimeout(workerTimeout); + const returnmsg = workerMessage.data; + const pubEmbedDetails = await generateRollEmbed(rq.apiRoll ? rq.api.userId : rq.dd.message.authorId, returnmsg, rq.modifiers); + const gmEmbedDetails = await generateRollEmbed(rq.apiRoll ? rq.api.userId : rq.dd.message.authorId, returnmsg, gmModifiers); + const countEmbed = generateCountDetailsEmbed(returnmsg.counts); - // If there was an error, report it to the user in hopes that they can determine what they did wrong - if (returnmsg.error) { - if (rq.apiRoll) { - rq.api.requestEvent.respondWith(stdResp.InternalServerError(returnmsg.errorMsg)); - } else { - rq.dd.m.edit({ embeds: [pubEmbedDetails.embed] }); - } + // If there was an error, report it to the user in hopes that they can determine what they did wrong + if (returnmsg.error) { + if (rq.apiRoll) { + rq.api.requestEvent.respondWith(stdResp.InternalServerError(returnmsg.errorMsg)); + } else { + rq.dd.m.edit({ embeds: [pubEmbedDetails.embed] }); + } - if (rq.apiRoll || DEVMODE && config.logRolls) { - // If enabled, log rolls so we can see what went wrong - dbClient.execute(queries.insertRollLogCmd(rq.apiRoll ? 1 : 0, 1), [rq.originalCommand, returnmsg.errorCode, rq.apiRoll ? null : rq.dd.m.id]).catch((e) => - utils.commonLoggers.dbError('rollQueue.ts:82', 'insert into', e) - ); - } - } else { - let n: DiscordenoMessage | void; - // Determine if we are to send a GM roll or a normal roll - if (rq.modifiers.gmRoll) { - if (rq.apiRoll) { - n = await sendMessage(rq.api.channelId, { - content: rq.modifiers.apiWarn, - embeds: [pubEmbedDetails.embed], - }).catch(() => { - apiErroredOut = true; - rq.api.requestEvent.respondWith(stdResp.InternalServerError('Message failed to send - location 0.')); - }); - } else { - // Send the public embed to correct channel - rq.dd.m.edit({ embeds: [pubEmbedDetails.embed] }); - } + if (rq.apiRoll || (DEVMODE && config.logRolls)) { + // If enabled, log rolls so we can see what went wrong + dbClient + .execute(queries.insertRollLogCmd(rq.apiRoll ? 1 : 0, 1), [rq.originalCommand, returnmsg.errorCode, rq.apiRoll ? null : rq.dd.m.id]) + .catch((e) => utils.commonLoggers.dbError('rollQueue.ts:82', 'insert into', e)); + } + } else { + let n: DiscordenoMessage | void; + // Determine if we are to send a GM roll or a normal roll + if (rq.modifiers.gmRoll) { + if (rq.apiRoll) { + n = await sendMessage(rq.api.channelId, { + content: rq.modifiers.apiWarn, + embeds: [pubEmbedDetails.embed], + }).catch(() => { + apiErroredOut = true; + rq.api.requestEvent.respondWith(stdResp.InternalServerError('Message failed to send - location 0.')); + }); + } else { + // Send the public embed to correct channel + rq.dd.m.edit({ embeds: [pubEmbedDetails.embed] }); + } - if (!apiErroredOut) { - // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged - rq.modifiers.gms.forEach(async (gm) => { - log(LT.LOG, `Messaging GM ${gm}`); - // Attempt to DM the GM and send a warning if it could not DM a GM - await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), { - embeds: rq.modifiers.count ? [gmEmbedDetails.embed, countEmbed] : [gmEmbedDetails.embed], - }).then(async () => { - // Check if we need to attach a file and send it after the initial details sent - if (gmEmbedDetails.hasAttachment) { - await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), { - file: gmEmbedDetails.attachment, - }).catch(() => { - if (n && rq.apiRoll) { - n.reply(generateDMFailed(gm)); - } else { - rq.dd.message.reply(generateDMFailed(gm)); - } - }); - } - }).catch(() => { - if (rq.apiRoll && n) { - n.reply(generateDMFailed(gm)); - } else { - rq.dd.message.reply(generateDMFailed(gm)); - } - }); - }); - } - } else { - // Not a gm roll, so just send normal embed to correct channel - if (rq.apiRoll) { - n = await sendMessage(rq.api.channelId, { - content: rq.modifiers.apiWarn, - embeds: rq.modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed], - }).catch(() => { - apiErroredOut = true; - rq.api.requestEvent.respondWith(stdResp.InternalServerError('Message failed to send - location 1.')); - }); - } else { - n = await rq.dd.m.edit({ - embeds: rq.modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed], - }); - } + if (!apiErroredOut) { + // And message the full details to each of the GMs, alerting roller of every GM that could not be messaged + rq.modifiers.gms.forEach(async (gm) => { + log(LT.LOG, `Messaging GM ${gm}`); + // Attempt to DM the GM and send a warning if it could not DM a GM + await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), { + embeds: rq.modifiers.count ? [gmEmbedDetails.embed, countEmbed] : [gmEmbedDetails.embed], + }) + .then(async () => { + // Check if we need to attach a file and send it after the initial details sent + if (gmEmbedDetails.hasAttachment) { + await sendDirectMessage(BigInt(gm.substring(2, gm.length - 1)), { + file: gmEmbedDetails.attachment, + }).catch(() => { + if (n && rq.apiRoll) { + n.reply(generateDMFailed(gm)); + } else { + rq.dd.message.reply(generateDMFailed(gm)); + } + }); + } + }) + .catch(() => { + if (rq.apiRoll && n) { + n.reply(generateDMFailed(gm)); + } else { + rq.dd.message.reply(generateDMFailed(gm)); + } + }); + }); + } + } else { + // Not a gm roll, so just send normal embed to correct channel + if (rq.apiRoll) { + n = await sendMessage(rq.api.channelId, { + content: rq.modifiers.apiWarn, + embeds: rq.modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed], + }).catch(() => { + apiErroredOut = true; + rq.api.requestEvent.respondWith(stdResp.InternalServerError('Message failed to send - location 1.')); + }); + } else { + n = await rq.dd.m.edit({ + embeds: rq.modifiers.count ? [pubEmbedDetails.embed, countEmbed] : [pubEmbedDetails.embed], + }); + } - if (pubEmbedDetails.hasAttachment && n) { - // Attachment requires you to send a new message - n.reply({ - file: pubEmbedDetails.attachment, - }); - } - } + if (pubEmbedDetails.hasAttachment && n) { + // Attachment requires you to send a new message + n.reply({ + file: pubEmbedDetails.attachment, + }); + } + } - if (rq.apiRoll && !apiErroredOut) { - dbClient.execute(queries.insertRollLogCmd(1, 0), [rq.originalCommand, returnmsg.errorCode, n ? n.id : null]).catch((e) => utils.commonLoggers.dbError('rollQueue.ts:155', 'insert into', e)); + if (rq.apiRoll && !apiErroredOut) { + dbClient + .execute(queries.insertRollLogCmd(1, 0), [rq.originalCommand, returnmsg.errorCode, n ? n.id : null]) + .catch((e) => utils.commonLoggers.dbError('rollQueue.ts:155', 'insert into', e)); - rq.api.requestEvent.respondWith(stdResp.OK(JSON.stringify( - rq.modifiers.count - ? { - counts: countEmbed, - details: pubEmbedDetails, - } - : { - details: pubEmbedDetails, - }, - ))); - } - } - } catch (e) { - log(LT.ERROR, `Unddandled Error: ${JSON.stringify(e)}`); - if (rq.apiRoll && !apiErroredOut) { - rq.api.requestEvent.respondWith(stdResp.InternalServerError(JSON.stringify(e))); - } - } - }); + rq.api.requestEvent.respondWith( + stdResp.OK( + JSON.stringify( + rq.modifiers.count + ? { + counts: countEmbed, + details: pubEmbedDetails, + } + : { + details: pubEmbedDetails, + } + ) + ) + ); + } + } + } catch (e) { + log(LT.ERROR, `Unddandled Error: ${JSON.stringify(e)}`); + if (rq.apiRoll && !apiErroredOut) { + rq.api.requestEvent.respondWith(stdResp.InternalServerError(JSON.stringify(e))); + } + } + }); }; // Runs the roll or queues it depending on how many workers are currently running export const queueRoll = async (rq: QueuedRoll) => { - if (rq.apiRoll) { - handleRollWorker(rq); - } else if (!rollQueue.length && currentWorkers < config.limits.maxWorkers) { - handleRollWorker(rq); - } else { - rq.dd.m.edit({ - embeds: [{ - color: infoColor2, - title: `${config.name} currently has its hands full and has queued your roll.`, - description: `There are currently ${currentWorkers + rollQueue.length} rolls ahead of this roll. + if (rq.apiRoll) { + handleRollWorker(rq); + } else if (!rollQueue.length && currentWorkers < config.limits.maxWorkers) { + handleRollWorker(rq); + } else { + rq.dd.m + .edit({ + embeds: [ + { + color: infoColor2, + title: `${config.name} currently has its hands full and has queued your roll.`, + description: `There are currently ${currentWorkers + rollQueue.length} rolls ahead of this roll. The results for this roll will replace this message when it is done.`, - }], - }).catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:197', rq.dd.m, e)); - rollQueue.push(rq); - } + }, + ], + }) + .catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:197', rq.dd.m, e)); + rollQueue.push(rq); + } }; // Checks the queue constantly to make sure the queue stays empty setInterval(async () => { - log(LT.LOG, `Checking rollQueue for items, rollQueue length: ${rollQueue.length}, currentWorkers: ${currentWorkers}, config.limits.maxWorkers: ${config.limits.maxWorkers}`); - if (rollQueue.length && currentWorkers < config.limits.maxWorkers) { - const temp = rollQueue.shift(); - if (temp) { - temp.dd.m.edit(rollingEmbed).catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:208', temp.dd.m, e)); - handleRollWorker(temp); - } - } + log( + LT.LOG, + `Checking rollQueue for items, rollQueue length: ${rollQueue.length}, currentWorkers: ${currentWorkers}, config.limits.maxWorkers: ${config.limits.maxWorkers}` + ); + if (rollQueue.length && currentWorkers < config.limits.maxWorkers) { + const temp = rollQueue.shift(); + if (temp) { + temp.dd.m.edit(rollingEmbed).catch((e: Error) => utils.commonLoggers.messageEditError('rollQueue.ts:208', temp.dd.m, e)); + handleRollWorker(temp); + } + } }, 1000);