Skip to content
This repository was archived by the owner on Sep 8, 2024. It is now read-only.

Commit 5418539

Browse files
committed
refactor!: split context menu by type
The old ContextMenu handlers are gone and now there is one context menu handler type depending on whether it is a message menu or a user menu. It makes easier to fetch the messages or users assigned to a context menu interaction. It is also split by type of interaction: guild or DM, and answers appropiately using the same new API as the other interaction types.
1 parent 32b940a commit 5418539

File tree

5 files changed

+140
-64
lines changed

5 files changed

+140
-64
lines changed

src/cmd/makibotctl.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { RESTGetAPIApplicationGuildCommandsResult, Routes } from "discord-api-ty
33
import path from "path";
44
import * as yargs from "yargs";
55
import Client from "../lib/http/client";
6-
import { CommandInteractionHandler, ContextMenuInteractionHandler } from "../lib/interaction";
6+
import {
7+
CommandInteractionHandler,
8+
MessageContextMenuInteractionHandler,
9+
UserContextMenuInteractionHandler,
10+
} from "../lib/interaction";
711
import { requireAllModules } from "../lib/utils/loader";
812

913
const client = new Client();
@@ -45,7 +49,8 @@ makibotctl.command<{ app: string; local?: string }>(
4549

4650
/* Compose the payloads for commands and menus. */
4751
const commandsDir = path.join(__dirname, "../interactions/commands");
48-
const menusDir = path.join(__dirname, "../interactions/menus");
52+
const messageMenusDir = path.join(__dirname, "../interactions/messagemenus");
53+
const userMenusDir = path.join(__dirname, "../interactions/usermenus");
4954

5055
const commandPayloads = requireAllModules(commandsDir).map((HandlerClass) => {
5156
if (typeof HandlerClass == "function") {
@@ -55,15 +60,23 @@ makibotctl.command<{ app: string; local?: string }>(
5560
return handler.build().toJSON();
5661
}
5762
});
58-
const menuPayloads = requireAllModules(menusDir).map((HandlerClass) => {
63+
const usermenuPayloads = requireAllModules(userMenusDir).map((HandlerClass) => {
5964
if (typeof HandlerClass == "function") {
6065
const handler = new (HandlerClass as {
61-
new (): ContextMenuInteractionHandler;
66+
new (): UserContextMenuInteractionHandler;
6267
})();
6368
return handler.build().toJSON();
6469
}
6570
});
66-
const payloads = [...commandPayloads, ...menuPayloads];
71+
const messagemenuPayloads = requireAllModules(messageMenusDir).map((HandlerClass) => {
72+
if (typeof HandlerClass == "function") {
73+
const handler = new (HandlerClass as {
74+
new (): MessageContextMenuInteractionHandler;
75+
})();
76+
return handler.build().toJSON();
77+
}
78+
});
79+
const payloads = [...commandPayloads, ...usermenuPayloads, ...messagemenuPayloads];
6780

6881
/* Send the payloads. */
6982
const restClient = new REST({ version: "9" });

src/interactions/menus/viewkarma.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/interactions/menus/mod.ts renamed to src/interactions/messagemenus/mod.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import { tokenToDate } from "datetoken";
2-
import { ApplicationCommandType, Snowflake } from "discord-api-types/v9";
2+
import { Snowflake } from "discord-api-types/v9";
33
import {
44
ButtonInteraction,
55
ContextMenuInteraction,
66
InteractionReplyOptions,
77
Message,
88
MessageActionRow,
99
MessageButton,
10+
MessageContextMenuInteraction,
1011
MessageSelectMenu,
1112
MessageSelectOptionData,
1213
SelectMenuInteraction,
1314
} from "discord.js";
1415
import { ContextMenuCommandBuilder, userMention } from "@discordjs/builders";
15-
import type { ContextMenuInteractionHandler } from "../../lib/interaction";
16+
import type { MessageContextMenuInteractionHandler } from "../../lib/interaction";
1617
import Member from "../../lib/member";
1718
import { applyAction } from "../../lib/modlog/actions";
1819
import { notifyModlog } from "../../lib/modlog/notifications";
@@ -357,37 +358,21 @@ class ModerationRequest {
357358
}
358359
}
359360

360-
export default class ModRequestCommand implements ContextMenuInteractionHandler {
361+
export default class ModRequestCommand implements MessageContextMenuInteractionHandler {
361362
name = "Aplicar o pedir moderación";
362363

363364
build() {
364365
return new ContextMenuCommandBuilder().setName("Aplicar o pedir moderación").setType(3);
365366
}
366367

367-
async handle(event: ContextMenuInteraction): Promise<void> {
368-
const parent = await event.deferReply({ ephemeral: true, fetchReply: true });
369-
if (event.inGuild()) {
370-
return this.handleGuild(event, parent.id);
371-
}
372-
const toast = createToast({
373-
title: "Este comando no puede usarse fuera de un servidor",
374-
severity: "error",
375-
});
376-
await event.editReply({
377-
embeds: [toast],
378-
});
379-
}
368+
async handleGuild(interaction: MessageContextMenuInteraction): Promise<void> {
369+
const parent = await interaction.deferReply({ ephemeral: true, fetchReply: true });
370+
const parentId = parent.id;
380371

381-
private async handleGuild(
382-
interaction: ContextMenuInteraction,
383-
parentId: Snowflake
384-
): Promise<void> {
385372
/* I'm only extracting the parameters here to avoid promises. */
386373
const server = new Server(interaction.guild);
387374
const reporter = await server.member(interaction.user);
388-
const message = await interaction.channel.messages.fetch(
389-
interaction.options.get("message").message.id
390-
);
375+
const message = await interaction.channel.messages.fetch(interaction.targetMessage.id);
391376
const target = await server.member(message.author.id);
392377

393378
const prompt = new ModerationRequest({ parentId, interaction, message, reporter, target });
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ContextMenuCommandBuilder } from "@discordjs/builders";
2+
import { UserContextMenuInteraction } from "discord.js";
3+
import type { UserContextMenuInteractionHandler } from "../../lib/interaction";
4+
import { handleKarmaInteraction } from "../../lib/karma/interaction";
5+
6+
export default class ViewKarmaCommand implements UserContextMenuInteractionHandler {
7+
name = "Ver karma";
8+
9+
build() {
10+
return new ContextMenuCommandBuilder().setName("Ver karma").setType(2);
11+
}
12+
13+
async handleGuild(event: UserContextMenuInteraction): Promise<void> {
14+
return handleKarmaInteraction(event, event.targetUser.id);
15+
}
16+
}

src/lib/interaction.ts

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import {
66
import type {
77
ButtonInteraction,
88
CommandInteraction,
9-
ContextMenuInteraction,
109
Interaction,
10+
MessageContextMenuInteraction,
11+
UserContextMenuInteraction,
1112
} from "discord.js";
1213
import path from "path";
1314
import type Makibot from "../Makibot";
@@ -49,8 +50,23 @@ export interface CommandInteractionHandler extends BaseInteractionHandler {
4950
| Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">;
5051
}
5152

52-
export interface ContextMenuInteractionHandler extends BaseInteractionHandler {
53-
handle(event: ContextMenuInteraction): Promise<void>;
53+
export interface UserContextMenuInteractionHandler extends BaseInteractionHandler {
54+
/** Handle the command when sent to a guild. */
55+
handleGuild?: (event: UserContextMenuInteraction) => Promise<void>;
56+
57+
/** Handle the command when sent to a DM. */
58+
handleDM?: (event: UserContextMenuInteraction) => Promise<void>;
59+
60+
build(): ContextMenuCommandBuilder;
61+
}
62+
63+
export interface MessageContextMenuInteractionHandler extends BaseInteractionHandler {
64+
/** Handle the command when sent to a guild. */
65+
handleGuild?: (event: MessageContextMenuInteraction) => Promise<void>;
66+
67+
/** Handle the command when sent to a DM. */
68+
handleDM?: (event: MessageContextMenuInteraction) => Promise<void>;
69+
5470
build(): ContextMenuCommandBuilder;
5571
}
5672

@@ -75,12 +91,14 @@ function loadInteractions<T extends BaseInteractionHandler>(path: string): { [k:
7591

7692
export class InteractionManager {
7793
private commands: Index<CommandInteractionHandler>;
78-
private menus: Index<ContextMenuInteractionHandler>;
94+
private usermenus: Index<UserContextMenuInteractionHandler>;
95+
private messagemenus: Index<MessageContextMenuInteractionHandler>;
7996
private buttons: Index<ButtonInteractionHandler>;
8097

8198
constructor(readonly root: string, private readonly client: Makibot) {
8299
this.commands = loadInteractions(path.join(root, "commands"));
83-
this.menus = loadInteractions(path.join(root, "menus"));
100+
this.usermenus = loadInteractions(path.join(root, "usermenus"));
101+
this.messagemenus = loadInteractions(path.join(root, "messagemenus"));
84102
this.buttons = loadInteractions(path.join(root, "buttons"));
85103
client.on("interactionCreate", this.handleInteraction.bind(this));
86104
}
@@ -90,7 +108,11 @@ export class InteractionManager {
90108
await this.handleCommandInteraction(interaction);
91109
}
92110
if (interaction.isContextMenu()) {
93-
await this.handleContextMenuInteraction(interaction);
111+
if (interaction.isUserContextMenu()) {
112+
await this.handleUserContextMenuInteraction(interaction);
113+
} else if (interaction.isMessageContextMenu()) {
114+
await this.handleMessageContextMenuInteraction(interaction);
115+
}
94116
}
95117
if (interaction.isButton()) {
96118
await this.handleButtonInteraction(interaction);
@@ -132,10 +154,77 @@ export class InteractionManager {
132154
}
133155
}
134156

135-
private async handleContextMenuInteraction(interaction: ContextMenuInteraction): Promise<void> {
136-
const handler = this.menus[interaction.commandName];
157+
private async handleUserContextMenuInteraction(
158+
interaction: UserContextMenuInteraction
159+
): Promise<void> {
160+
const handler = this.usermenus[interaction.commandName];
161+
if (handler) {
162+
if (interaction.inGuild()) {
163+
if (handler.handleGuild) {
164+
await handler.handleGuild(interaction);
165+
} else {
166+
const toast = createToast({
167+
title: "Menú no apto en una guild",
168+
description: "Este menú no se puede pulsar en una guild",
169+
severity: "error",
170+
});
171+
await interaction.reply({
172+
embeds: [toast],
173+
ephemeral: true,
174+
});
175+
}
176+
} else {
177+
if (handler.handleDM) {
178+
await handler.handleDM(interaction);
179+
} else {
180+
const toast = createToast({
181+
title: "Menú no apto fuera de una guild",
182+
description: "Este menú no se puede pulsar fuera de una guild",
183+
severity: "error",
184+
});
185+
await interaction.reply({
186+
embeds: [toast],
187+
ephemeral: true,
188+
});
189+
}
190+
}
191+
}
192+
}
193+
194+
private async handleMessageContextMenuInteraction(
195+
interaction: MessageContextMenuInteraction
196+
): Promise<void> {
197+
const handler = this.messagemenus[interaction.commandName];
137198
if (handler) {
138-
await handler.handle(interaction);
199+
if (interaction.inGuild()) {
200+
if (handler.handleGuild) {
201+
await handler.handleGuild(interaction);
202+
} else {
203+
const toast = createToast({
204+
title: "Menú no apto en una guild",
205+
description: "Este menú no se puede pulsar en una guild",
206+
severity: "error",
207+
});
208+
await interaction.reply({
209+
embeds: [toast],
210+
ephemeral: true,
211+
});
212+
}
213+
} else {
214+
if (handler.handleDM) {
215+
await handler.handleDM(interaction);
216+
} else {
217+
const toast = createToast({
218+
title: "Menú no apto fuera de una guild",
219+
description: "Este menú no se puede pulsar fuera de una guild",
220+
severity: "error",
221+
});
222+
await interaction.reply({
223+
embeds: [toast],
224+
ephemeral: true,
225+
});
226+
}
227+
}
139228
}
140229
}
141230

0 commit comments

Comments
 (0)