-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModmailMessageService.ts
More file actions
355 lines (288 loc) · 13.8 KB
/
ModmailMessageService.ts
File metadata and controls
355 lines (288 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
import { Logger } from "pino";
import Modmail from "../schema/v1/Modmail";
import { ModmailRepo } from "../repositories/ModmailRepo";
import { Arc3 } from "../arc3.js";
import { SendAttachmentsAndMessageToUser, SendAttachmentsAndMessageToWebhook, SendModmailSelectMenu } from "../util/ModmailUtils.js";
import { Locale, useTextContent } from "../hooks/useTextContent.js";
import { Message, PartialMessage } from "discord.js";
import { ModmailModeratorMessageEmbed } from "../../ui/ModmailUi.js";
export class ModmailMessageService {
private logger : Logger;
private modmailRepo : ModmailRepo;
constructor(modmailRepo: ModmailRepo) {
this.logger = Arc3.Arc3.clientLogger.child("ModmailMessageService");
this.modmailRepo = modmailRepo;
}
/**
* Processes a modmail message received.
* It checks if the message is in a DM channel and processes it accordingly.
* @param {Message} message - The message object to process.
*/
public async ProcessModmailMessageRecieved(this: ModmailMessageService, message: Message) {
if (message.channel.isDMBased())
return await this.ProcessModmailDmMessageRecieved(message);
const activeModmails = await this.modmailRepo.getActiveModmails();
if (activeModmails.find(m => m.channelsnowflake?.toString() === message.channelId )) {
return await this.ProcessModmailChannelMessageRecieved(message);
}
}
/**
* Processes a modmail message update.
* It checks if the message is in a DM channel and processes it accordingly.
* @param {Message | PartialMessage} oldMessage - The old message object.
* @param {Message} newMessage - The new message object.
* @return {Promise<void>}
*/
public async ProcessModmailMessageUpdated(this: ModmailMessageService, oldMessage: Message | PartialMessage, newMessage: Message) {
const { text } = useTextContent(Locale.EN).actions;
if (oldMessage.channel.isDMBased()) {
const correspondingMessage = this.modmailRepo.getRecentModmailMessageID(oldMessage.id);
if (!correspondingMessage) {
return;
}
this.EditModmailWebhookMessage(oldMessage, newMessage, correspondingMessage)
.then( async _ => {
this.logger.info("Edited modmail webhook message for DM message %s", oldMessage.id);
await newMessage.react(text('arc.modmail.delivery.emoji.edited'));
})
.catch( e => this.logger.error(e) );
} else {
const activeModmails = await this.modmailRepo.getActiveModmails();
const modmail = activeModmails.find(m => m.channelsnowflake?.toString() === newMessage.channelId);
if (modmail && !newMessage.author.bot) {
await this.HandleModeratorMessageEdit(oldMessage, newMessage, modmail);
}
}
}
/**
* Edits a modmail webhook message when the original message is edited.
* @param {Message | PartialMessage} oldMessage - The old message object.
* @param {Message} newMessage - The new message object.
* @param {string} correspondingMessage - The ID of the corresponding webhook message.
* @returns {Promise<void>}
*/
private async EditModmailWebhookMessage(oldMessage: Message | PartialMessage, newMessage: Message, correspondingMessage: string) {
const modmails = await this.modmailRepo.getActiveModmails();
const modmail = modmails.find( m => m.usersnowflake?.toString() === oldMessage.author?.id );
if (!modmail) {
this.logger.warn("No active modmail found for user %s when processing updated modmail message", oldMessage.author?.id);
return;
}
const webhook = await Arc3.Arc3.clientInstance.fetchWebhook(modmail?.webhooksnowflake?.toString() ?? "0")
if (!webhook) {
this.logger.error("No webhook found for modmail %s when processing updated modmail message", modmail._id?.toString());
return;
}
await webhook.editMessage(correspondingMessage, {
content: newMessage.content,
embeds: newMessage.embeds,
components: newMessage.components
});
await this.modmailRepo.CreateTranscript(
modmail,
newMessage.author.id,
newMessage.attachments.map(x => x.proxyURL ),
newMessage.createdAt,
newMessage.content,
newMessage.guildId?? "0",
"Modmail",
false,
oldMessage.id
)
}
/**
* Handles moderator message edits in modmail.
* Edits the existing embed in the user's DM and creates a new transcript entry.
* @param {Message | PartialMessage} oldMessage - The old message object.
* @param {Message} newMessage - The new message object.
* @param {InstanceType<typeof Modmail>} modmail - The modmail object.
*/
private async HandleModeratorMessageEdit(this: ModmailMessageService, oldMessage: Message | PartialMessage, newMessage: Message, modmail: InstanceType<typeof Modmail>) {
const { text } = useTextContent(Locale.EN).actions;
const correspondingUserMessageId = this.modmailRepo.getRecentModmailMessageID(newMessage.id);
if (newMessage.content.startsWith('#')) {
await this.modmailRepo.CreateTranscript(
modmail,
newMessage.author.id,
newMessage.attachments.map(x => x.proxyURL),
newMessage.createdAt,
newMessage.content,
newMessage.guildId ?? "0",
"Modmail",
true,
newMessage.id
);
return;
}
if (!correspondingUserMessageId) {
this.logger.warn("No corresponding user message found for moderator message edit %s", newMessage.id);
return;
}
try {
const user = await Arc3.Arc3.clientInstance.users.fetch(modmail.usersnowflake?.toString() ?? "0");
const dmChannel = await user.createDM();
const userMessage = await dmChannel.messages.fetch(correspondingUserMessageId);
const editedEmbed = ModmailModeratorMessageEmbed(
newMessage.author.username,
newMessage.author.displayAvatarURL(),
newMessage.content
);
await userMessage.edit({ embeds: [editedEmbed] });
await newMessage.react(text('arc.modmail.delivery.emoji.edited'));
await this.modmailRepo.CreateTranscript(
modmail,
newMessage.author.id,
newMessage.attachments.map(x => x.proxyURL),
newMessage.createdAt,
newMessage.content,
newMessage.guildId ?? "0",
"Modmail",
false,
newMessage.id
);
} catch (e) {
this.logger.error(e, "Error editing message in user DM for modmail: " + modmail._id?.toString());
}
}
/**
* Processes a modmail message received in a modmail channel.
* It checks if the message is from a bot and processes it accordingly.
* @param {Message} message - The message object to process.
* @returns {Promise<void>}
*/
private async ProcessModmailChannelMessageRecieved(this: ModmailMessageService, message: Message) {
if (message.author.bot)
return;
const modmails = await this.modmailRepo.getActiveModmails();
const modmail = modmails.find(m => m.channelsnowflake?.toString() === message.channelId );
if (!modmail) {
this.logger.warn("No modmail found for channel %s", message.channelId);
return;
}
// Check if message is a comment (starts with #)
if (message.content.startsWith('#')) {
await this.HandleModmailComment(message, modmail);
return;
}
await this.ProcessModmailMessageToUser(message, modmail);
}
/**
* Processes a modmail message received in a DM channel.
* It checks if the user has an active modmail and processes the message accordingly.
* If no active modmail is found, it handles the creation of a new modmail.
* @param {Message} message - The message object to process.
*/
private async ProcessModmailDmMessageRecieved( this: ModmailMessageService, message: Message) {
// We need to guard certain dm messages to save performance.
// If it is from a bot we can safely ignore
if (message.author.bot)
return;
// Get the active modmails
const modmails = await this.modmailRepo.getActiveModmails();
const usersWithOpenModmail = modmails.map( (x: InstanceType<typeof Modmail>) => x.usersnowflake?.toString());
// Guard that the user has an active modmail
if (!usersWithOpenModmail.includes(message.author.id)) {
await this.HandleNewModmail(message);
return;
}
if (message.content.toLowerCase() === "close session")
// TODO Handle close modmail
return
// We have established the user has an active modmail, is not a bot, and does not wish to close the session
await this.ProcessModmailMessageToGuild(
message,
modmails.filter((x: InstanceType<typeof Modmail>)=> x.usersnowflake?.toString() === message.author.id)[0]
);
}
/**
* Processes a modmail message and sends it to the corresponding guild.
* This method is called when an active modmail is found for the user.
* @param {Message} message - The message object to process.
* @param {any} modmail - The modmail object associated with the user.
*/
private async ProcessModmailMessageToGuild(this: ModmailMessageService, message: Message, modmail: InstanceType<typeof Modmail>) {
// Get the active modmail webhook
const webhook = await Arc3.Arc3.clientInstance.fetchWebhook(modmail.webhooksnowflake?.toString() ?? "0");
const { text } = useTextContent(Locale.EN).actions;
SendAttachmentsAndMessageToWebhook(message, webhook)
.then(async (webhookMessageID) => {
if (webhookMessageID)
this.modmailRepo.addRecentMessage(message.id, webhookMessageID)
await message.react(text('arc.modmail.delivery.emoji.delivered'));
await this.modmailRepo.CreateTranscript(
modmail,
message.author.id,
message.attachments.map(x => x.proxyURL ),
message.createdAt,
message.content,
message.guildId?? "0",
"Modmail",
false,
message.id
);
}).catch(async (e) => {
this.logger.error(e, "Error sending message in modmail: " + modmail._id?.toString())
await message.react(text('arc.modmail.delivery.emoji.failed'));
});
}
/**
* Processes a modmail message from moderator to user.
* @param {Message} message - The message object from the modmail channel.
* @param {InstanceType<typeof Modmail>} modmail - The modmail object.
*/
private async ProcessModmailMessageToUser(this: ModmailMessageService, message: Message, modmail: InstanceType<typeof Modmail>) {
const user = await Arc3.Arc3.clientInstance.users.fetch(modmail.usersnowflake?.toString() ?? "0");
const { text } = useTextContent(Locale.EN).actions;
SendAttachmentsAndMessageToUser(message, user)
.then( async ( userMessageId ) => {
if (userMessageId)
this.modmailRepo.addRecentMessage(message.id, userMessageId);
await message.react(text('arc.modmail.delivery.emoji.delivered'));
await this.modmailRepo.CreateTranscript(
modmail,
message.author.id,
message.attachments.map(x => x.proxyURL),
message.createdAt,
message.content,
message.guildId ?? "0",
"Modmail",
false,
message.id
);
}).catch( async (e) => {
this.logger.error(e, "Error sending message to user in modmail: " + modmail._id?.toString());
await message.react(text('arc.modmail.delivery.emoji.failed'));
});
}
/**
* Handles a comment message in modmail (messages starting with #).
* Comments are logged but not sent to the user.
* @param {Message} message - The message object.
* @param {InstanceType<typeof Modmail>} modmail - The modmail object.
*/
private async HandleModmailComment(this: ModmailMessageService, message: Message, modmail: InstanceType<typeof Modmail>) {
await this.modmailRepo.CreateTranscript(
modmail,
message.author.id,
message.attachments.map(x => x.proxyURL),
message.createdAt,
message.content,
message.guildId ?? "0",
"Modmail",
true,
message.id
);
this.logger.info("Comment added to modmail %s by %s", modmail._id?.toString(), message.author.tag);
}
/**
* Handles the creation of a new modmail when no active modmail is found for the user.
* It sends a message to the user with a select menu to choose a server for modmail.
* @param {Message} message - The message object to process.
*/
private async HandleNewModmail(this: ModmailMessageService, message: Message) {
if (!(message.content.includes("mod") || message.content.includes("mail")))
return;
this.logger.info("Creating new Modmail");
await SendModmailSelectMenu(message);
}
}