diff --git a/bin/delete.js b/bin/delete.js new file mode 100644 index 0000000..89398ea --- /dev/null +++ b/bin/delete.js @@ -0,0 +1,19 @@ +import { logger } from "@discordeno/utils"; + +import REST from "../src/lib/handlers/RESTHandler"; +import * as InteractionHandler from "../src/lib/handlers/InteractionHandler"; +import * as CommandHandler from "../src/lib/handlers/CommandHandler"; +await InteractionHandler.load(); + +const registered = await REST.getGlobalApplicationCommands(); + +for (let { id, name, type } of registered) { + const cmd = CommandHandler.commands.get(name); + if (cmd && cmd.type == type) continue; + + await REST.deleteGlobalApplicationCommand(id); + + logger.info(`Deleted ${name}(${id})`); +} + +process.exit(); diff --git a/bin/list.js b/bin/list.js new file mode 100644 index 0000000..ec4efee --- /dev/null +++ b/bin/list.js @@ -0,0 +1,20 @@ +import { logger } from "@discordeno/utils"; + +import REST from "../src/lib/handlers/RESTHandler"; +const registered = await REST.getGlobalApplicationCommands(); + +const TYPES = ["CHAT_INPUT", "USER", "MESSAGE"]; + +logger.info("┌" + "-".repeat(58) + "┐"); +logger.info("| " + "ID".padEnd(24, " ") + " | " + "NAME".padEnd(16, " ") + " | " + "TYPE".padEnd(10, " ") + " |"); +logger.info("├" + "-".repeat(58) + "┤"); + +for (let { id, name, type } of registered) { + logger.info( + "| " + id.padEnd(24, " ") + " | " + name.padEnd(16, " ") + " | " + TYPES[type - 1].padEnd(10, " ") + " |", + ); +} + +logger.info("└" + "-".repeat(58) + "┘"); + +process.exit(); diff --git a/bin/update.js b/bin/update.js new file mode 100644 index 0000000..dbdc4b4 --- /dev/null +++ b/bin/update.js @@ -0,0 +1,28 @@ +import { logger } from "@discordeno/utils"; + +import REST from "../src/lib/handlers/RESTHandler"; +import * as InteractionHandler from "../src/lib/handlers/InteractionHandler"; +import * as CommandHandler from "../src/lib/handlers/CommandHandler"; +await InteractionHandler.load(); + +for (let [key, value] of CommandHandler.commands.entries()) { + const response = await fetch(`https://discord.com/api/v10/applications/${REST.applicationId}/commands`, { + method: "POST", + headers: { "Content-Type": "application/json", Authorization: "Bot " + process.env.DISCORD_TOKEN }, + body: JSON.stringify({ + name: value.name, + type: value.type, + description: value?.description || "", + options: value?.options, + nsfw: value?.nsfw, + integration_types: [0, 1], + contexts: [0, 1, 2], + }), + }); + + const command = await response.json(); + + logger.info(`Registered ${command.name}(${command.id})`); +} + +process.exit(); diff --git a/package.json b/package.json index 23805b6..aed6a91 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,22 @@ { - "name": "bot-template", - "module": "src/index.ts", - "devDependencies": { - "@discordeno/types": "^19.0.0-next.d81b28a", - "@types/bun": "latest" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "type": "module", - "dependencies": { - "@discordeno/bot": "^19.0.0-next.d81b28a", - "@discordeno/rest": "^19.0.0-next.d81b28a" - } + "name": "bot-template", + "module": "src/index.ts", + "devDependencies": { + "@discordeno/types": "^19.0.0-next.d81b28a", + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "type": "module", + "dependencies": { + "@discordeno/bot": "^19.0.0-next.d81b28a", + "@discordeno/rest": "^19.0.0-next.d81b28a" + }, + "scripts": { + "dev": "bun run --watch src/index.ts", + "bot:list": "bun run bin/list.js", + "bot:delete": "bun run bin/delete.js", + "bot:update": "bun run bin/update.js" + } } diff --git a/src/index.ts b/src/index.ts index 5618fa9..73dc34b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { createBot, Intents } from "@discordeno/bot"; +import { createBot, Intents, logger } from "@discordeno/bot"; import * as interactionHandler from "./lib/handlers/InteractionHandler"; const client = createBot({ @@ -6,7 +6,7 @@ const client = createBot({ intents: Intents.Guilds, events: { ready(payload, rawPayload) { - console.log("Logged in"); + logger.info(`Logged into ${payload.user.username}`); }, interactionCreate: interactionHandler.handle, }, diff --git a/src/interactions/ping.ts b/src/interactions/default.ts similarity index 66% rename from src/interactions/ping.ts rename to src/interactions/default.ts index e087ade..cfb2550 100644 --- a/src/interactions/ping.ts +++ b/src/interactions/default.ts @@ -1,13 +1,13 @@ import type { Command } from "../lib/types/command"; -import { MessageComponentTypes, TextStyles, type Interaction } from "@discordeno/bot"; +import { MessageComponentTypes, TextStyles } from "@discordeno/bot"; import SlashCommand from "../lib/classes/SlashCommand"; import Modal from "../lib/classes/Modal"; import { collectModal } from "../lib/handlers/ModalHandler"; export default class extends SlashCommand implements Command { - static name = "ping"; - static description = "Show a test modal"; - static options = []; + name = "default"; + description = "Show a test modal"; + options = []; constructor() { super(); @@ -20,9 +20,9 @@ export default class extends SlashCommand implements Command { components: [ { type: MessageComponentTypes.InputText, - customId: "dope", + customId: "default", style: TextStyles.Short, - label: "dope", + label: "default", }, ], }, @@ -31,11 +31,9 @@ export default class extends SlashCommand implements Command { collectModal(modal) .then(({ interaction, values }) => { if (!interaction.data?.components) return; - interaction.respond("dope: " + values.get("dope")); + interaction.respond("default: " + values.get("default")); }) - .catch((_) => { - // whatever bro dont respond to my modal 🙄 - }); + .catch((_) => {}); return modal; } diff --git a/src/interactions/send.ts b/src/interactions/send.ts new file mode 100644 index 0000000..ae32921 --- /dev/null +++ b/src/interactions/send.ts @@ -0,0 +1,72 @@ +import type { Command } from "../lib/types/command"; +import { MessageComponentTypes, TextStyles } from "@discordeno/bot"; +import SlashCommand from "../lib/classes/SlashCommand"; +import Modal from "../lib/classes/Modal"; +import { collectModal } from "../lib/handlers/ModalHandler"; + +interface SendMailResponse { + success: boolean; + message: string; +} + +export default class extends SlashCommand implements Command { + name = "send"; + description = "Send mail to a user in the Enigmatica 9 server!"; + options = []; + + constructor() { + super(); + } + + async run() { + const modal = new Modal().setTitle("Send Mail").setComponents([ + { + type: MessageComponentTypes.ActionRow, + components: [ + { + type: MessageComponentTypes.InputText, + customId: "recipient", + style: TextStyles.Short, + label: "Recipient", + }, + ], + }, + { + type: MessageComponentTypes.ActionRow, + components: [ + { + type: MessageComponentTypes.InputText, + customId: "content", + style: TextStyles.Paragraph, + label: "Message", + }, + ], + }, + ]); + + collectModal(modal) + .then(async ({ interaction, values }) => { + if (!interaction.data?.components) return; + const user = interaction?.member?.user || { username: "[unknown]" }; + + try { + const response = await fetch(process.env.MAIL_SERVER + "/send", { + method: "POST", + body: JSON.stringify({ + author: user.username, + recipient: values.get("recipient"), + content: values.get("content"), + }), + }); + + const body = (await response.json()) as SendMailResponse; + interaction.respond(body.message); + } catch (e) { + return interaction.respond((e as Error).message); + } + }) + .catch((_) => {}); + + return modal; + } +} diff --git a/src/lib/handlers/InteractionHandler.ts b/src/lib/handlers/InteractionHandler.ts index 038c088..eac136f 100644 --- a/src/lib/handlers/InteractionHandler.ts +++ b/src/lib/handlers/InteractionHandler.ts @@ -13,7 +13,9 @@ export async function load(client: Bot) { for (let i = 0; i < files.length; i++) { let value = files[i]; const interaction = await import(import.meta.dirname + "/../../interactions/" + value); - interactions.set(interaction.default.name, interaction.default); + const cmd = new interaction.default(); + + interactions.set(cmd.name, interaction.default); } CommandHandler.load(client, interactions); diff --git a/src/lib/handlers/ModalHandler.ts b/src/lib/handlers/ModalHandler.ts index 13b1899..0f4fae3 100644 --- a/src/lib/handlers/ModalHandler.ts +++ b/src/lib/handlers/ModalHandler.ts @@ -9,12 +9,13 @@ function getModalValues(interaction: Interaction): Map { const values = new Map(); if (!interaction.data?.components) return values; - const actionRow: Component = interaction.data?.components[0]; - if (actionRow.components?.length) { - actionRow.components.forEach((value, index) => { - values.set(value.customId, value.value); - }); - } + interaction.data?.components.forEach((actionRow: Component) => { + if (actionRow.components?.length) { + actionRow.components.forEach((value, index) => { + values.set(value.customId, value.value); + }); + } + }); return values; } @@ -25,10 +26,13 @@ export function collectModal(modal: Modal): Promise { modal.setId(id); modals.set(id, resolve); - setTimeout(() => { - modals.delete(id); - reject(new Error("Modal timeout")); - }, 1000 * 60 * 30); + setTimeout( + () => { + modals.delete(id); + reject(new Error("Modal timeout")); + }, + 1000 * 60 * 30, + ); }); } diff --git a/src/lib/types/environment.d.ts b/src/lib/types/environment.d.ts index 79d535b..73a50bd 100644 --- a/src/lib/types/environment.d.ts +++ b/src/lib/types/environment.d.ts @@ -1,5 +1,8 @@ declare module "bun" { - interface Env { - DISCORD_TOKEN: string; - } + interface Env { + DISCORD_ID: string; + DISCORD_TOKEN: string; + DISCORD_SECRET: string; + MAIL_SERVER: string; + } }