diff --git a/deno.json b/deno.json index 78ac82a..db76459 100644 --- a/deno.json +++ b/deno.json @@ -34,7 +34,9 @@ "db/": "./src/db/", "embeds/": "./src/embeds/", "endpoints/": "./src/endpoints/", + "events/": "./src/events/", "utils/": "./src/utils/", - "src/api.ts": "./src/api.ts" + "src/api.ts": "./src/api.ts", + "src/events.ts": "./src/events.ts" } } \ No newline at end of file diff --git a/mod.ts b/mod.ts index 260a62e..b113b80 100644 --- a/mod.ts +++ b/mod.ts @@ -3,22 +3,15 @@ * * December 21, 2020 */ -import { botId, cache, DiscordActivityTypes, DiscordenoGuild, DiscordenoMessage, editBotNickname, editBotStatus, Intents, sendMessage, startBot } from '@discordeno'; -import { initLog, log, LogTypes as LT } from '@Log4Deno'; +import { Intents, startBot } from '@discordeno'; +import { initLog } from '@Log4Deno'; import config from '~config'; import { DEBUG, DEVMODE, LOCALMODE } from '~flags'; -import commands from 'commands/_index.ts'; - -import dbClient from 'db/client.ts'; -import { ignoreList } from 'db/common.ts'; - -import { successColor, warnColor } from 'embeds/colors.ts'; - import api from 'src/api.ts'; +import eventHandlers from 'src/events.ts'; -import intervals from 'utils/intervals.ts'; import utils from 'utils/utils.ts'; // Extend the BigInt prototype to support JSON.stringify @@ -37,295 +30,7 @@ initLog('logs', DEBUG); 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', - }); - - // 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 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); - - // 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, - raw: DEVMODE ? (dMsg) => log(LT.LOG, `Raw 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 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; - } - - 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(); - - // 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 're': - // [[report or [[re (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; - case 'roll': - case 'r': - commands.roll(message, args, args.join('')); - 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('')}`.includes(config.postfix)) { - // [[roll]] - // Dice rolling commence! - commands.roll(message, args, command); - } else if (command) { - // [[emoji or [[emoji-alias - // Check if the unhandled command is an emoji request - commands.emoji(message, command); - } - break; - } - }, - }, + eventHandlers, }); // Start up the command prompt for debug usage diff --git a/src/events.ts b/src/events.ts new file mode 100644 index 0000000..a219067 --- /dev/null +++ b/src/events.ts @@ -0,0 +1,24 @@ +import { EventHandlers } from '@discordeno'; + +import { DEVMODE } from '~flags'; + +import { debugHandler } from 'events/debug.ts'; +import { guildCreateHandler } from 'events/guildCreate.ts'; +import { guildDeleteHandler } from 'events/guildDelete.ts'; +import { messageCreateHandler } from 'events/messageCreate.ts'; +import { readyHandler } from 'events/ready.ts'; +import { rawHandler } from 'events/raw.ts'; + +const eventHandlers: Partial = {}; + +eventHandlers.guildCreate = guildCreateHandler; +eventHandlers.guildDelete = guildDeleteHandler; +eventHandlers.messageCreate = messageCreateHandler; +eventHandlers.ready = readyHandler; + +if (DEVMODE) { + eventHandlers.debug = debugHandler; + eventHandlers.raw = rawHandler; +} + +export default eventHandlers; diff --git a/src/events/debug.ts b/src/events/debug.ts new file mode 100644 index 0000000..7da829a --- /dev/null +++ b/src/events/debug.ts @@ -0,0 +1,4 @@ +import { DebugArg } from '@discordeno'; +import { log, LogTypes as LT } from '@Log4Deno'; + +export const debugHandler = (dMsg: string | DebugArg) => log(LT.LOG, `Debug Message | ${JSON.stringify(dMsg)}`); diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts new file mode 100644 index 0000000..50286bb --- /dev/null +++ b/src/events/guildCreate.ts @@ -0,0 +1,37 @@ +import { DiscordenoGuild, sendMessage } from '@discordeno'; +import { log, LogTypes as LT } from '@Log4Deno'; + +import config from '~config'; + +import { successColor } from 'embeds/colors.ts'; + +import utils from 'utils/utils.ts'; + +export const guildCreateHandler = (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)); +}; diff --git a/src/events/guildDelete.ts b/src/events/guildDelete.ts new file mode 100644 index 0000000..027956a --- /dev/null +++ b/src/events/guildDelete.ts @@ -0,0 +1,42 @@ +import { DiscordenoGuild, sendMessage } from '@discordeno'; +import { log, LogTypes as LT } from '@Log4Deno'; + +import config from '~config'; + +import dbClient from 'db/client.ts'; + +import { warnColor } from 'embeds/colors.ts'; + +import utils from 'utils/utils.ts'; + +export const guildDeleteHandler = (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)); +}; diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts new file mode 100644 index 0000000..6237e2b --- /dev/null +++ b/src/events/messageCreate.ts @@ -0,0 +1,151 @@ +import { botId, DiscordenoMessage } from '@discordeno'; +import { log, LogTypes as LT } from '@Log4Deno'; + +import config from '~config'; + +import commands from 'commands/_index.ts'; + +import { ignoreList } from 'db/common.ts'; + +export const messageCreateHandler = (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 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; + } + + 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(); + + // 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 're': + // [[report or [[re (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; + case 'roll': + case 'r': + commands.roll(message, args, args.join('')); + 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('')}`.includes(config.postfix)) { + // [[roll]] + // Dice rolling commence! + commands.roll(message, args, command); + } else if (command) { + // [[emoji or [[emoji-alias + // Check if the unhandled command is an emoji request + commands.emoji(message, command); + } + break; + } +}; diff --git a/src/events/raw.ts b/src/events/raw.ts new file mode 100644 index 0000000..415c896 --- /dev/null +++ b/src/events/raw.ts @@ -0,0 +1,4 @@ +import { GatewayPayload } from '@discordeno'; +import { log, LogTypes as LT } from '@Log4Deno'; + +export const rawHandler = (dMsg: GatewayPayload) => log(LT.LOG, `Raw Debug Message | ${JSON.stringify(dMsg)}`); diff --git a/src/events/ready.ts b/src/events/ready.ts new file mode 100644 index 0000000..b358248 --- /dev/null +++ b/src/events/ready.ts @@ -0,0 +1,95 @@ +import { botId, cache, DiscordActivityTypes, editBotNickname, editBotStatus, sendMessage } from '@discordeno'; +import { log, LogTypes as LT } from '@Log4Deno'; + +import config from '~config'; +import { LOCALMODE } from '~flags'; + +import { successColor } from 'embeds/colors.ts'; + +import intervals from 'utils/intervals.ts'; +import utils from 'utils/utils.ts'; + +export const readyHandler = () => { + 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 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 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); +};