From 23708d152f1363499b326b3c26c7dffd2d7e6b8e Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Sun, 20 Mar 2022 20:59:33 +0000 Subject: [PATCH] More stuff --- .../commands/chartSearch/buildChartEmbed.ts | 2 +- bot/src/commands/help/help.ts | 10 ++ bot/src/database/mongo.ts | 6 +- bot/src/database/queries.ts | 2 +- .../{command => }/handleIsCommand.ts | 21 ++-- .../{selectMenu => }/handleIsSelectMenu.ts | 10 +- bot/src/main.ts | 8 +- bot/src/profile/buildProfileEmbed.ts | 2 +- bot/src/slashCommands/register.ts | 101 ++++++++++-------- bot/src/slashCommands/types.ts | 10 ++ 10 files changed, 97 insertions(+), 75 deletions(-) rename bot/src/interactionHandlers/{command => }/handleIsCommand.ts (56%) rename bot/src/interactionHandlers/{selectMenu => }/handleIsSelectMenu.ts (86%) diff --git a/bot/src/commands/chartSearch/buildChartEmbed.ts b/bot/src/commands/chartSearch/buildChartEmbed.ts index c141b1c02..b877bed34 100644 --- a/bot/src/commands/chartSearch/buildChartEmbed.ts +++ b/bot/src/commands/chartSearch/buildChartEmbed.ts @@ -8,7 +8,7 @@ import { import { Game, IDStrings, Playtypes, PublicUserDocument, UGSRatingsLookup } from "tachi-common"; import { PBScoreDocument } from "tachi-common/js/types"; import { LoggerLayers } from "../../data/data"; -import { validSelectCustomIdPrefaces } from "../../interactionHandlers/selectMenu/handleIsSelectMenu"; +import { validSelectCustomIdPrefaces } from "../../interactionHandlers/handleIsSelectMenu"; import { TachiServerV1Get } from "../../utils/fetch-tachi"; import { createLayeredLogger } from "../../utils/logger"; import { formatGameScoreRating, getGameImage } from "../../utils/utils"; diff --git a/bot/src/commands/help/help.ts b/bot/src/commands/help/help.ts index efce3b6ce..9f621d29a 100644 --- a/bot/src/commands/help/help.ts +++ b/bot/src/commands/help/help.ts @@ -1,5 +1,15 @@ +import { SlashCommandBuilder } from "@discordjs/builders"; import { CommandInteraction } from "discord.js"; +import { SlashCommand } from "../../slashCommands/types"; export const help = async (interaction: CommandInteraction): Promise => { await interaction.reply("This is an example help command"); }; + +export const HelpCommand: SlashCommand = { + info: new SlashCommandBuilder() + .setName("help") + .setDescription("Shows information about this bot.") + .toJSON(), + exec: help, +}; diff --git a/bot/src/database/mongo.ts b/bot/src/database/mongo.ts index c5038105a..119ce4892 100644 --- a/bot/src/database/mongo.ts +++ b/bot/src/database/mongo.ts @@ -2,14 +2,14 @@ import { Snowflake } from "discord.js/typings/index.js"; import monk from "monk"; import { integer } from "tachi-common"; import { LoggerLayers } from "../data/data"; -import { ProcessEnv } from "../setup"; +import { BotConfig } from "../setup"; import { createLayeredLogger } from "../utils/logger"; const logger = createLayeredLogger(LoggerLayers.database); -logger.info(`Connecting to ${ProcessEnv.MONGO_URL}...`); +logger.info(`Connecting to ${BotConfig.MONGO_URL}...`); -const monkDB = monk(ProcessEnv.MONGO_URL); +const monkDB = monk(BotConfig.MONGO_URL); monkDB .then(() => { diff --git a/bot/src/database/queries.ts b/bot/src/database/queries.ts index b27090e16..97aeb7bf3 100644 --- a/bot/src/database/queries.ts +++ b/bot/src/database/queries.ts @@ -5,7 +5,7 @@ import db, { DiscordUserMapDocument } from "./mongo"; const logger = createLayeredLogger(LoggerLayers.databaseQuery); export function GetUserForDiscordID(discordID: string): Promise { - logger.verbose(`Fetching linked user for DiscordID: ${discordID}`); + logger.verbose(`Fetching linked user with DiscordID: ${discordID}.`); return db.discordUserMap.findOne({ discordID: discordID }); } diff --git a/bot/src/interactionHandlers/command/handleIsCommand.ts b/bot/src/interactionHandlers/handleIsCommand.ts similarity index 56% rename from bot/src/interactionHandlers/command/handleIsCommand.ts rename to bot/src/interactionHandlers/handleIsCommand.ts index 40a9634cc..a9f666cdc 100644 --- a/bot/src/interactionHandlers/command/handleIsCommand.ts +++ b/bot/src/interactionHandlers/handleIsCommand.ts @@ -1,20 +1,11 @@ -import { CommandInteraction, Interaction } from "discord.js"; -import { LoggerLayers } from "../../data/data"; -import { DiscordUserMapDocument } from "../../database/mongo"; -import { GetUserForDiscordID } from "../../database/queries"; -import { slashCommands } from "../../slashCommands/register"; -import { createLayeredLogger } from "../../utils/logger"; +import { CommandInteraction } from "discord.js"; +import { LoggerLayers } from "../data/data"; +import { DiscordUserMapDocument } from "../database/mongo"; +import { SLASH_COMMANDS } from "../slashCommands/register"; +import { createLayeredLogger } from "../utils/logger"; const logger = createLayeredLogger(LoggerLayers.slashCommands); -// For performance's sake, create a lookup table here and use that to refer -// to commands. -const COMMAND_LOOKUP_TABLE = new Map(); - -for (const command of slashCommands) { - COMMAND_LOOKUP_TABLE.set(command.info.name, command); -} - /** * Handles incoming command requests by resolving the interaction to the command * it refers to, and calling it. @@ -28,7 +19,7 @@ export function handleIsCommand( requestingUser: DiscordUserMapDocument ) { try { - const command = COMMAND_LOOKUP_TABLE.get(interaction.commandName); + const command = SLASH_COMMANDS.get(interaction.commandName); if (!command) { throw new Error(`A command was requested that does not exist.`); diff --git a/bot/src/interactionHandlers/selectMenu/handleIsSelectMenu.ts b/bot/src/interactionHandlers/handleIsSelectMenu.ts similarity index 86% rename from bot/src/interactionHandlers/selectMenu/handleIsSelectMenu.ts rename to bot/src/interactionHandlers/handleIsSelectMenu.ts index cae4ef811..0f2c4d0b2 100644 --- a/bot/src/interactionHandlers/selectMenu/handleIsSelectMenu.ts +++ b/bot/src/interactionHandlers/handleIsSelectMenu.ts @@ -1,10 +1,10 @@ import { Interaction, SelectMenuInteraction } from "discord.js"; import { Game, Playtypes } from "tachi-common"; -import { buildChartEmbed } from "../../commands/chartSearch/buildChartEmbed"; -import { LoggerLayers } from "../../data/data"; -import { buildProfileIntractable } from "../../profile/buildProfileEmbed"; -import { createLayeredLogger } from "../../utils/logger"; -import { stringToSimpleGameType } from "../../utils/utils"; +import { buildChartEmbed } from "../commands/chartSearch/buildChartEmbed"; +import { LoggerLayers } from "../data/data"; +import { buildProfileIntractable } from "../profile/buildProfileEmbed"; +import { createLayeredLogger } from "../utils/logger"; +import { stringToSimpleGameType } from "../utils/utils"; const logger = createLayeredLogger(LoggerLayers.selectInteractionHandler); diff --git a/bot/src/main.ts b/bot/src/main.ts index 3571e89e6..8076a07b9 100644 --- a/bot/src/main.ts +++ b/bot/src/main.ts @@ -1,11 +1,11 @@ import { Client, CommandInteraction, Intents, SelectMenuInteraction } from "discord.js"; import { LoggerLayers, METALLIC_MIND_SPLASHES } from "./data/data"; import { GetUserForDiscordID } from "./database/queries"; -import { handleIsCommand } from "./interactionHandlers/command/handleIsCommand"; -import { handleIsSelectMenu } from "./interactionHandlers/selectMenu/handleIsSelectMenu"; +import { handleIsCommand } from "./interactionHandlers/handleIsCommand"; +import { handleIsSelectMenu } from "./interactionHandlers/handleIsSelectMenu"; import { app } from "./server/server"; import { BotConfig, ProcessEnv } from "./setup"; -import { registerSlashCommands } from "./slashCommands/register"; +import { RegisterSlashCommands } from "./slashCommands/register"; import { createLayeredLogger } from "./utils/logger"; import { RFA, TruncateString } from "./utils/misc"; import { initWatchHandler } from "./utils/utils"; @@ -84,7 +84,7 @@ We've sent you a DM with instructions on how to link your account.` }&permissions=8&scope=applications.commands%20bot` ); - await registerSlashCommands(client); + await RegisterSlashCommands(client); } catch (e) { logger.crit("Log in Failed:", e); process.exit(1); // screwed. diff --git a/bot/src/profile/buildProfileEmbed.ts b/bot/src/profile/buildProfileEmbed.ts index dcaceae48..f1c1ab1ad 100644 --- a/bot/src/profile/buildProfileEmbed.ts +++ b/bot/src/profile/buildProfileEmbed.ts @@ -8,7 +8,7 @@ import { import { Game, UserGameStats, IDStrings, PublicUserDocument, UGSRatingsLookup } from "tachi-common"; import { find } from "lodash"; import { LoggerLayers } from "../data/data"; -import { validSelectCustomIdPrefaces } from "../interactionHandlers/selectMenu/handleIsSelectMenu"; +import { validSelectCustomIdPrefaces } from "../interactionHandlers/handleIsSelectMenu"; import { TachiServerV1Get } from "../utils/fetch-tachi"; import { createLayeredLogger } from "../utils/logger"; import { diff --git a/bot/src/slashCommands/register.ts b/bot/src/slashCommands/register.ts index 400c8a8a1..60a980288 100644 --- a/bot/src/slashCommands/register.ts +++ b/bot/src/slashCommands/register.ts @@ -1,36 +1,25 @@ import { SlashCommandBuilder } from "@discordjs/builders"; import { REST } from "@discordjs/rest"; -import { APIApplicationCommandOption } from "discord-api-types"; import { Routes } from "discord-api-types/v9"; import { Client, CommandInteraction } from "discord.js"; import { searchForSong } from "../commands/chartSearch/chartSearch"; -import { getProfileByName } from "../profile/fetch"; -import { ProcessEnv } from "../setup"; +import { HelpCommand } from "../commands/help/help"; import { LoggerLayers } from "../data/data"; -import { help } from "../commands/help/help"; +import { getProfileByName } from "../profile/fetch"; +import { BotConfig, ProcessEnv } from "../setup"; import { createLayeredLogger } from "../utils/logger"; import { gamesToChoicesObject } from "../utils/utils"; -import { Command } from "./types"; +import { SlashCommand } from "./types"; const logger = createLayeredLogger(LoggerLayers.slashCommands); -export interface SlashCommand { - info: { - name: string; - description: string; - options: APIApplicationCommandOption[]; - }; - exec: Command; -} +export const SLASH_COMMANDS: Map = new Map( + Object.entries({ + help: HelpCommand, + }) +); -export const slashCommands: SlashCommand[] = [ - { - info: new SlashCommandBuilder() - .setName("help") - .setDescription("Shows information about this bot") - .toJSON(), - exec: , - }, +const unused = [ { info: new SlashCommandBuilder() .setName("profile") @@ -70,20 +59,27 @@ export const slashCommands: SlashCommand[] = [ const rest = new REST({ version: "9", -}).setToken(ProcessEnv.DISCORD_TOKEN); +}).setToken(BotConfig.DISCORD_TOKEN); -export const registerSlashCommands = async (client: Client): Promise => { +/** + * Register our slash commands. If in prod, these + * @param client + */ +export async function RegisterSlashCommands(client: Client): Promise { try { - if (ProcessEnv.NODE_ENV === "production") { - logger.info("Registering global slash commands"); + const commandsArray = Object.values(SLASH_COMMANDS); + + if (ProcessEnv.nodeEnv === "production") { + logger.info("Registering global slash commands."); await rest.put(Routes.applicationCommands(client.application!.id), { - body: slashCommands.map((command) => command.info), + body: commandsArray.map((command) => command.info), }); } else { - logger.info("Registering guild slash commands"); + logger.info("Registering guild slash commands."); + + UnregisterAllCommands(client); - await tidyOldGuildCommands(client); if (process.env.DEV_SERVER_ID) { await rest.put( Routes.applicationGuildCommands( @@ -91,32 +87,47 @@ export const registerSlashCommands = async (client: Client): Promise => { process.env.DEV_SERVER_ID ), { - body: slashCommands.map((command) => command.info), + body: commandsArray.map((command) => command.info), } ); } } - logger.info("Successfully registered slash commands"); - } catch (e) { - logger.error("Failed to register slash commands", e); + logger.info("Successfully registered slash commands."); + } catch (err) { + logger.error("Failed to register slash commands.", err); + throw err; } -}; +} -export const tidyOldGuildCommands = async (client: Client): Promise => { +/** + * Unregister all the commmands we have. + */ +export async function UnregisterAllCommands(client: Client): Promise { try { - logger.info("Tidying old guild slash commands"); + logger.info("Tidying old guild slash commands."); const guilds = client.guilds.cache; - guilds.forEach((guild) => { - const commands = guild.commands.cache; - commands.forEach((command) => { - command.delete(); - }); - }); + // discord.js doesn't use arrays because those aren't cool anymore + // so we have to discard the left side of this. + // They use collections, which inherit from ES6's Map. Ah well. - logger.info("Successfully tidied old guild slash commands"); - } catch (e) { - logger.error("Failed to tidy old guild slash commands"); + const promises = []; + for (const [, guild] of guilds) { + const commands = guild.commands.cache; + + for (const [, command] of commands) { + promises.push(command.delete()); + } + } + + // parallelise waiting for these to be deleted. + await Promise.all(promises); + + logger.info("Successfully tidied old guild slash commands."); + } catch (err) { + logger.error("Failed to tidy old guild slash commands.", err); + + throw err; } -}; +} diff --git a/bot/src/slashCommands/types.ts b/bot/src/slashCommands/types.ts index 768cdaef9..e3d336fdb 100644 --- a/bot/src/slashCommands/types.ts +++ b/bot/src/slashCommands/types.ts @@ -1,3 +1,4 @@ +import { APIApplicationCommandOption } from "discord-api-types"; import { CommandInteraction } from "discord.js"; import { DiscordUserMapDocument } from "../database/mongo"; @@ -5,3 +6,12 @@ export type Command = ( interaction: CommandInteraction, requestingUser: DiscordUserMapDocument ) => Promise; + +export interface SlashCommand { + info: { + name: string; + description: string; + options: APIApplicationCommandOption[]; + }; + exec: Command; +}