Skip to content

Commit 4d2e839

Browse files
feat: add slash command for actions and add-message and bump version
1 parent f692d87 commit 4d2e839

File tree

13 files changed

+488
-235
lines changed

13 files changed

+488
-235
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "message-manager-backend",
3-
"version": "2.0.0",
3+
"version": "2.2.0",
44
"description": "Backend for the message manager site",
55
"main": "./dist/index.js",
66
"exports": "./dist/index.js",

src/discord_commands/global.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,21 @@
147147
],
148148
"dm_permission": false
149149
},
150+
{
151+
"default_permission": true,
152+
"type": 1,
153+
"name": "actions",
154+
"description": "Start actions on a message - edit, delete, or report",
155+
"options": [
156+
{
157+
"name": "message-id",
158+
"type": 3,
159+
"description": "The ID of the message to get actions for - must be in the same channel.",
160+
"required": true
161+
}
162+
],
163+
"dm_permission": false
164+
},
150165
{
151166
"default_permission": true,
152167
"type": 1,
Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
{
2-
"name": "Add Message",
3-
"type": 3
4-
}
1+
[
2+
{
3+
"name": "Add Message",
4+
"type": 3
5+
},
6+
{
7+
"type": 1,
8+
"name": "add-message",
9+
"description": "Add a message to the bot - use this to migrate old messages",
10+
"options": [
11+
{
12+
"name": "message-id",
13+
"type": 3,
14+
"description": "The ID of the message to add - must be in the same channel.",
15+
"required": true
16+
}
17+
]
18+
}
19+
]

src/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ enum InteractionOrRequestFinalStatus {
3535
EMBED_REQUIRES_TITLE_OR_DESCRIPTION,
3636
MESSAGE_GENERATION_CACHE_NOT_FOUND,
3737
MIGRATION_ATTEMPTED_ON_MESSAGE_WITH_MULTIPLE_EMBEDS,
38+
MESSAGE_NOT_FOUND_DISCORD_DELETED_OR_NOT_EXIST,
3839
GENERIC_EXPECTED_PERMISSIONS_FAILURE = 3000,
3940
USER_MISSING_DISCORD_PERMISSION,
4041
BOT_MISSING_DISCORD_PERMISSION,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { DiscordAPIError } from "@discordjs/rest";
2+
import {
3+
APIApplicationCommandInteractionDataStringOption,
4+
APIChatInputApplicationCommandGuildInteraction,
5+
ApplicationCommandOptionType,
6+
RESTGetAPIChannelMessageResult,
7+
Routes,
8+
} from "discord-api-types/v9";
9+
import { FastifyInstance } from "fastify";
10+
11+
import {
12+
ExpectedFailure,
13+
InteractionOrRequestFinalStatus,
14+
UnexpectedFailure,
15+
} from "../../../errors";
16+
import { GuildSession } from "../../../lib/session";
17+
import { InternalInteractionType } from "../../interaction";
18+
import { actionsLogic } from "../../shared/actions";
19+
import { InteractionReturnData } from "../../types";
20+
21+
export default async function handleActionsCommand(
22+
internalInteraction: InternalInteractionType<APIChatInputApplicationCommandGuildInteraction>,
23+
session: GuildSession,
24+
instance: FastifyInstance
25+
): Promise<InteractionReturnData> {
26+
const interaction = internalInteraction.interaction;
27+
// First option: Message Id
28+
const messageId: string | undefined = (
29+
interaction.data.options?.find(
30+
(option) =>
31+
option.name === "message-id" &&
32+
option.type === ApplicationCommandOptionType.String
33+
) as APIApplicationCommandInteractionDataStringOption
34+
)?.value;
35+
if (!messageId) {
36+
throw new UnexpectedFailure(
37+
InteractionOrRequestFinalStatus.APPLICATION_COMMAND_MISSING_EXPECTED_OPTION,
38+
"No message id option on actions command"
39+
);
40+
}
41+
// Fetch message from api
42+
try {
43+
const message = (await instance.restClient.get(
44+
Routes.channelMessage(interaction.channel_id, messageId)
45+
)) as RESTGetAPIChannelMessageResult;
46+
return await actionsLogic({
47+
instance,
48+
interaction,
49+
message,
50+
session,
51+
});
52+
} catch (error) {
53+
if (!(error instanceof DiscordAPIError)) {
54+
throw error;
55+
}
56+
// Handle forbidden and not found, with separate messages
57+
if (error.status === 403) {
58+
throw new ExpectedFailure(
59+
InteractionOrRequestFinalStatus.BOT_MISSING_DISCORD_PERMISSION,
60+
"The bot is missing the discord permissions to access that message"
61+
);
62+
} else if (error.status === 404) {
63+
throw new ExpectedFailure(
64+
InteractionOrRequestFinalStatus.MESSAGE_NOT_FOUND_DISCORD_DELETED_OR_NOT_EXIST,
65+
"That message could not be found, make sure you are using the command in the same channel as the message"
66+
);
67+
}
68+
throw error;
69+
}
70+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { DiscordAPIError } from "@discordjs/rest";
2+
import {
3+
APIApplicationCommandInteractionDataStringOption,
4+
APIChatInputApplicationCommandGuildInteraction,
5+
ApplicationCommandOptionType,
6+
RESTGetAPIChannelMessageResult,
7+
Routes,
8+
} from "discord-api-types/v9";
9+
import { FastifyInstance } from "fastify";
10+
11+
import {
12+
ExpectedFailure,
13+
InteractionOrRequestFinalStatus,
14+
UnexpectedFailure,
15+
} from "../../../errors";
16+
import { GuildSession } from "../../../lib/session";
17+
import { InternalInteractionType } from "../../interaction";
18+
import { addMessageLogic } from "../../shared/addMessage";
19+
import { InteractionReturnData } from "../../types";
20+
21+
export default async function handleAddMessageCommand(
22+
internalInteraction: InternalInteractionType<APIChatInputApplicationCommandGuildInteraction>,
23+
session: GuildSession,
24+
instance: FastifyInstance
25+
): Promise<InteractionReturnData> {
26+
const interaction = internalInteraction.interaction;
27+
// First option: Message Id
28+
const messageId: string | undefined = (
29+
interaction.data.options?.find(
30+
(option) =>
31+
option.name === "message-id" &&
32+
option.type === ApplicationCommandOptionType.String
33+
) as APIApplicationCommandInteractionDataStringOption
34+
)?.value;
35+
if (!messageId) {
36+
throw new UnexpectedFailure(
37+
InteractionOrRequestFinalStatus.APPLICATION_COMMAND_MISSING_EXPECTED_OPTION,
38+
"No message id option on add message command"
39+
);
40+
}
41+
// Fetch message from api
42+
try {
43+
const message = (await instance.restClient.get(
44+
Routes.channelMessage(interaction.channel_id, messageId)
45+
)) as RESTGetAPIChannelMessageResult;
46+
return await addMessageLogic({
47+
instance,
48+
interaction,
49+
message,
50+
session,
51+
});
52+
} catch (error) {
53+
if (!(error instanceof DiscordAPIError)) {
54+
throw error;
55+
}
56+
// Handle forbidden and not found, with separate messages
57+
if (error.status === 403) {
58+
throw new ExpectedFailure(
59+
InteractionOrRequestFinalStatus.BOT_MISSING_DISCORD_PERMISSION,
60+
"The bot is missing the discord permissions to access that message"
61+
);
62+
} else if (error.status === 404) {
63+
throw new ExpectedFailure(
64+
InteractionOrRequestFinalStatus.MESSAGE_NOT_FOUND_DISCORD_DELETED_OR_NOT_EXIST,
65+
"That message could not be found, make sure you are using the command in the same channel as the message"
66+
);
67+
}
68+
throw error;
69+
}
70+
}
Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
import {
2-
APIButtonComponent,
32
APIMessage,
43
APIMessageApplicationCommandGuildInteraction,
5-
ButtonStyle,
6-
ComponentType,
7-
InteractionResponseType,
8-
MessageFlags,
94
} from "discord-api-types/v9";
105
import { FastifyInstance } from "fastify";
116

12-
import { embedPink } from "../../../constants";
137
import {
148
InteractionOrRequestFinalStatus,
159
UnexpectedFailure,
1610
} from "../../../errors";
17-
import { getMessageActionsPossible } from "../../../lib/messages/checks";
1811
import { GuildSession } from "../../../lib/session";
19-
import { addTipToEmbed } from "../../../lib/tips";
2012
import { InternalInteractionType } from "../../interaction";
13+
import { actionsLogic } from "../../shared/actions";
2114
import { InteractionReturnData } from "../../types";
2215

23-
export default async function handleActionMessageCommand(
16+
export default function handleActionMessageCommand(
2417
internalInteraction: InternalInteractionType<APIMessageApplicationCommandGuildInteraction>,
2518
session: GuildSession,
2619
instance: FastifyInstance
@@ -39,62 +32,10 @@ export default async function handleActionMessageCommand(
3932
"Message not found in resolved data"
4033
);
4134
}
42-
// The result does not need to be checked if it is not possible it will throw
43-
const possibleActions = await getMessageActionsPossible({
35+
return actionsLogic({
36+
interaction,
4437
message,
45-
instance,
46-
guildId: interaction.guild_id,
4738
session,
39+
instance,
4840
});
49-
50-
const components: APIButtonComponent[] = [];
51-
if (possibleActions.edit) {
52-
components.push({
53-
type: ComponentType.Button,
54-
custom_id: `edit:${message.id}`,
55-
label: "Edit",
56-
style: ButtonStyle.Success,
57-
});
58-
}
59-
if (possibleActions.delete) {
60-
components.push({
61-
type: ComponentType.Button,
62-
custom_id: `delete:${message.id}`,
63-
label: "Delete",
64-
style: ButtonStyle.Danger,
65-
});
66-
}
67-
components.push({
68-
type: ComponentType.Button,
69-
custom_id: `report:${message.id}`,
70-
label: "Report",
71-
style: ButtonStyle.Danger,
72-
});
73-
74-
const messageLink = `https://discord.com/channels/${interaction.guild_id}/${message.channel_id}/${message.id}`;
75-
76-
return {
77-
type: InteractionResponseType.ChannelMessageWithSource,
78-
data: {
79-
flags: MessageFlags.Ephemeral,
80-
embeds: [
81-
addTipToEmbed({
82-
title: "Message Actions",
83-
color: embedPink,
84-
description:
85-
`Click on the below buttons to edit, delete, or report [this message](${messageLink})` +
86-
`\nIf the action is not available, you may be missing the required permissions for that action.`,
87-
88-
timestamp: new Date().toISOString(),
89-
url: messageLink,
90-
}),
91-
],
92-
components: [
93-
{
94-
type: ComponentType.ActionRow,
95-
components,
96-
},
97-
],
98-
},
99-
};
10041
}

0 commit comments

Comments
 (0)