Skip to content

Commit e51a088

Browse files
authored
Add a "close ticket" button and log closure to #mod-log (#91)
closes #89
1 parent 9dc2310 commit e51a088

File tree

1 file changed

+96
-5
lines changed

1 file changed

+96
-5
lines changed

Diff for: app/commands/setupTickets.ts

+96-5
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import type {
55
ChatInputCommandInteraction,
66
} from "discord.js";
77
import {
8-
ButtonStyle,
98
ComponentType,
9+
ActionRowBuilder,
10+
ButtonBuilder,
11+
ButtonStyle,
1012
PermissionFlagsBits,
1113
SlashCommandBuilder,
1214
InteractionResponseType,
@@ -47,8 +49,80 @@ export const command = new SlashCommandBuilder()
4749

4850
export const webserver: RequestHandler = async (req, res, next) => {
4951
const body = req.body as APIInteraction;
50-
// @ts-expect-error because apparently custom_id types are broken
51-
console.log("hook:", body.data.component_type, body.data.custom_id);
52+
53+
if (
54+
// @ts-expect-error because apparently custom_id types are broken
55+
body.data.component_type === 2 &&
56+
// @ts-expect-error because apparently custom_id types are broken
57+
body.data.custom_id.includes("close-ticket")
58+
) {
59+
// @ts-expect-error because apparently custom_id types are broken
60+
const [, ticketOpenerUserId] = body.data.custom_id.split("||");
61+
const threadId = body.message?.channel_id;
62+
if (!body.member) {
63+
console.error(
64+
"[err]: no member in ticket interaction",
65+
JSON.stringify(body),
66+
);
67+
res.send({
68+
type: InteractionResponseType.ChannelMessageWithSource,
69+
data: {
70+
content: "Something went wrong",
71+
flags: MessageFlags.Ephemeral,
72+
},
73+
});
74+
return;
75+
}
76+
77+
const { [SETTINGS.moderator]: mod, [SETTINGS.modLog]: modLog } =
78+
await fetchSettings(
79+
// @ts-expect-error because this shouldn't have used a Guild instance but
80+
// it's a lot to refactor
81+
{ id: body.guild_id },
82+
[SETTINGS.moderator, SETTINGS.modLog],
83+
);
84+
85+
const { roles, user } = body.member;
86+
const interactionUserId = user.id;
87+
88+
if (
89+
!threadId ||
90+
(!roles?.includes(mod) && ticketOpenerUserId !== interactionUserId)
91+
) {
92+
res.send({
93+
type: InteractionResponseType.ChannelMessageWithSource,
94+
data: {
95+
content: "This isn't your ticket to close!",
96+
flags: MessageFlags.Ephemeral,
97+
},
98+
});
99+
return;
100+
}
101+
102+
// TODO: await interaction.channel.setLocked(true);
103+
await Promise.all([
104+
rest.delete(Routes.threadMembers(threadId, ticketOpenerUserId)),
105+
rest.post(Routes.channelMessages(modLog), {
106+
body: {
107+
content: `<@${ticketOpenerUserId}>’s ticket <#${threadId}> closed by <@${interactionUserId}> `,
108+
mentions: [],
109+
flags: MessageFlags.SuppressNotifications,
110+
},
111+
}),
112+
res.send({
113+
type: InteractionResponseType.ChannelMessageWithSource,
114+
data: {
115+
content: `The ticket was closed by <@${ticketOpenerUserId}>`,
116+
mentions: [],
117+
flags: MessageFlags.SuppressNotifications,
118+
},
119+
}),
120+
]);
121+
122+
return;
123+
}
124+
125+
// Handle "open ticket" button pressed
52126
// @ts-expect-error because apparently custom_id types are broken
53127
if (body.data.component_type === 2 && body.data.custom_id === "open-ticket") {
54128
res.send({
@@ -76,6 +150,8 @@ export const webserver: RequestHandler = async (req, res, next) => {
76150
});
77151
return;
78152
}
153+
154+
// Handle "what's up" modal submission
79155
if (isModalInteraction(body)) {
80156
if (
81157
!body.channel ||
@@ -98,7 +174,7 @@ export const webserver: RequestHandler = async (req, res, next) => {
98174
// @ts-expect-error because this shouldn't have used a Guild instance but
99175
// it's a lot to refactor
100176
{ id: body.guild_id },
101-
[SETTINGS.moderator],
177+
[SETTINGS.moderator, SETTINGS.modLog],
102178
);
103179
const thread = (await rest.post(Routes.threads(body.channel.id), {
104180
body: {
@@ -112,7 +188,7 @@ export const webserver: RequestHandler = async (req, res, next) => {
112188
})) as RESTPostAPIChannelThreadsResult;
113189
await rest.post(Routes.channelMessages(thread.id), {
114190
body: {
115-
content: `<@${body.message.interaction_metadata.user.id}>, this is a private space only visible to the <@&${mod}> role.`,
191+
content: `<@${body.message.interaction_metadata.user.id}>, this is a private space only visible to you and the <@&${mod}> role.`,
116192
} as RESTPostAPIChannelMessageJSONBody,
117193
});
118194
await rest.post(Routes.channelMessages(thread.id), {
@@ -122,6 +198,21 @@ export const webserver: RequestHandler = async (req, res, next) => {
122198
)}`,
123199
},
124200
});
201+
await rest.post(Routes.channelMessages(thread.id), {
202+
body: {
203+
content: "When you’ve finished, please close the ticket.",
204+
components: [
205+
new ActionRowBuilder().addComponents(
206+
new ButtonBuilder()
207+
.setCustomId(
208+
`close-ticket||${body.message.interaction_metadata.user.id}`,
209+
)
210+
.setLabel("Close ticket")
211+
.setStyle(ButtonStyle.Danger),
212+
),
213+
],
214+
},
215+
});
125216

126217
res.send({
127218
type: InteractionResponseType.ChannelMessageWithSource,

0 commit comments

Comments
 (0)