Skip to content

Commit 5565d59

Browse files
committed
✨ initial basic modmail features
1 parent e4ebaa9 commit 5565d59

19 files changed

+747
-419
lines changed

src/v2/cache/cacheFns.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ export async function upsert(options: CacheUpsertOptions) {
3333
const { guild, type, user, meta } = options;
3434

3535
// Cannot use destructuring due to the properties being optional and TS not liking it
36-
// eslint-disable-next-line unicorn/consistent-destructuring
37-
const expireTime = "expiresAt" in options ? options.expiresAt : Date.now() + options.expiresIn;
36+
37+
const expireTime =
38+
'expiresAt' in options ? options.expiresAt : Date.now() + options.expiresIn;
3839

3940
const result = await GenericCache.findOneAndUpdate(
4041
{
@@ -62,3 +63,19 @@ async function _purge() {
6263
timestamp: { $lt: Date.now() },
6364
});
6465
}
66+
67+
export async function purgeType({
68+
guild,
69+
type,
70+
}: {
71+
guild: string;
72+
type: string;
73+
}) {
74+
await Promise.all([
75+
_purge(),
76+
GenericCache.deleteMany({
77+
type,
78+
guild,
79+
}),
80+
]);
81+
}

src/v2/commands/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,17 @@ export const registerCommands = async (client: Client): Promise<void> => {
125125
}
126126
} catch (error) {
127127
console.error(error);
128-
await interaction.reply({
129-
ephemeral: true,
130-
content: 'Something went wrong when trying to execute the command',
131-
});
128+
129+
if (interaction.deferred) {
130+
await interaction.editReply({
131+
content: 'Something went wrong when trying to execute the command',
132+
});
133+
} else {
134+
await interaction.reply({
135+
ephemeral: true,
136+
content: 'Something went wrong when trying to execute the command',
137+
});
138+
}
132139
}
133140
})
134141
);

src/v2/commands/post/index.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { cache } from '../../spam_filter/index.js';
2222
import { MultistepForm } from '../../utils/MultistepForm.js';
2323
import { asyncCatch } from '../../utils/asyncCatch.js';
2424
import { createEmbed, createMarkdownCodeBlock } from '../../utils/discordTools.js';
25+
import { lock, unlock } from '../../utils/dmLock';
2526
import { map } from '../../utils/map.js';
2627
import { pipe } from '../../utils/pipe.js';
2728
import { capitalize } from '../../utils/string.js';
@@ -278,12 +279,12 @@ const handleJobPostingRequest = async (
278279
): Promise<void> => {
279280
const { guild, member } = interaction;
280281
const { user: author } = interaction;
281-
const { username, discriminator, id } = author;
282+
const { username, discriminator, id: userID } = author;
282283

283-
const filter: CollectorFilter<[Message]> = m => m.author.id === id;
284+
const filter: CollectorFilter<[Message]> = m => m.author.id === userID;
284285
const send = (str: string) => author.send(str);
285286
// Generate cache entry
286-
const entry = generateCacheEntry(id);
287+
const entry = generateCacheEntry(userID);
287288

288289
try {
289290
// Check if the user has been cached
@@ -312,10 +313,10 @@ const handleJobPostingRequest = async (
312313

313314
// Notify the user regarding the rules, and get the channel
314315
const channel = await author.createDM();
315-
316316
const form = new MultistepForm(questions, channel, author);
317-
317+
lock(guild.id, userID, 'JOB_POST_FORM')
318318
const answers = (await form.getResult('guidelines')) as unknown as Answers;
319+
unlock(guild.id, userID, 'JOB_POST_FORM')
319320

320321
console.log(answers)
321322
// Just return if the iteration breaks due to invalid input
@@ -326,7 +327,7 @@ const handleJobPostingRequest = async (
326327

327328
const url = await createJobPost(answers, guild, {
328329
discriminator,
329-
userID: id,
330+
userID,
330331
username,
331332
});
332333

src/v2/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import pointDecaySystem, { loadLastDecayFromDB } from './helpful_role/point_deca
3232
import { registerMessageContextMenu } from './message_context/index.js';
3333
import { handleDM } from './modules/modmail/index.js';
3434
import { registerUserContextMenu } from './user_context/index.js';
35-
import { asyncCatch } from './utils/asyncCatch.js';
3635
import { stripMarkdownQuote } from './utils/content_format.js';
36+
import { purge as purgeDMLocks } from './utils/dmLock.js';
3737

3838
const NON_COMMAND_MSG_TYPES = new Set([
3939
'GUILD_TEXT',
@@ -84,6 +84,7 @@ client.on('ready', () => {
8484
});
8585

8686
client.once('ready', async (): Promise<void> => {
87+
purgeDMLocks(SERVER_ID)
8788
pointDecaySystem({
8889
guild: client.guilds.resolve(SERVER_ID)
8990
})
@@ -141,10 +142,11 @@ const isWebdevAndWebDesignServer = (msg: Message) =>
141142
msg.guild?.id === SERVER_ID || false;
142143

143144

144-
client.on('messageCreate', msg => {
145+
client.on('messageCreate', async msg => {
145146
if (msg.author.bot) {
146147
return;
147148
}
149+
148150
if(msg.channel.type === 'DM') {
149151
return handleDM(msg)
150152
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { ThreadChannel } from "discord.js";
2+
3+
import type { CommandDataWithHandler } from "../../../../types";
4+
import { SERVER_ID, MOD_ROLE_ID } from "../../../env";
5+
import { ModMailThread } from "../db/modmail_thread";
6+
import { cache } from "../util/cache";
7+
8+
export const closeCommand: CommandDataWithHandler = {
9+
type: "CHAT_INPUT",
10+
description: 'Close a modmail thread',
11+
name: "close",
12+
options: [
13+
{
14+
name: 'reason',
15+
description: "Reason or note regarding the closing of the modmail thread",
16+
type: "STRING"
17+
}
18+
],
19+
guildValidate: guild => guild.id === SERVER_ID,
20+
defaultPermission: false,
21+
async managePermissions(guild, permissions) {
22+
await permissions.add({
23+
guild,
24+
permissions: [
25+
{
26+
id: MOD_ROLE_ID,
27+
type: 'ROLE',
28+
permission: true,
29+
},
30+
],
31+
});
32+
},
33+
async handler(client, interaction) {
34+
const [modmailThread] = await Promise.all([
35+
ModMailThread.findOne({ threadId: interaction.channel.id }),
36+
interaction.deferReply(),
37+
]);
38+
39+
if(!modmailThread) {
40+
interaction.editReply({ content: "Could not find modmail thread" })
41+
throw new Error("Could not find modmail thread")
42+
}
43+
44+
if(modmailThread.closedAt) {
45+
interaction.editReply({ content: "This modmail thread is already closed" })
46+
return
47+
}
48+
49+
const thread = interaction.channel as ThreadChannel
50+
51+
await thread.setName(`✅ ${thread.name.replace(/^ /u, '')}`);
52+
53+
await thread.setArchived(true);
54+
55+
cache.del(`${modmailThread.guildId}|${modmailThread.userId}`);
56+
cache.del(modmailThread.threadId);
57+
58+
await modmailThread.updateOne({
59+
closedAt: Date.now()
60+
}).exec();
61+
62+
63+
const reason = interaction.options.get('reason')?.value as string
64+
65+
interaction.editReply({
66+
content: ["Modmail Thread Closed.", reason? `Note/Reason: ${reason}` : null].filter(Boolean).join('\n'),
67+
})
68+
}
69+
}

0 commit comments

Comments
 (0)