From c018d35e2a9d983f33faa606b37cf81fd08cbd2f Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Wed, 21 Sep 2022 01:26:46 +0200 Subject: [PATCH] - refactor - add modal --- .gitignore | 1 + prisma/dev.db | Bin 20480 -> 0 bytes src/Components/actions.component.ts | 10 +++ src/Components/commands.component.ts | 30 +++++++ src/Components/quote-modal.component.ts | 23 +++++ src/Controllers/discord.controller.ts | 73 ++++++++------- src/Services/discord.service.ts | 46 ---------- src/Services/discord/discord.service.ts | 114 ++++++++++++++++++++++++ 8 files changed, 221 insertions(+), 76 deletions(-) delete mode 100644 prisma/dev.db create mode 100644 src/Components/actions.component.ts create mode 100644 src/Components/commands.component.ts create mode 100644 src/Components/quote-modal.component.ts delete mode 100644 src/Services/discord.service.ts create mode 100644 src/Services/discord/discord.service.ts diff --git a/.gitignore b/.gitignore index 11ddd8d..360a0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules # Keep environment variables out of version control .env +prisma/dev.db diff --git a/prisma/dev.db b/prisma/dev.db deleted file mode 100644 index 028592a79a8e38e9a4e7305204e30614ca3a12ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI)zi-<{6bEopmf}{8p>9s#<)ERCkvI^4$g;MoHfso#WJjiuI16!iJX>riQsGfR zhc1SJ{v8GSC-l$gUgVF+RCnmmAxDK$EXfH96hRQ+`yg5r`R*Ow=cqxH?b9cnAQj|1 zPDV5(miD!#>)N-3Xqr}3TS;xRD5#z6*evwf&Ig{0+T-J&H!A;V#k;?18-G@Qzx$Wk zK|lZk5P$##AOHafKmY;|xJBTkcxUtBL;WnJEEMu(s6MQCQoNjqh%dh^9rT@+>kzl~ zUB@9yxep>b5|7l#olCm5zPYufU)`V9>l`_K z(mQrZZ_w$GlYYC~>YtGxoHNoIxX10DD$;d&?xVMZCQ>9lHG7q~&NDS8AD5ryag>TE zeb326&{X&>b^1`X=C-@ed^|aHzHbdWE;$(V`)W2XJA3N3x+mqGtLOUqreWy6c+(!e zaT3T8^+v%(Len6QaV&|&~3kaFUQcRn>X|desQ1BOWGO8|)yUpZ0hypnjzDHH# z?_x%f%gwv-vbCKj-c~S-FXTMoUBN6hFP6k%s3v5TzqmQ;(v&i#iCT}F8CkVi*6P~u zNs_45_0nJ@)Zo!LpN@{lVW0}hRE(v^<4JY}komPW=$*EYdd}f$!CHCepm1mNsiA8@ z3B~T{zEJJ+`LaYF}h;w+*%F6``pI0s;_#00bZa0SG_<0uX=z1Rwx` z|08gDPcIKHH%e{wh5DBblM7}EV^^?Reb=_Qxy$xyeAjPK+hR=c2H$U*wqUl|)f~XtHE)VnKjd_HI1fG zvn<2%Bp(WO6NqQ`l+-<-X-=oC`qC?}H1$A000Izz00bZa0SG_<0uX=z1R(I432d(o z{?na??EC*hB$pDhY+02-iSbN~PV diff --git a/src/Components/actions.component.ts b/src/Components/actions.component.ts new file mode 100644 index 0000000..1666815 --- /dev/null +++ b/src/Components/actions.component.ts @@ -0,0 +1,10 @@ +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; + +export default function getActionsComponent() { + return new ActionRowBuilder().addComponents( + new ButtonBuilder() + .setCustomId("quoteModal") + .setLabel("Quote") + .setStyle(ButtonStyle.Primary) + ); +} diff --git a/src/Components/commands.component.ts b/src/Components/commands.component.ts new file mode 100644 index 0000000..dcce9e2 --- /dev/null +++ b/src/Components/commands.component.ts @@ -0,0 +1,30 @@ +import { SlashCommandBuilder } from "discord.js"; + +export default function getCommands() { + const commands = [ + new SlashCommandBuilder() + .setName("quote") + .setDescription("Lets you post a qoute without showing your name") + .addStringOption((option) => + option + .setName("content") + .setDescription("The content of the quote") + .setRequired(true) + ), + new SlashCommandBuilder() + .setName("quotemodal") + .setDescription( + "Shows a model which lets you post a quote without showing your name" + ), + new SlashCommandBuilder() + .setName("actions") + .setDescription("Handle actions on this server") + .addSubcommand((subcommand) => + subcommand + .setName("send") + .setDescription("Sends bot actions in the current channel") + ), + ].map((command) => command.toJSON()); + + return commands; +} diff --git a/src/Components/quote-modal.component.ts b/src/Components/quote-modal.component.ts new file mode 100644 index 0000000..77b130f --- /dev/null +++ b/src/Components/quote-modal.component.ts @@ -0,0 +1,23 @@ +import { + ActionRowBuilder, + ModalBuilder, + TextInputBuilder, + TextInputStyle, +} from "discord.js"; + +export default function getQuoteModal() { + const modal = new ModalBuilder() + .setCustomId("quoteModal") + .setTitle("Submit a quote!"); + + const contentInput = new TextInputBuilder() + .setCustomId("contentInput") + .setLabel("Content") + .setStyle(TextInputStyle.Paragraph); + + const firstActionRow = new ActionRowBuilder().addComponents(contentInput); + + modal.addComponents(firstActionRow as any); + + return modal; +} diff --git a/src/Controllers/discord.controller.ts b/src/Controllers/discord.controller.ts index 691649d..b3bbaa4 100644 --- a/src/Controllers/discord.controller.ts +++ b/src/Controllers/discord.controller.ts @@ -1,24 +1,24 @@ import { + ButtonInteraction, CacheType, ChatInputCommandInteraction, Interaction, + ModalSubmitInteraction, } from "discord.js"; -import config from "config"; import EventEmitter from "events"; -import DiscordService from "Services/discord.service"; -import PrismaService from "Services/prisma.service"; +import DiscordService from "Services/discord/discord.service"; export default class DiscordController extends EventEmitter { private discordService!: DiscordService; - private prismaService: PrismaService; constructor() { super(); this.discordService = new DiscordService(); - this.prismaService = new PrismaService(); + // log when running this.discordService.client.once("ready", () => { console.log("Listening..."); }); + // listen for interactions this.discordService.client.on( "interactionCreate", this.handleInteraction.bind(this) @@ -30,42 +30,55 @@ export default class DiscordController extends EventEmitter { } async handleInteraction(interaction: Interaction) { - if (!interaction.isChatInputCommand()) return; + if (interaction.isModalSubmit()) { + await this.handleModalSubmit(interaction); + } + if (interaction.isChatInputCommand()) { + await this.handleChatInputCommand(interaction); + } + + if (interaction.isButton()) { + await this.handleButton(interaction); + } + } + + async handleButton(interaction: ButtonInteraction) { + const { customId } = interaction; + + if (customId === "quoteModal") { + this.discordService.handleQuoteModal(interaction); + } + } + + async handleChatInputCommand( + interaction: ChatInputCommandInteraction + ) { const { commandName } = interaction; switch (commandName) { case "quote": - await this.quote(interaction); + await this.discordService.handleQuote(interaction); + case "quotemodal": + await this.discordService.handleQuoteModal(interaction); + case "actions": + await this.discordService.handleActions(interaction); + default: break; } } - async quote(interaction: ChatInputCommandInteraction) { - // find the channel with configured id - const channel = this.discordService.client.channels.cache.find( - (ch) => ch.id === config.discord.channelId - ); + async handleModalSubmit(interaction: ModalSubmitInteraction) { + const { customId } = interaction; - if (channel?.isTextBased()) { - const content = interaction.options.getString("content") || ""; - - // send quote content as message - await this.prismaService.client.message.create({ - data: { - content, - userName: interaction.user.username, - }, - }); - - await channel.send(content); - - await interaction.reply({ - content: "Completed!", - ephemeral: true, - }); - return; + switch (customId) { + case "quoteModal": + await this.discordService.handleQuote(interaction); + default: + break; } } + + async sendActions(interaction: ChatInputCommandInteraction) {} } diff --git a/src/Services/discord.service.ts b/src/Services/discord.service.ts deleted file mode 100644 index 61dd8b9..0000000 --- a/src/Services/discord.service.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - Client, - IntentsBitField, - Routes, - SlashCommandBuilder, -} from "discord.js"; -import { REST } from "@discordjs/rest"; -import config from "config"; - -const COMMANDS = [ - new SlashCommandBuilder() - .setName("quote") - .setDescription("Lets you post a qoute without showing your name") - .addStringOption((option) => - option - .setName("content") - .setDescription("The content of the quote") - .setRequired(true) - ), -].map((command) => command.toJSON()); - -export default class DiscordService { - rest: REST; - client: Client; - constructor() { - this.rest = new REST({ version: "10" }).setToken(config.discord.token); - - this.client = new Client({ intents: [IntentsBitField.Flags.Guilds] }); - } - - async init() { - try { - await this.rest.put( - Routes.applicationCommands(config.discord.applicationId), - { - body: COMMANDS, - } - ); - console.log("Successfully added commands"); - - await this.client.login(config.discord.token); - } catch (e) { - console.error(e); - } - } -} diff --git a/src/Services/discord/discord.service.ts b/src/Services/discord/discord.service.ts new file mode 100644 index 0000000..9d6e629 --- /dev/null +++ b/src/Services/discord/discord.service.ts @@ -0,0 +1,114 @@ +import { + CacheType, + ChatInputCommandInteraction, + Client, + IntentsBitField, + Interaction, + Routes, + SlashCommandBuilder, +} from "discord.js"; +import { REST } from "@discordjs/rest"; +import config from "config"; +import PrismaService from "Services/prisma.service"; +import getCommands from "Components/commands.component"; +import getQuoteModal from "Components/quote-modal.component"; +import getActionsComponent from "Components/actions.component"; + +export default class DiscordService { + rest: REST; + client: Client; + private prismaService: PrismaService; + + constructor() { + this.rest = new REST({ version: "10" }).setToken(config.discord.token); + this.prismaService = new PrismaService(); + + this.client = new Client({ intents: [IntentsBitField.Flags.Guilds] }); + } + + async init() { + try { + await this.rest.put( + Routes.applicationCommands(config.discord.applicationId), + { + body: getCommands(), + } + ); + console.log("Successfully added commands"); + + await this.client.login(config.discord.token); + } catch (e) { + console.error(e); + } + } + + async handleQuote(interaction: Interaction) { + let content: string | undefined = undefined; + + if (interaction.isModalSubmit()) { + content = interaction.fields.getTextInputValue("contentInput"); + } else if (interaction.isChatInputCommand()) { + content = interaction.options.getString("content") || ""; + } + + if (content) { + await this.createQuote({ content }, interaction); + } + } + + async handleQuoteModal(interaction: Interaction) { + const modal = getQuoteModal(); + + if (interaction.isButton() || interaction.isChatInputCommand()) { + await interaction.showModal(modal); + } + } + + async handleActions(interaction: Interaction) { + if (interaction.isChatInputCommand()) { + if (interaction.options.getSubcommand() === "send") { + const channel = interaction.channel; + + if (channel?.isTextBased()) { + const actions = getActionsComponent(); + + await interaction.reply({ + content: "Choose an action!", + components: [actions as any], + }); + } + } + } + } + + async createQuote( + { content }: { content: string }, + interaction: Interaction + ) { + // find the channel with configured id + const channel = this.client.channels.cache.find( + (ch) => ch.id === config.discord.channelId + ); + + if (channel?.isTextBased()) { + // send quote content as message + await this.prismaService.client.message.create({ + data: { + content, + userName: interaction.user.username, + }, + }); + + await channel.send(content); + + if (interaction.isChatInputCommand() || interaction.isModalSubmit()) { + await interaction.reply({ + content: "Completed!", + ephemeral: true, + }); + } + + return; + } + } +}