diff --git a/src/commands/command.ts b/src/commands/command.ts index 28d8536..6e76a9f 100644 --- a/src/commands/command.ts +++ b/src/commands/command.ts @@ -4,7 +4,6 @@ type Command = { name: string; description: string; aliases?: string[]; - draft: boolean; options?: ApplicationCommandOptions[]; type?: 1; diff --git a/src/commands/index.ts b/src/commands/index.ts index 5f49dff..f177d28 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -2,10 +2,12 @@ import Command from "./command"; import ping from "./ping"; import remind from "./remind"; +import reminders from "./reminders"; const commands: { [key: string]: Command } = { ping, - remind + remind, + reminders }; export default commands; diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 3d97dd6..e03f161 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -3,7 +3,6 @@ import Command from "./command"; const ping: Command = { name: "ping", description: "Ping the bot.", - draft: false, command: async (interaction) => { await interaction.createMessage("Pong!"); } diff --git a/src/commands/remind.ts b/src/commands/remind.ts index d4be978..6c5dfca 100644 --- a/src/commands/remind.ts +++ b/src/commands/remind.ts @@ -3,14 +3,19 @@ import Command from "./command"; import db from "../things/db"; import bot from "../things/bot"; -import { Constants } from "eris"; +import { Constants, InteractionDataOptionsString } from "eris"; import parse from "parse-duration"; import logger from "../things/logger"; +import { + Optional, + unravelOption, + unravelOptionalOption +} from "../utils/options"; + const remind: Command = { name: "remind", description: "Reminds you to do something.", - draft: false, options: [ { name: "when", @@ -25,18 +30,19 @@ const remind: Command = { } ], command: async (interaction) => { - const whenOption = interaction.data.options?.find((x) => x.name === "when"); - const whatOption = interaction.data.options?.find((x) => x.name === "what"); - - let what = null; - if (whatOption?.type === Constants.ApplicationCommandOptionTypes.STRING) - what = whatOption.value; - - // fucking ts - if (whenOption?.type != Constants.ApplicationCommandOptionTypes.STRING) - return; + // i love abusing types + const options = interaction.data.options!; + const whenOption = unravelOption( + "when", + options + ); + const whatOption = unravelOptionalOption< + Optional + >("what", options); const when = parse(whenOption.value); + const what = whatOption?.value; + if (when === null) { await interaction.createMessage({ content: diff --git a/src/commands/reminders.ts b/src/commands/reminders.ts new file mode 100644 index 0000000..e3dd4be --- /dev/null +++ b/src/commands/reminders.ts @@ -0,0 +1,125 @@ +import Command from "./command"; + +import db from "../things/db"; + +import { + CommandInteraction, + Constants, + InteractionDataOptionsNumber, + InteractionDataOptionsSubCommand +} from "eris"; + +import { + Optional, + unravelOption, + unravelOptionalOption +} from "../utils/options"; + +async function listReminders(interaction: CommandInteraction) { + const userReminders = await db.reminder.findMany({ + where: { + user: (interaction.member || interaction.user)!.id + } + }); + + console.log(userReminders); + + await interaction.createMessage({ + embeds: [ + { + title: "Your reminders", + description: userReminders + .map((x) => { + const timestamp = ``; + const id = `ID: \`${x.id}\``; + const message = x.message || "No message set"; + + return `${timestamp} - ${id} - ${message}`; + }) + .join("\n") + } + ] + }); +} + +async function deleteReminder(interaction: CommandInteraction, id: number) { + const reminder = await db.reminder.findUnique({ + where: { + id + } + }); + + if (!reminder) { + await interaction.createMessage({ + content: "That reminder doesn't exist.", + flags: Constants.MessageFlags.EPHEMERAL + }); + return; + } + + const user = (interaction.member || interaction.user)!.id; + if (reminder.user !== user) { + await interaction.createMessage({ + content: "You can't delete someone else's reminder.", + flags: Constants.MessageFlags.EPHEMERAL + }); + return; + } + + await db.reminder.delete({ + where: { + id + } + }); + + await interaction.createMessage({ + content: "Reminder deleted.", + flags: Constants.MessageFlags.EPHEMERAL + }); +} + +const reminders: Command = { + name: "reminders", + description: "List your set reminders.", + options: [ + { + name: "list", + description: "List all your reminders.", + type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND + }, + { + name: "delete", + description: "Delete a reminder.", + type: Constants.ApplicationCommandOptionTypes.SUB_COMMAND, + options: [ + { + name: "id", + description: "The ID of the reminder to delete.", + required: true, + type: Constants.ApplicationCommandOptionTypes.NUMBER + } + ] + } + ], + command: async (interaction) => { + const options = interaction.data.options!; + const listOption = unravelOptionalOption< + Optional + >("list", options); + const deleteOption = unravelOptionalOption< + Optional + >("delete", options); + + if (listOption !== undefined) await listReminders(interaction); + if (deleteOption !== undefined) { + const id = unravelOption( + "id", + deleteOption.options! + ); + + await deleteReminder(interaction, id.value); + } + } +}; + +export default reminders; diff --git a/src/index.ts b/src/index.ts index d174d8d..add9c87 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,29 +6,26 @@ import commands from "./commands/index"; import Eris, { Constants } from "eris"; bot.on("ready", () => { - for (const command of Object.values(commands)) { - if (command.draft) { - logger.info(`Loading draft command ${command.name}`); + const cmds = Object.values(commands).map((x) => { + return { + name: x.name, + description: x.description, + type: Constants.ApplicationCommandTypes.CHAT_INPUT, + options: x.options + }; + }); - for (const guild of config.testGuilds) { - logger.info(`Loading draft command ${command.name} for guild ${guild}`); + if (process.env["NODE_ENV"] === "production") { + logger.info(`Loading ${cmds.length} commands in global mode...`); - bot.createGuildCommand(guild, { - name: command.name, - description: command.description, - type: Constants.ApplicationCommandTypes.CHAT_INPUT, - options: command.options - }); - } - } else { - logger.info(`Loading command ${command.name}`); + bot.bulkEditCommands(cmds); + } else { + logger.info(`Loading ${cmds.length} commands in test mode...`); - bot.createCommand({ - name: command.name, - description: command.description, - type: Constants.ApplicationCommandTypes.CHAT_INPUT, - options: command.options - }); + for (const guild of config.testGuilds) { + logger.info(`Loading commands in guild ${guild}...`); + + bot.bulkEditGuildCommands(guild, cmds); } } @@ -49,6 +46,11 @@ bot.on("interactionCreate", async (interaction) => { { err }, `Error running command ${commandName} for interaction ${interaction.id}` ); + + await interaction.createMessage({ + content: ":warn: Something went wrong running this command.", + flags: Constants.MessageFlags.EPHEMERAL + }); } } else { logger.warn(`Unhandled command ${commandName}?`); diff --git a/src/utils/options.ts b/src/utils/options.ts new file mode 100644 index 0000000..989671b --- /dev/null +++ b/src/utils/options.ts @@ -0,0 +1,16 @@ +import { InteractionDataOptions, InteractionDataOptionsSubCommand } from "eris"; + +export type Optional = T | undefined; + +export function unravelOption( + name: string, + options: InteractionDataOptions[] +): T { + return options.find((x) => x.name === name) as T; +} + +export function unravelOptionalOption< + T extends Optional +>(name: string, options: InteractionDataOptions[]): T { + return options.find((x) => x.name === name) as T; +}