diff --git a/.prettierrc b/.prettierrc index 222861c..47b748f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { - "tabWidth": 2, - "useTabs": false + "tabWidth": 4, + "useTabs": false, + "printWidth": 120 } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} diff --git a/bun.lockb b/bun.lockb index 7655fa0..e628973 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 6486979..23805b6 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "type": "module", "dependencies": { "@discordeno/bot": "^19.0.0-next.d81b28a", - "@discordeno/rest": "^19.0.0-next.d81b28a", - "@discordeno/utils": "^19.0.0-next.d81b28a" + "@discordeno/rest": "^19.0.0-next.d81b28a" } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 2a5e4b8..8902dc3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,40 @@ -console.log("Hello via Bun!"); +import { createBot, Intents } from "@discordeno/bot"; +import * as interactionHandler from "./lib/handlers/InteractionHandler"; + +const client = createBot({ + token: process.env.DISCORD_TOKEN, + intents: Intents.Guilds, + events: { + ready(payload, rawPayload) { + console.log("Logged in"); + }, + interactionCreate: interactionHandler.handle, + }, +}); + +// Load handlers step and whatnot ( hatchets ) +await interactionHandler.load(client); + +client.start(); + +client.transformers.desiredProperties.message.content = true; +client.transformers.desiredProperties.message.author = true; + +client.transformers.desiredProperties.user.id = true; +client.transformers.desiredProperties.user.username = true; +client.transformers.desiredProperties.user.avatar = true; + +client.transformers.desiredProperties.member.user = true; +client.transformers.desiredProperties.member.roles = true; +client.transformers.desiredProperties.member.permissions = true; + +client.transformers.desiredProperties.interaction.id = true; +client.transformers.desiredProperties.interaction.type = true; +client.transformers.desiredProperties.interaction.data = true; +client.transformers.desiredProperties.interaction.token = true; +client.transformers.desiredProperties.interaction.guildId = true; +client.transformers.desiredProperties.interaction.channelId = true; +client.transformers.desiredProperties.interaction.member = true; + +process.on("SIGINT", process.exit); +process.on("SIGTERM", process.exit); diff --git a/src/interactions/ping.ts b/src/interactions/ping.ts new file mode 100644 index 0000000..f335c0c --- /dev/null +++ b/src/interactions/ping.ts @@ -0,0 +1,33 @@ +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"; + +export default class extends SlashCommand implements Command { + static name = "ping"; + static description = "Show a test modal"; + static options = []; + + constructor() { + super(); + } + + async run() { + return new Modal() + .setTitle("dope") + .setId("dope") + .setComponents([ + { + type: MessageComponentTypes.ActionRow, + components: [ + { + type: MessageComponentTypes.InputText, + customId: "dope", + style: TextStyles.Short, + label: "dope", + }, + ], + }, + ]); + } +} diff --git a/src/lib/classes/ApplicationCommand.ts b/src/lib/classes/ApplicationCommand.ts new file mode 100644 index 0000000..2c90c0b --- /dev/null +++ b/src/lib/classes/ApplicationCommand.ts @@ -0,0 +1,5 @@ +import { InteractionTypes } from "@discordeno/types"; + +export default class ApplicationCommand { + static type = InteractionTypes.ApplicationCommand; +} diff --git a/src/lib/classes/MessageCommand.ts b/src/lib/classes/MessageCommand.ts new file mode 100644 index 0000000..dd6225b --- /dev/null +++ b/src/lib/classes/MessageCommand.ts @@ -0,0 +1,6 @@ +import { ApplicationCommandTypes } from "@discordeno/types"; +import ApplicationCommand from "./ApplicationCommand"; + +export default class SlashCommand extends ApplicationCommand { + static commandType = ApplicationCommandTypes.Message; +} diff --git a/src/lib/classes/Modal.ts b/src/lib/classes/Modal.ts new file mode 100644 index 0000000..61e748d --- /dev/null +++ b/src/lib/classes/Modal.ts @@ -0,0 +1,22 @@ +import { InteractionResponseTypes, type Component } from "@discordeno/bot"; +import type { InteractionCallbackData } from "@discordeno/types"; + +export default class Modal { + type = InteractionResponseTypes.Modal; + data: InteractionCallbackData = {}; + + setTitle(title: string): Modal { + this.data = Object.assign(this.data, { title }); + return this; + } + + setId(id: string): Modal { + this.data = Object.assign(this.data, { custom_id: id }); + return this; + } + + setComponents(components: Array) { + this.data = Object.assign(this.data, { components }); + return this; + } +} diff --git a/src/lib/classes/SlashCommand.ts b/src/lib/classes/SlashCommand.ts new file mode 100644 index 0000000..856ced1 --- /dev/null +++ b/src/lib/classes/SlashCommand.ts @@ -0,0 +1,6 @@ +import { ApplicationCommandTypes } from "@discordeno/types"; +import ApplicationCommand from "./ApplicationCommand"; + +export default class SlashCommand extends ApplicationCommand { + static commandType = ApplicationCommandTypes.ChatInput; +} diff --git a/src/lib/classes/UserCommand.ts b/src/lib/classes/UserCommand.ts new file mode 100644 index 0000000..edb3bbe --- /dev/null +++ b/src/lib/classes/UserCommand.ts @@ -0,0 +1,6 @@ +import { ApplicationCommandTypes } from "@discordeno/types"; +import ApplicationCommand from "./ApplicationCommand"; + +export default class SlashCommand extends ApplicationCommand { + static commandType = ApplicationCommandTypes.User; +} diff --git a/src/lib/handlers/AutocompleteHandler.ts b/src/lib/handlers/AutocompleteHandler.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/handlers/CommandHandler.ts b/src/lib/handlers/CommandHandler.ts new file mode 100644 index 0000000..70f438d --- /dev/null +++ b/src/lib/handlers/CommandHandler.ts @@ -0,0 +1,27 @@ +import type { Bot, Interaction, InteractionResponse } from "@discordeno/bot"; +import type { Command } from "../types/command"; +import REST from "./RESTHandler"; + +export const commands = new Map(); + +export function load(client: Bot, interactions: Map) { + for (let [key, value] of interactions) { + commands.set(key, new value()); + } +} + +export function handle(interaction: Interaction) { + if (!interaction.data) return; + + const resolved = commands.get(interaction.data.name); + if (!resolved) return interaction.respond("What the fuck did you do"); + resolved + .run() + .then((response: InteractionResponse) => + REST.sendInteractionResponse(interaction.id, interaction.token, response) + ) + .catch((e: Error) => { + console.log("CommandHandler.ts -> Exception"); + console.log(e); + }); +} diff --git a/src/lib/handlers/ComponentHandler.ts b/src/lib/handlers/ComponentHandler.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/handlers/InteractionHandler.ts b/src/lib/handlers/InteractionHandler.ts new file mode 100644 index 0000000..def67af --- /dev/null +++ b/src/lib/handlers/InteractionHandler.ts @@ -0,0 +1,50 @@ +import { InteractionTypes, type Bot, type Interaction, InteractionResponseTypes } from "@discordeno/bot"; +import { readdir } from "node:fs/promises"; +import REST from "./RESTHandler"; + +import * as CommandHandler from "./CommandHandler"; + +export const interactions = new Map(); + +export async function load(client: Bot) { + const files = await readdir("src/interactions"); + + 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); + } + + CommandHandler.load(client, interactions); +} + +export function handle(interaction: Interaction) { + const { type } = interaction; + + if (type == InteractionTypes.ApplicationCommand) { + CommandHandler.handle(interaction); + } else if (type == InteractionTypes.MessageComponent) { + REST.sendInteractionResponse(interaction.id, interaction.token, { + type: InteractionResponseTypes.ChannelMessageWithSource, + data: { + content: "Not Implemented", + flags: 64, + }, + }); + } else if (type == InteractionTypes.ApplicationCommandAutocomplete) { + REST.sendInteractionResponse(interaction.id, interaction.token, { + type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, + data: { + choices: [{ name: "not_implemented", value: "Not Implemented" }], + }, + }); + } else if (type == InteractionTypes.ModalSubmit) { + REST.sendInteractionResponse(interaction.id, interaction.token, { + type: InteractionResponseTypes.ChannelMessageWithSource, + data: { + content: "Not Implemented", + flags: 64, + }, + }); + } +} diff --git a/src/lib/handlers/ModalHandler.ts b/src/lib/handlers/ModalHandler.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/handlers/RESTHandler.ts b/src/lib/handlers/RESTHandler.ts new file mode 100644 index 0000000..cdde6f3 --- /dev/null +++ b/src/lib/handlers/RESTHandler.ts @@ -0,0 +1,7 @@ +import { createRestManager } from "@discordeno/rest"; + +const REST = createRestManager({ + token: process.env.DISCORD_TOKEN, +}); + +export default REST; diff --git a/src/lib/types/command.d.ts b/src/lib/types/command.d.ts new file mode 100644 index 0000000..3112e00 --- /dev/null +++ b/src/lib/types/command.d.ts @@ -0,0 +1,6 @@ +import { type Interaction, type InteractionCallbackData, type InteractionResponse } from "@discordeno/bot"; + +interface Command { + run(interaction: Interaction): Promise; + autocomplete?(interaction: Interaction): Promise; +} diff --git a/src/lib/types/environment.d.ts b/src/lib/types/environment.d.ts new file mode 100644 index 0000000..79d535b --- /dev/null +++ b/src/lib/types/environment.d.ts @@ -0,0 +1,5 @@ +declare module "bun" { + interface Env { + DISCORD_TOKEN: string; + } +}