diff --git a/config.example.ts b/config.example.ts index e3f5ee6..c655cfa 100644 --- a/config.example.ts +++ b/config.example.ts @@ -12,6 +12,10 @@ export const config = { 'password': '', // Password for the account, user account may need to be authenticated with the "Standard" Authentication Type if this does not work out of the box 'name': '', // Name of the database Schema to use for the bot }, + 'link': { // Links to various sites + 'sourceCode': 'https://github.com/Burn-E99/GroupUp', // Link to the repository + 'supportServer': '', // Invite link to the Discord support server + }, 'logChannel': 'the_log_channel', // Discord channel ID where the bot should put startup messages and other error messages needed 'reportChannel': 'the_report_channel', // Discord channel ID where reports will be sent when using the built-in report command 'devServer': 'the_dev_server', // Discord guild ID where testing of indev features/commands will be handled, used in conjuction with the DEVMODE bool in mod.ts diff --git a/deps.ts b/deps.ts index 5c937fa..e0d1204 100644 --- a/deps.ts +++ b/deps.ts @@ -7,8 +7,36 @@ export const botId = getBotIdFromToken(LOCALMODE ? config.localToken : config.to export { enableCachePlugin, enableCacheSweepers } from 'https://deno.land/x/discordeno@17.0.1/plugins/cache/mod.ts'; export type { BotWithCache } from 'https://deno.land/x/discordeno@17.0.1/plugins/cache/mod.ts'; -export { ActivityTypes, createBot, editBotMember, editBotStatus, getBotIdFromToken, Intents, sendInteractionResponse, sendMessage, startBot } from 'https://deno.land/x/discordeno@17.0.1/mod.ts'; -export type { ActionRow, Bot, ButtonComponent, CreateMessage, Embed, EventHandlers, Guild, Interaction, Message } from 'https://deno.land/x/discordeno@17.0.1/mod.ts'; +export { + ActivityTypes, + ApplicationCommandFlags, + ApplicationCommandTypes, + createBot, + editBotMember, + editBotStatus, + getBotIdFromToken, + Intents, + InteractionResponseTypes, + sendInteractionResponse, + sendMessage, + startBot, +} from 'https://deno.land/x/discordeno@17.0.1/mod.ts'; +export type { + ActionRow, + ApplicationCommand, + ApplicationCommandOption, + Bot, + ButtonComponent, + CreateApplicationCommand, + CreateMessage, + Embed, + EventHandlers, + Guild, + Interaction, + MakeRequired, + Message, + PermissionStrings, +} from 'https://deno.land/x/discordeno@17.0.1/mod.ts'; export { Client } from 'https://deno.land/x/mysql@v2.11.0/mod.ts'; diff --git a/mod.ts b/mod.ts index 478c0ff..bd99686 100644 --- a/mod.ts +++ b/mod.ts @@ -2,6 +2,7 @@ import config from './config.ts'; import { DEBUG, LOCALMODE } from './flags.ts'; import { createBot, enableCachePlugin, enableCacheSweepers, initLog, Intents, startBot } from './deps.ts'; import { events } from './src/events.ts'; +import { createSlashCommands } from './src/commands/_index.ts'; // Initialize logging client with folder to use for logs, needs --allow-write set on Deno startup initLog('logs', DEBUG); @@ -15,4 +16,6 @@ const bot = enableCachePlugin(createBot({ enableCacheSweepers(bot); // Start the bot -startBot(bot); +await startBot(bot); + +await createSlashCommands(bot); diff --git a/src/commandUtils.ts b/src/commandUtils.ts index bde9ce3..47ff881 100644 --- a/src/commandUtils.ts +++ b/src/commandUtils.ts @@ -1,4 +1,6 @@ +import { ApplicationCommandFlags } from '../deps.ts'; import config from '../config.ts'; +import { lfgChannels } from './db.ts'; export const failColor = 0xe71212; export const warnColor = 0xe38f28; @@ -7,24 +9,14 @@ export const infoColor1 = 0x313bf9; export const infoColor2 = 0x6805e9; export const getRandomStatus = (guildCount: number): string => { - let status = ''; - switch (Math.floor((Math.random() * 5) + 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; - case 4: - status = 'Mention me to check my prefix!'; - break; - default: - status = `Running LFGs in ${guildCount} servers`; - break; - } - - return status; + const statuses = [ + `Running V${config.version}`, + `${config.prefix}info to learn more`, + `Running LFGs in ${guildCount} servers`, + ]; + return statuses[Math.floor((Math.random() * statuses.length) + 1)]; +}; + +export const isLFGChannel = (channelId: bigint) => { + return (lfgChannels.includes(channelId) || channelId === 0n) ? ApplicationCommandFlags.Ephemeral : undefined; }; diff --git a/src/commands/_index.ts b/src/commands/_index.ts new file mode 100644 index 0000000..446b6fe --- /dev/null +++ b/src/commands/_index.ts @@ -0,0 +1,23 @@ +import { Bot, CreateApplicationCommand, log, LT, MakeRequired } from '../../deps.ts'; +import { Commands } from '../types/commandTypes.ts'; +import utils from '../utils.ts'; + +import info from './info.ts'; + +export const commands: Array = [info]; + +export const createSlashCommands = async (bot: Bot) => { + const globalCommands: MakeRequired[] = []; + for (const command of commands) { + globalCommands.push({ + name: command.details.name, + description: command.details.description, + type: command.details.type, + options: command.details.options ? command.details.options : undefined, + dmPermission: command.details.dmPermission ? command.details.dmPermission : false, + defaultMemberPermissions: command.details.defaultMemberPermissions ? command.details.defaultMemberPermissions : undefined, + }); + } + + await bot.helpers.upsertGlobalApplicationCommands(globalCommands).catch((errMsg) => log(LT.ERROR, `Failed to upsert application commands | ${utils.jsonStringifyBig(errMsg)}`)); +}; diff --git a/src/commands/info.ts b/src/commands/info.ts new file mode 100644 index 0000000..dbc872e --- /dev/null +++ b/src/commands/info.ts @@ -0,0 +1,41 @@ +import config from '../../config.ts'; +import { ApplicationCommandTypes, Bot, Interaction, InteractionResponseTypes } from '../../deps.ts'; +import { infoColor2, isLFGChannel } from '../commandUtils.ts'; +import { CommandDetails } from '../types/commandTypes.ts'; +import utils from '../utils.ts'; + +const details: CommandDetails = { + name: 'info', + description: `Information about ${config.name} and its developer`, + type: ApplicationCommandTypes.ChatInput, +}; + +const execute = (bot: Bot, interaction: Interaction) => { + bot.helpers.sendInteractionResponse( + interaction.id, + interaction.token, + { + type: InteractionResponseTypes.ChannelMessageWithSource, + data: { + flags: isLFGChannel(interaction.channelId || 0n), + embeds: [{ + color: infoColor2, + title: `${config.name}, the LFG bot`, + description: `${config.name} is developed by Ean AKA Burn_E99. +Want to check out my source code? Check it out [here](${config.links.sourceCode}). +Need help with this bot? Join my support server [here](${config.links.supportServer}). + +Ran into a bug? Report it to my developers using \`/report [issue description]\`.`, + footer: { + text: `Current Version: ${config.version}`, + }, + }], + }, + }, + ).catch((e: Error) => utils.commonLoggers.interactionSendError('info.ts', interaction, e)); +}; + +export default { + details, + execute, +}; diff --git a/src/db.ts b/src/db.ts index 099cc2a..94e0077 100644 --- a/src/db.ts +++ b/src/db.ts @@ -13,3 +13,5 @@ export const dbClient = await new Client().connect({ export const queries = { callIncCnt: (cmdName: string) => `CALL INC_CNT("${cmdName}");`, }; + +export const lfgChannels: Array = [1055568692697649232n]; diff --git a/src/events.ts b/src/events.ts index 9361475..22059b8 100644 --- a/src/events.ts +++ b/src/events.ts @@ -1,8 +1,5 @@ import { DEVMODE } from '../flags.ts'; -import { - // Discordeno deps - EventHandlers, -} from '../deps.ts'; +import { EventHandlers } from '../deps.ts'; import eventHandlers from './events/_index.ts'; export const events: Partial = {}; @@ -11,6 +8,7 @@ events.ready = eventHandlers.ready; events.guildCreate = eventHandlers.guildCreate; events.guildDelete = eventHandlers.guildDelete; events.messageCreate = eventHandlers.messageCreate; +events.interactionCreate = eventHandlers.interactionCreate; if (DEVMODE) { events.debug = eventHandlers.debug; diff --git a/src/events/_index.ts b/src/events/_index.ts index 2bdf3db..d3f4bd9 100644 --- a/src/events/_index.ts +++ b/src/events/_index.ts @@ -3,6 +3,7 @@ import { guildCreate } from './guildCreate.ts'; import { guildDelete } from './guildDelete.ts'; import { debug } from './debug.ts'; import { messageCreate } from './messageCreate.ts'; +import { interactionCreate } from './interactionCreate.ts'; export default { ready, @@ -10,4 +11,5 @@ export default { guildDelete, debug, messageCreate, + interactionCreate, }; diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts new file mode 100644 index 0000000..8c802ce --- /dev/null +++ b/src/events/interactionCreate.ts @@ -0,0 +1,16 @@ +import { Bot, BotWithCache, Interaction } from '../../deps.ts'; +import { commands } from '../commands/_index.ts'; + +const commandNames: Array = commands.map((command) => command.details.name); + +export const interactionCreate = (rawBot: Bot, interaction: Interaction) => { + const bot = rawBot as BotWithCache; + if (interaction.data && interaction.id) { + if (interaction.data.name) { + if (commandNames.includes(interaction.data.name)) { + const cmdIdx = commandNames.indexOf(interaction.data.name); + commands[cmdIdx].execute(bot, interaction); + } + } + } +}; diff --git a/src/types/commandTypes.ts b/src/types/commandTypes.ts new file mode 100644 index 0000000..65024d4 --- /dev/null +++ b/src/types/commandTypes.ts @@ -0,0 +1,15 @@ +import { ApplicationCommandOption, ApplicationCommandTypes, PermissionStrings } from '../../deps.ts'; + +export type CommandDetails = { + name: string; + description: string; + type: ApplicationCommandTypes; + options?: ApplicationCommandOption[]; + dmPermission?: boolean; + defaultMemberPermissions?: PermissionStrings[]; +}; + +export type Commands = { + details: CommandDetails; + execute: Function; +}; diff --git a/src/utils.ts b/src/utils.ts index aaa8964..ac26a42 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,12 @@ -import { - // Log4Deno deps - log, - LT, - // Discordeno deps - Message, -} from '../deps.ts'; +import { Interaction, log, LT, Message } from '../deps.ts'; const jsonStringifyBig = (input: any) => { return JSON.stringify(input, (_key, value) => typeof value === 'bigint' ? value.toString() + 'n' : value); }; const genericLogger = (level: LT, message: string) => log(level, message); +const interactionSendError = (location: string, interaction: Interaction | string, err: Error) => + genericLogger(LT.ERROR, `${location} | Failed to respond to interaction: ${jsonStringifyBig(interaction)} | Error: ${err.name} - ${err.message}`); const messageEditError = (location: string, message: Message | string, err: Error) => genericLogger(LT.ERROR, `${location} | Failed to edit message: ${jsonStringifyBig(message)} | Error: ${err.name} - ${err.message}`); const messageGetError = (location: string, message: Message | string, err: Error) => @@ -28,6 +24,7 @@ const dbError = (location: string, type: string, err: Error) => genericLogger(LT export default { commonLoggers: { dbError, + interactionSendError, messageGetError, messageEditError, messageSendError,