Skip to content

Commit 47f6d30

Browse files
Add deleteButton
1 parent 4013171 commit 47f6d30

File tree

5 files changed

+58
-55
lines changed

5 files changed

+58
-55
lines changed

src/bot.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import "dotenv/config";
22
import { Client, Collection, LimitedCollection } from "discord.js";
33
import { MyContext } from "./interfaces";
4-
import { loadCommands, commandHandler } from "./handlers/CommandHandler";
4+
import { loadCommands, interactionCreateHandler } from "./handlers/CommandHandler";
55
import { messageHandler } from "./handlers/MessageHandler";
66

77
(async function () {
@@ -42,7 +42,7 @@ import { messageHandler } from "./handlers/MessageHandler";
4242
});
4343

4444
docsBot.on("messageCreate", messageHandler);
45-
docsBot.on("interactionCreate", commandHandler.bind(null, context));
45+
docsBot.on("interactionCreate", interactionCreateHandler.bind(null, context));
4646

4747
docsBot.login(process.env.TOKEN);
4848
})();

src/commands/docs/djs.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Command } from "../../interfaces";
22
import { SlashCommandBuilder } from "@discordjs/builders";
33
import Doc, { sources } from "discord.js-docs";
44
import { checkEmbedLimits } from "../../utils/EmbedUtils";
5+
import { deleteButton } from "../../utils/CommandUtils";
6+
import { MessageActionRow } from "discord.js";
57

68
const supportedBranches = Object.keys(sources).map((branch) => [capitalize(branch), branch] as [string, string]);
79

@@ -29,6 +31,7 @@ const command: Command = {
2931
.setRequired(false),
3032
),
3133
async execute(interaction) {
34+
const deleteButtonRow = new MessageActionRow().addComponents([deleteButton]);
3235
const query = interaction.options.getString("query");
3336
// The Default source should be stable
3437
const source: keyof typeof sources =
@@ -47,7 +50,7 @@ const command: Command = {
4750
// Satisfies the method's MessageEmbedOption type
4851
const embedObj = { ...notFoundEmbed, timestamp: timeStampDate };
4952

50-
interaction.editReply({ embeds: [embedObj] }).catch(console.error);
53+
interaction.editReply({ embeds: [embedObj], components: [deleteButtonRow] }).catch(console.error);
5154
return;
5255
}
5356

@@ -60,7 +63,7 @@ const command: Command = {
6063
// The final field should be the View Source button
6164
embedObj.fields = [embedObj.fields?.at(-1)];
6265
}
63-
interaction.editReply({ embeds: [embedObj] }).catch(console.error);
66+
interaction.editReply({ embeds: [embedObj], components: [deleteButtonRow] }).catch(console.error);
6467
return;
6568
},
6669
};

src/commands/docs/mdn.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SlashCommandBuilder } from "@discordjs/builders";
2-
import { MessageEmbed } from "discord.js";
2+
import { deleteButton } from "../../utils/CommandUtils";
3+
import { MessageActionRow, MessageEmbed } from "discord.js";
34
import { gunzipSync } from "zlib";
45
import { XMLParser } from "fast-xml-parser";
56
import { Command } from "../../interfaces";
@@ -20,7 +21,7 @@ let sources = {
2021

2122
const MDN_BASE_URL = "https://developer.mozilla.org/en-US/docs/" as const;
2223
const MDN_ICON_URL = "https://i.imgur.com/1P4wotC.png" as const;
23-
const MDN_BLUE_COLOR = 0x83BFFF as const;
24+
const MDN_BLUE_COLOR = 0x83bfff as const;
2425

2526
const command: Command = {
2627
data: new SlashCommandBuilder()
@@ -33,36 +34,37 @@ const command: Command = {
3334
.setRequired(true),
3435
),
3536
async execute(interaction) {
37+
const deleteButtonRow = new MessageActionRow().addComponents([deleteButton]);
3638
const query = interaction.options.getString("query");
3739
const { index, sitemap } = await getSources();
3840
const search: string[] = index.search(query, { limit: 10 }).map((id) => sitemap[<number>id].loc);
3941
const embed = new MessageEmbed()
4042
.setColor(MDN_BLUE_COLOR)
41-
.setAuthor("MDN Documentation", MDN_ICON_URL)
43+
.setAuthor({ name: "MDN Documentation", iconURL: MDN_ICON_URL })
4244
.setTitle(`Search for: ${query}`);
4345

4446
if (!search.length) {
45-
embed.setColor(0xFF0000).setDescription("No results found...");
46-
interaction.editReply({ embeds: [embed] });
47+
embed.setColor(0xff0000).setDescription("No results found...");
48+
interaction.editReply({ embeds: [embed], components: [deleteButtonRow] });
4749
return;
4850
}
4951

5052
if (search.length === 1) {
5153
const res = await fetch(`${MDN_BASE_URL + search[0]}/index.json`);
5254
const doc: MdnDoc = (await res.json()).doc;
5355
const docEmbed = embed
54-
.setColor(0xFFFFFF)
56+
.setColor(0xffffff)
5557
.setTitle(doc.pageTitle)
5658
.setURL(`https://developer.mozilla.org/${doc.mdn_url}`)
5759
.setThumbnail(this.MDN_ICON_URL)
5860
.setDescription(doc.summary);
59-
interaction.editReply({ embeds: [docEmbed] });
61+
interaction.editReply({ embeds: [docEmbed], components: [deleteButtonRow] });
6062
return;
6163
}
6264

6365
const results = search.map((path) => `**• [${path.replace(/_|-/g, " ")}](${MDN_BASE_URL}${path})**`);
6466
embed.setDescription(results.join("\n"));
65-
interaction.editReply({ embeds: [embed] });
67+
interaction.editReply({ embeds: [embed], components: [deleteButtonRow] });
6668
return;
6769
},
6870
};

src/handlers/CommandHandler.ts

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import glob from "glob";
22
import type { Command, MyContext } from "../interfaces";
3-
import type { CommandInteraction, Interaction } from "discord.js";
4-
import { Permissions, Formatters } from "discord.js";
3+
import type { CommandInteraction, Interaction, Message } from "discord.js";
4+
import { Permissions, Formatters, MessageActionRow } from "discord.js";
5+
import { deleteButton } from "../utils/CommandUtils";
56

6-
export async function commandHandler(
7-
context: MyContext,
8-
interaction: Interaction
9-
) {
7+
export async function interactionCreateHandler(context: MyContext, interaction: Interaction) {
108
if (interaction.isCommand()) {
119
await interaction.deferReply();
1210
const command = context.commands.get(interaction.commandName);
@@ -19,7 +17,27 @@ export async function commandHandler(
1917
} catch (e) {
2018
console.error(e);
2119
const errorMessage = "An error has occurred";
22-
interaction.editReply(errorMessage).catch(console.error);
20+
interaction
21+
.editReply({
22+
content: errorMessage,
23+
components: [new MessageActionRow().addComponents([deleteButton])],
24+
})
25+
.catch(console.error);
26+
}
27+
}
28+
if (interaction.isButton()) {
29+
// The delete button
30+
if (interaction.customId === "deletebtn") {
31+
// If the button clicker is the command initiator
32+
if (interaction.user.id === interaction.message.interaction.user.id) {
33+
(interaction.message as Message).delete().catch(console.error);
34+
} else
35+
interaction
36+
.reply({
37+
content: "Only the command initiator is allowed to delete this message",
38+
ephemeral: true,
39+
})
40+
.catch(console.error);
2341
}
2442
}
2543
}
@@ -31,20 +49,18 @@ export async function commandHandler(
3149
export function loadCommands(context: MyContext) {
3250
// Promisifies the process of glob
3351
return new Promise((resolve) => {
34-
// Find all js files
52+
// Find all js files
3553
glob(`${__dirname}/../commands/**/*.js`, async (err, files) => {
3654
await Promise.all(
3755
files.map(async (file) => {
38-
const { default: myCommandFile }: { default: Command } = await import(
39-
file
40-
).catch((err) => {
56+
const { default: myCommandFile }: { default: Command } = await import(file).catch((err) => {
4157
console.error(err);
4258
// Since the return value gets destructured, an empty object is returned
4359
return {};
4460
});
4561
if (!myCommandFile) return;
4662
context.commands.set(myCommandFile.data.name, myCommandFile);
47-
})
63+
}),
4864
);
4965
resolve(undefined);
5066
});
@@ -56,21 +72,14 @@ export function loadCommands(context: MyContext) {
5672
* @param command
5773
* @returns Whether to cancel the command
5874
*/
59-
// * Note that as of writing, slash commands can override permissions
60-
function commandPermissionCheck(
61-
interaction: CommandInteraction,
62-
command: Command
63-
): boolean {
75+
// * Note that as of writing, slash commands can override permissions
76+
function commandPermissionCheck(interaction: CommandInteraction, command: Command): boolean {
6477
const { client, user, channel } = interaction;
6578
// If the channel is a dm
6679
// if it's a partial, channel.type wouldn't exist
6780
if (channel.type === "DM" || !channel) {
6881
if (command.guildOnly) {
69-
interaction
70-
.editReply(
71-
"This is a guild exclusive command, not to be executed in a dm"
72-
)
73-
.catch(console.error);
82+
interaction.editReply("This is a guild exclusive command, not to be executed in a dm").catch(console.error);
7483
// For guild only commands that were executed in a dm, cancel the command
7584
return true;
7685
}
@@ -80,15 +89,13 @@ function commandPermissionCheck(
8089
if (command.botPermissions) {
8190
const botPermissions = new Permissions(command.botPermissions);
8291
// The required permissions for the bot to run the command, missing in the channel.
83-
const missingPermissions = channel
84-
.permissionsFor(client.user)
85-
.missing(botPermissions);
92+
const missingPermissions = channel.permissionsFor(client.user).missing(botPermissions);
8693
if (missingPermissions.length > 0) {
8794
interaction
8895
.editReply(
8996
`In order to run this command, I need the following permissions: ${missingPermissions
9097
.map((perm) => `\`${perm}\``)
91-
.join(", ")}`
98+
.join(", ")}`,
9299
)
93100
.catch(console.error);
94101
return true;
@@ -97,15 +104,13 @@ function commandPermissionCheck(
97104
if (command.authorPermissions) {
98105
const authorPermissions = new Permissions(command.authorPermissions);
99106
// The required permissions for the user to run the command, missing in the channel.
100-
const missingPermissions = channel
101-
.permissionsFor(user.id)
102-
.missing(authorPermissions);
107+
const missingPermissions = channel.permissionsFor(user.id).missing(authorPermissions);
103108
if (missingPermissions.length > 0) {
104109
interaction
105110
.editReply(
106111
`In order to run this command, you need: ${missingPermissions
107112
.map((perm) => `\`${perm}\``)
108-
.join(", ")}`
113+
.join(", ")}`,
109114
)
110115
.catch(console.error);
111116
return true;
@@ -114,11 +119,7 @@ function commandPermissionCheck(
114119
// By default, allow execution;
115120
return false;
116121
}
117-
function commandCooldownCheck(
118-
interaction: CommandInteraction,
119-
command: Command,
120-
context: MyContext
121-
): boolean {
122+
function commandCooldownCheck(interaction: CommandInteraction, command: Command, context: MyContext): boolean {
122123
const { user } = interaction;
123124
if (command.cooldown) {
124125
const id = user.id + "/" + interaction.commandName;
@@ -131,18 +132,12 @@ function commandCooldownCheck(
131132
interaction
132133
.editReply(
133134
//TODO revert to using custom logic to send remaining time as the discord timestamp formatting isn't very descriptive
134-
`Please wait ${Formatters.time(
135-
existingCooldown,
136-
"R"
137-
)} before using the command again`
135+
`Please wait ${Formatters.time(existingCooldown, "R")} before using the command again`,
138136
)
139137
.catch(console.error);
140138
return true;
141139
}
142-
context.cooldownCounter.set(
143-
user.id + "/" + interaction.commandName,
144-
Date.now() + command.cooldown
145-
);
140+
context.cooldownCounter.set(user.id + "/" + interaction.commandName, Date.now() + command.cooldown);
146141
}
147142
return false;
148143
}

src/utils/CommandUtils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { MessageButton } from "discord.js";
2+
3+
export const deleteButton = new MessageButton().setCustomId("deletebtn").setEmoji("🗑").setStyle("SECONDARY");

0 commit comments

Comments
 (0)