/[sudobot]/branches/5.x/src/services/LoggerService.ts
ViewVC logotype

Annotation of /branches/5.x/src/services/LoggerService.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 42404 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 /**
2     * This file is part of SudoBot.
3     *
4     * Copyright (C) 2021-2023 OSN Developers.
5     *
6     * SudoBot is free software; you can redistribute it and/or modify it
7     * under the terms of the GNU Affero General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * SudoBot is distributed in the hope that it will be useful, but
12     * WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU Affero General Public License for more details.
15     *
16     * You should have received a copy of the GNU Affero General Public License
17     * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
18     */
19    
20     import { formatDistanceToNowStrict } from "date-fns";
21     import {
22     APIEmbedField,
23     ActionRowBuilder,
24     AttachmentBuilder,
25     BanOptions,
26     ButtonBuilder,
27     ButtonStyle,
28     ChannelType,
29     Collection,
30     ColorResolvable,
31     Colors,
32     EmbedBuilder,
33     EmbedData,
34     Guild,
35     GuildChannel,
36     GuildMember,
37     Message,
38     MessageCreateOptions,
39     MessagePayload,
40     MessageResolvable,
41     MessageType,
42     NonThreadGuildBasedChannel,
43     Role,
44     TextChannel,
45     User,
46     escapeMarkdown,
47     roleMention
48     } from "discord.js";
49     import Service from "../core/Service";
50     import { MessageRuleType } from "../types/MessageRuleSchema";
51     import { NotUndefined } from "../types/NotUndefined";
52     import { log, logError } from "../utils/logger";
53     import { isTextableChannel } from "../utils/utils";
54     import { GuildConfig } from "./ConfigManager";
55    
56     export const name = "logger";
57    
58     type LoggingChannelType = Exclude<keyof NotUndefined<GuildConfig["logging"]>, "enabled">;
59    
60     export default class LoggerService extends Service {
61     private async send(guild: Guild, options: string | MessagePayload | MessageCreateOptions, channel?: LoggingChannelType) {
62     const channelId =
63     this.client.configManager.config[guild.id]?.logging?.[channel ?? "primary_channel"] ??
64     this.client.configManager.config[guild.id]?.logging?.primary_channel;
65     const enabled = this.client.configManager.config[guild.id]?.logging?.enabled;
66    
67     if (!enabled || !channelId) return null;
68    
69     try {
70     const channel = await guild.channels.fetch(channelId as string);
71    
72     if (!channel || !isTextableChannel(channel)) return null;
73    
74     return await channel.send(options);
75     } catch (e) {
76     logError(e);
77     return null;
78     }
79     }
80    
81     private createLogEmbed({
82     options,
83     title,
84     user,
85     fields,
86     footerText,
87     timestamp,
88     moderator,
89     reason,
90     id,
91     color,
92     showUserId = true
93     }: CreateLogEmbedOptions) {
94     const embed = new EmbedBuilder({
95     title,
96     author: user
97     ? {
98     name: user.tag,
99     iconURL: user.displayAvatarURL()
100     }
101     : undefined,
102     fields: [
103     ...(reason !== undefined
104     ? [
105     {
106     name: "Reason",
107     value: `${reason ?? "*No reason provided*"}`
108     }
109     ]
110     : []),
111     ...(fields ?? []),
112     ...(moderator
113     ? [
114     {
115     name: "Responsible Moderator",
116     value: moderator.id === this.client.user?.id ? "System" : `${moderator.tag} (${moderator.id})`
117     }
118     ]
119     : []),
120     ...(id
121     ? [
122     {
123     name: "Infraction ID",
124     value: `${id}`
125     }
126     ]
127     : []),
128     ...(user && showUserId
129     ? [
130     {
131     name: "User ID",
132     value: user.id
133     }
134     ]
135     : [])
136     ],
137     footer: footerText
138     ? {
139     text: footerText
140     }
141     : undefined,
142     ...options
143     });
144    
145     if (timestamp === undefined) embed.setTimestamp();
146     else if (timestamp) embed.setTimestamp(timestamp);
147    
148     if (color) embed.setColor(color);
149    
150     return embed;
151     }
152    
153     private async sendLogEmbed(
154     guild: Guild,
155     options: CreateLogEmbedOptions,
156     extraOptions?: MessagePayload | MessageCreateOptions,
157     channel?: LoggingChannelType
158     ) {
159     return await this.send(
160     guild,
161     {
162     embeds: [this.createLogEmbed(options)],
163     ...((extraOptions as any) ?? {})
164     },
165     channel
166     );
167     }
168    
169     /*
170     * Logging methods.
171     */
172    
173     async logMessageRuleAction({
174     message,
175     embedOptions = {},
176     rule,
177     actions
178     }: {
179     message: Message;
180     actions: MessageRuleType["actions"];
181     embedOptions?: CreateLogEmbedOptions;
182     rule: MessageRuleType["type"];
183     }) {
184     log("Actions", actions);
185    
186     await this.sendLogEmbed(message.guild!, {
187     color: Colors.Red,
188     user: message.author,
189     footerText: "AutoMod",
190     moderator: this.client.user!,
191     ...embedOptions,
192     fields: [
193     ...(embedOptions.fields ?? []),
194     {
195     name: "Rule",
196     value: `\`${rule}\``,
197     inline: true
198     },
199     {
200     name: "Actions taken",
201     value: `\`${actions.length === 0 ? "none" : actions.join("`, `")}\``,
202     inline: true
203     },
204     {
205     name: "Message",
206     value: `${message.url}`
207     }
208     ]
209     });
210     }
211    
212     async logFileFilterDeletedMessage(
213     message: Message,
214     { contentType, hash, url }: { hash: string; url: string; name?: string; contentType?: string | null }
215     ) {
216     await this.sendLogEmbed(message.guild!, {
217     title: "Blocked file detected",
218     color: Colors.Red,
219     user: message.author,
220     fields: [
221     {
222     name: "File",
223     value:
224     `${name ? `[${escapeMarkdown(name)}](${url})` : `[Unnamed](${url})`}: \`${hash}\`` +
225     (contentType ? ` (\`${contentType}\`)` : "")
226     }
227     ],
228     footerText: "Deleted",
229     moderator: this.client.user!
230     });
231     }
232    
233     async logMemberTimeout(
234     member: GuildMember,
235     { reason, id, moderator }: Omit<CommonUserActionOptions, "guild" | "id"> & { reason?: string; id?: string | number }
236     ) {
237     await this.sendLogEmbed(member.guild, {
238     title: "Member timed-out",
239     color: Colors.Red,
240     user: member.user,
241     fields: [
242     {
243     name: "Duration",
244     value: formatDistanceToNowStrict(member.communicationDisabledUntil!)
245     },
246     {
247     name: "User Information",
248     value: `Username: ${member.user.username}\nMention: ${member.user.toString()}\nID: ${member.user.id}`
249     }
250     ],
251     footerText: "Timed-out",
252     reason,
253     id: id?.toString(),
254     moderator
255     });
256     }
257    
258     async logMemberTimeoutRemove(
259     member: GuildMember,
260     { reason, id, moderator }: Omit<CommonUserActionOptions, "guild" | "id"> & { reason?: string; id?: string | number }
261     ) {
262     await this.sendLogEmbed(member.guild, {
263     title: "Member timeout removed",
264     color: Colors.Green,
265     user: member.user,
266     fields: [
267     {
268     name: "User Information",
269     value: `Username: ${member.user.username}\nMention: ${member.user.toString()}\nID: ${member.user.id}`
270     }
271     ],
272     footerText: "Timed-out removed",
273     reason,
274     id: id?.toString(),
275     moderator
276     });
277     }
278    
279     async logChannelCreate(channel: NonThreadGuildBasedChannel) {
280     await this.sendLogEmbed(channel.guild, {
281     title: "Channel Created",
282     color: Colors.Green,
283     fields: [
284     {
285     name: "Name",
286     value: channel.name
287     },
288     {
289     name: "ID",
290     value: channel.id
291     },
292     {
293     name: "Mention",
294     value: channel.toString()
295     },
296     {
297     name: "Type",
298     value: ChannelType[channel.type]
299     }
300     ],
301     footerText: "Created"
302     });
303     }
304    
305     async logChannelDelete(channel: NonThreadGuildBasedChannel) {
306     await this.sendLogEmbed(channel.guild, {
307     title: "Channel Deleted",
308     color: Colors.Red,
309     fields: [
310     {
311     name: "Name",
312     value: channel.name
313     },
314     {
315     name: "ID",
316     value: channel.id
317     },
318     {
319     name: "Type",
320     value: ChannelType[channel.type]
321     }
322     ],
323     footerText: "Deleted"
324     });
325     }
326    
327     async logChannelUpdate(oldChannel: NonThreadGuildBasedChannel, newChannel: NonThreadGuildBasedChannel) {
328     await this.sendLogEmbed(newChannel.guild, {
329     title: "Channel Updated",
330     color: Colors.Green,
331     fields: [
332     {
333     name: "Old Name",
334     value: oldChannel.name,
335     inline: true
336     },
337     {
338     name: "New Name",
339     value: newChannel.name,
340     inline: true
341     },
342     {
343     name: "ID",
344     value: newChannel.id
345     }
346     ],
347     footerText: "Updated"
348     });
349     }
350    
351     async logRoleCreate(role: Role) {
352     const permissions = role.permissions.toArray();
353    
354     await this.sendLogEmbed(role.guild, {
355     title: "Role Created",
356     color: Colors.Green,
357     fields: [
358     {
359     name: "Name",
360     value: role.name
361     },
362     {
363     name: "ID",
364     value: role.id
365     },
366     {
367     name: "Mention",
368     value: role.toString()
369     },
370     {
371     name: "Icon",
372     value: role.icon ? role.iconURL()! : "*None*"
373     },
374     {
375     name: "Permissions",
376     value: permissions.length === 0 ? "*Nothing*" : "`" + permissions.join("`, `") + "`"
377     }
378     ],
379     footerText: "Created"
380     });
381     }
382    
383     async logRoleDelete(role: Role) {
384     const permissions = role.permissions.toArray();
385    
386     await this.sendLogEmbed(role.guild, {
387     title: "Role Deleted",
388     color: Colors.Red,
389     fields: [
390     {
391     name: "Name",
392     value: role.name
393     },
394     {
395     name: "ID",
396     value: role.id
397     },
398     {
399     name: "Icon",
400     value: role.icon ? role.iconURL()! : "*None*"
401     },
402     {
403     name: "Permissions",
404     value: permissions.length === 0 ? "*Nothing*" : "`" + permissions.join("`, `") + "`"
405     }
406     ],
407     footerText: "Deleted"
408     });
409     }
410    
411     async logRoleUpdate(oldRole: Role, newRole: Role) {
412     const newRolePermissions = newRole.permissions.toArray();
413     const oldRolePermissions = oldRole.permissions.toArray();
414     const addedPermissions = newRolePermissions.filter(permission => !oldRolePermissions.includes(permission));
415     const removedPermissions = oldRolePermissions.filter(permission => !newRolePermissions.includes(permission));
416    
417     await this.sendLogEmbed(newRole.guild, {
418     title: "Role Updated",
419     color: Colors.Green,
420     fields: [
421     {
422     name: "Name",
423     value: `Old name: ${oldRole.name}\nNew name: ${newRole.name}`
424     },
425     {
426     name: "ID",
427     value: newRole.id
428     },
429     {
430     name: "Mention",
431     value: newRole.toString()
432     },
433     {
434     name: "Icon",
435     value: `Old icon: ${oldRole.icon ? oldRole.iconURL()! : "*None*"}\nNew icon: ${
436     newRole.icon ? newRole.iconURL()! : "*None*"
437     }`
438     },
439     {
440     name: "Added Permissions",
441     value: addedPermissions.length === 0 ? "*Nothing*" : "`" + addedPermissions.join("`, `") + "`",
442     inline: true
443     },
444     {
445     name: "Removed Permissions",
446     value: removedPermissions.length === 0 ? "*Nothing*" : "`" + removedPermissions.join("`, `") + "`",
447     inline: true
448     }
449     ],
450     footerText: "Updated"
451     });
452     }
453    
454     async logNicknameUpdate(oldMember: GuildMember, newMember: GuildMember) {
455     await this.sendLogEmbed(newMember.guild, {
456     title: "Member nickname updated",
457     user: newMember.user,
458     color: 0x007bff,
459     fields: [
460     {
461     name: "Old Nickname",
462     value: oldMember.nickname ?? "*Nothing*",
463     inline: true
464     },
465     {
466     name: "New Nickname",
467     value: newMember.nickname ?? "*Nothing*",
468     inline: true
469     },
470     {
471     name: "User Information",
472     value: `Username: ${newMember.user.username}\nMention: ${newMember.user.toString()}\nID: ${newMember.user.id}`
473     }
474     ],
475     footerText: "Updated"
476     });
477     }
478    
479     async logMemberRoleUpdate(oldMember: GuildMember, newMember: GuildMember) {
480     const added = newMember.roles.cache.filter(role => !oldMember.roles.cache.has(role.id));
481     const removed = oldMember.roles.cache.filter(role => !newMember.roles.cache.has(role.id));
482    
483     await this.sendLogEmbed(
484     newMember.guild,
485     {
486     title: "Member roles updated",
487     user: newMember.user,
488     color: Colors.Green,
489     showUserId: false,
490     fields: [
491     {
492     name: "Added",
493     value: added.size === 0 ? "*Nothing added*" : added.reduce((acc, role) => `${acc} ${role.toString()}`, "")
494     },
495     {
496     name: "Removed",
497     value:
498     removed.size === 0
499     ? "*Nothing removed*"
500     : removed.reduce((acc, role) => `${acc} ${role.toString()}`, "")
501     },
502     {
503     name: "User Information",
504     value: `Username: ${newMember.user.username}\nMention: ${newMember.user.toString()}\nID: ${
505     newMember.user.id
506     }`
507     }
508     ],
509     footerText: "Roles Updated"
510     },
511     {
512     allowedMentions: {
513     roles: []
514     }
515     }
516     );
517     }
518    
519     async logGuildMemberAdd(member: GuildMember) {
520     if (!this.client.configManager.config[member.guild.id]?.logging?.events.member_join) {
521     return;
522     }
523    
524     let members = 0,
525     bots = 0;
526    
527     for (const m of member.guild.members.cache.values()) {
528     if (m.user.bot) bots++;
529     else members++;
530     }
531    
532     const createdAt = Math.round((member.user.createdAt?.getTime() ?? Date.now()) / 1000);
533     const inviteOrVanity = await this.client.inviteTracker.findNewMemberInviteLink(member);
534    
535     console.log(createdAt);
536    
537     await this.sendLogEmbed(
538     member.guild,
539     {
540     title: "New member joined",
541     user: member.user,
542     color: 0x007bff,
543     options: {
544     description: `${member.user.toString()} just joined the server!`
545     },
546     fields: [
547     {
548     name: "New Account?",
549     value: Date.now() - member.user.createdTimestamp < 3 * 24 * 60 * 60 * 1000 ? ":warning: Yes" : "No",
550     inline: true
551     },
552     {
553     name: "Bot?",
554     value: member.user.bot ? "Yes" : "No",
555     inline: true
556     },
557     {
558     name: "Account Created At",
559     value: `<t:${createdAt}:f> (<t:${createdAt}:R>)`
560     },
561     {
562     name: "User Information",
563     value: `Username: ${member.user.username}\nMention: ${member.user.toString()}\nID: ${member.user.id}`,
564     inline: true
565     },
566     {
567     name: "Positions",
568     value:
569     `Among all members: ${members + bots}th\n` +
570     (member.user.bot ? `Among the bots: ${bots}th` : `Among the human members: ${members}th`),
571     inline: true
572     },
573     ...(inviteOrVanity
574     ? [
575     {
576     name: "Invite Information",
577     value:
578     `Invite Link: https://discord.gg/${
579     inviteOrVanity?.vanity?.code ?? inviteOrVanity?.invite?.code
580     }\nUses: ${inviteOrVanity?.vanity?.uses ?? inviteOrVanity?.invite?.uses}` +
581     (!inviteOrVanity.isVanity
582     ? `\nInvited By: ${
583     inviteOrVanity.invite.inviterId
584     ? `<@${inviteOrVanity.invite.inviterId}> (${inviteOrVanity.invite.inviterId})`
585     : "Unknown"
586     }` +
587     (inviteOrVanity.invite.createdAt
588     ? `\nCreated: <t:${Math.round(
589     inviteOrVanity.invite.createdAt.getTime() / 1000
590     )}:R>`
591     : "") +
592     (inviteOrVanity.invite.expiresAt
593     ? `\nExpires: <t:${Math.round(
594     inviteOrVanity.invite.expiresAt.getTime() / 1000
595     )}:R>`
596     : "") +
597     (inviteOrVanity.invite.channelId
598     ? `\nChannel: <#${inviteOrVanity.invite.channelId}> (${inviteOrVanity.invite.channelId})`
599     : "") +
600     (inviteOrVanity.invite.temporary ? `\n\n__This is a temporary invite.__` : "")
601     : "")
602     }
603     ]
604     : [])
605     ],
606     footerText: `Joined • ${
607     member.guild.members.cache.size >= member.guild.memberCount
608     ? member.guild.members.cache.size
609     : member.guild.memberCount
610     } members total`
611     },
612     undefined,
613     "join_leave_channel"
614     );
615     }
616    
617     async logGuildMemberRemove(member: GuildMember) {
618     if (!this.client.configManager.config[member.guild.id]?.logging?.events.member_leave) {
619     return;
620     }
621    
622     const joinedAt = Math.round((member.joinedAt?.getTime() ?? Date.now()) / 1000);
623    
624     await this.sendLogEmbed(
625     member.guild,
626     {
627     title: "Member left",
628     user: member.user,
629     color: 0xf14a60,
630     fields: [
631     {
632     name: "Roles",
633     value:
634     member.roles.cache.size === 1
635     ? "**No roles**"
636     : member.roles.cache
637     .filter(role => role.id !== member.guild.id)
638     .sort((m1, m2) => m2.position - m1.position)
639     .reduce((acc, role) => `${acc} ${roleMention(role.id)}`, "")
640     },
641     {
642     name: "Joined At",
643     value: `<t:${joinedAt}:f> (<t:${joinedAt}:R>)`
644     },
645     {
646     name: "User Information",
647     value: `Username: ${member.user.username}\nMention: ${member.user.toString()}\nID: ${member.user.id}`
648     },
649     {
650     name: "Bot?",
651     value: member.user.bot ? "Yes" : "No",
652     inline: true
653     }
654     ],
655     footerText: `Left • ${
656     member.guild.members.cache.size >= member.guild.memberCount
657     ? member.guild.members.cache.size
658     : member.guild.memberCount
659     } members total`
660     },
661     undefined,
662     "join_leave_channel"
663     );
664     }
665    
666     async logMessageEdit(oldMessage: Message, newMessage: Message) {
667     if (!this.client.configManager.config[newMessage.guildId!]?.logging?.events.message_edit) {
668     return;
669     }
670    
671     const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
672     new ButtonBuilder()
673     .setStyle(ButtonStyle.Link)
674     .setLabel("Go to context")
675     .setURL(`https://discord.com/channels/${newMessage.guildId!}/${newMessage.channelId!}/${newMessage.id}`)
676     );
677    
678     if (newMessage.type === MessageType.Reply)
679     row.addComponents(
680     new ButtonBuilder()
681     .setStyle(ButtonStyle.Link)
682     .setLabel("Go to referenced message")
683     .setURL(
684     `https://discord.com/channels/${newMessage.guildId!}/${newMessage.channelId!}/${
685     newMessage.reference!.messageId
686     }`
687     )
688     );
689    
690     await this.sendLogEmbed(
691     newMessage.guild!,
692     {
693     title: "Message Updated",
694     user: newMessage.author,
695     color: 0x007bff,
696     options: {
697     description: `**-+-+Before**\n${oldMessage.content}\n\n**-+-+After**\n${newMessage.content}`
698     },
699     fields: [
700     {
701     name: "User",
702     value: `${newMessage.author.toString()}\nUsername: ${newMessage.author.username}\nID: ${
703     newMessage.author.id
704     }`
705     },
706     {
707     name: "Channel",
708     value: `${newMessage.channel.toString()}\nName: ${(newMessage.channel as TextChannel).name}\nID: ${
709     newMessage.channel.id
710     }`
711     },
712     {
713     name: "Message",
714     value: `Link: [Click here](${`https://discord.com/channels/${newMessage.guildId!}/${newMessage.channelId!}/${
715     newMessage.id
716     }`})\nID: ${newMessage.id}`
717     }
718     ],
719     footerText: "Updated"
720     },
721     {
722     components: [row]
723     },
724     "message_logging_channel"
725     );
726     }
727    
728     async logMessageDelete(message: Message) {
729     if (!this.client.configManager.config[message.guildId!]?.logging?.events.message_delete) {
730     return;
731     }
732    
733     const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
734     new ButtonBuilder()
735     .setStyle(ButtonStyle.Link)
736     .setLabel("Go to context")
737     .setURL(`https://discord.com/channels/${message.guildId!}/${message.channelId!}/${message.id}`)
738     );
739    
740     if (message.type === MessageType.Reply)
741     row.addComponents(
742     new ButtonBuilder()
743     .setStyle(ButtonStyle.Link)
744     .setLabel("Go to referenced message")
745     .setURL(
746     `https://discord.com/channels/${message.guildId!}/${message.channelId!}/${message.reference!.messageId}`
747     )
748     );
749    
750     await this.sendLogEmbed(
751     message.guild!,
752     {
753     title: "Message Deleted",
754     color: Colors.Red,
755     user: message.author,
756     options: {
757     description: message.content
758     },
759     fields: [
760     {
761     name: "User",
762     value: `${message.author.toString()}\nUsername: ${message.author.username}\nID: ${message.author.id}`
763     },
764     {
765     name: "Channel",
766     value: `${message.channel.toString()}\nName: ${(message.channel as TextChannel).name}\nID: ${
767     message.channel.id
768     }`
769     },
770     {
771     name: "Message",
772     value: `Link: [Click here](${`https://discord.com/channels/${message.guildId!}/${message.channelId!}/${
773     message.id
774     }`})\nID: ${message.id}`
775     }
776     ],
777     footerText: "Deleted"
778     },
779     {
780     components: [row],
781     files: [
782     ...message.attachments
783     .map(
784     a =>
785     ({
786     attachment: a.proxyURL,
787     name: a.name
788     } as AttachmentBuilder)
789     )
790     .values()
791     ]
792     },
793     "message_logging_channel"
794     );
795     }
796    
797     async logRaid({ guild, action }: { guild: Guild; action: string }) {
798     await this.sendLogEmbed(guild, {
799     title: "Possible raid detected",
800     reason: "Too many users joined in a short timeframe.",
801     color: Colors.Red,
802     fields: [
803     {
804     name: "Action",
805     value: action
806     }
807     ],
808     footerText: "Raid detected"
809     });
810     }
811    
812     async logServerLockOrUnlock({
813     guild,
814     action,
815     moderator,
816     countInvalidChannel,
817     countSkipped,
818     countFailed,
819     countSuccess,
820     reason
821     }: {
822     guild: Guild;
823     action: "Locked" | "Unlocked";
824     moderator: User;
825     countInvalidChannel: number;
826     countSkipped: number;
827     countFailed: number;
828     countSuccess: number;
829     reason?: string;
830     }) {
831     await this.sendLogEmbed(guild, {
832     title: `Server ${action.toLowerCase()}`,
833     reason: reason ?? "The user ran a command to perform this action",
834     moderator,
835     color: 0x007bff,
836     footerText: action,
837     options: {
838     description: `Results:\n\n${countInvalidChannel === 0 ? "" : `InvalidChannel: ${countInvalidChannel}\n`}${
839     countSkipped === 0 ? "" : `Skipped: ${countSkipped}\n`
840     }${countSuccess === 0 ? "" : `Success: ${countSuccess}\n`}${countFailed === 0 ? "" : `Failed: ${countFailed}\n`}`
841     }
842     });
843     }
844    
845     async logChannelLockOrUnlock({
846     guild,
847     action,
848     moderator,
849     channel,
850     reason
851     }: {
852     guild: Guild;
853     action: "Locked" | "Unlocked";
854     moderator: User;
855     channel: GuildChannel;
856     reason?: string;
857     }) {
858     await this.sendLogEmbed(guild, {
859     title: `Channel ${action.toLowerCase()}`,
860     reason: reason ?? "The user ran a command to perform this action",
861     moderator,
862     color: 0x007bff,
863     footerText: action,
864     fields: [
865     {
866     name: "Channel",
867     value: `${channel.toString()} (${channel.id})`
868     }
869     ]
870     });
871     }
872    
873     async logUserBan({
874     moderator,
875     user,
876     deleteMessageSeconds,
877     reason,
878     guild,
879     id,
880     duration,
881     includeDeleteMessageSeconds = true
882     }: LogUserBanOptions) {
883     await this.sendLogEmbed(guild, {
884     user,
885     title: "A user was banned",
886     footerText: (duration ? "Temporarily " : "") + "Banned",
887     reason: reason ?? null,
888     moderator,
889     id,
890     color: Colors.Red,
891     fields: [
892     ...(includeDeleteMessageSeconds
893     ? [
894     {
895     name: "Message Deletion Timeframe",
896     value: deleteMessageSeconds
897     ? formatDistanceToNowStrict(new Date(Date.now() - deleteMessageSeconds * 1000))
898     : "*No timeframe provided*"
899     }
900     ]
901     : []),
902     ...(duration
903     ? [
904     {
905     name: "Duration",
906     value: formatDistanceToNowStrict(new Date(Date.now() - duration))
907     }
908     ]
909     : [])
910     ]
911     });
912     }
913    
914     async logUserSoftBan({ moderator, user, deleteMessageSeconds, reason, guild, id }: LogUserBanOptions) {
915     await this.sendLogEmbed(guild, {
916     user,
917     title: "A user was softbanned",
918     footerText: "Softbanned",
919     reason: reason ?? null,
920     moderator,
921     id,
922     color: Colors.Red,
923     fields: [
924     {
925     name: "Message Deletion Timeframe",
926     value: deleteMessageSeconds
927     ? formatDistanceToNowStrict(new Date(Date.now() - deleteMessageSeconds * 1000))
928     : "*No timeframe provided*"
929     }
930     ]
931     });
932     }
933    
934     async logUserUnban({ moderator, user, reason, guild, id }: LogUserUnbanOptions) {
935     this.sendLogEmbed(guild, {
936     user,
937     title: "A user was unbanned",
938     footerText: "Unbanned",
939     reason: reason ?? null,
940     moderator,
941     id,
942     color: Colors.Green
943     });
944     }
945    
946     async logMemberKick({
947     moderator,
948     member,
949     reason,
950     guild,
951     id
952     }: CommonUserActionOptions & { member: GuildMember; reason?: string }) {
953     this.sendLogEmbed(guild, {
954     user: member.user,
955     title: "A member was kicked",
956     footerText: "Kicked",
957     reason: reason ?? null,
958     moderator,
959     id,
960     color: Colors.Orange
961     });
962     }
963    
964     async logMemberMute({
965     moderator,
966     member,
967     reason,
968     guild,
969     id,
970     duration
971     }: CommonUserActionOptions & { member: GuildMember; reason?: string; duration?: number }) {
972     this.sendLogEmbed(guild, {
973     user: member.user,
974     title: "A member was muted",
975     footerText: "Muted",
976     reason: reason ?? null,
977     moderator,
978     id,
979     color: Colors.DarkGold,
980     fields: [
981     {
982     name: "Duration",
983     value: duration ? formatDistanceToNowStrict(new Date(Date.now() - duration)) : "*No duration was specified*"
984     }
985     ]
986     });
987     }
988    
989     async logMemberWarning({
990     moderator,
991     member,
992     reason,
993     guild,
994     id
995     }: CommonUserActionOptions & { member: GuildMember; reason?: string }) {
996     this.sendLogEmbed(guild, {
997     user: member.user,
998     title: "A member was warned",
999     footerText: "Warned",
1000     reason: reason ?? null,
1001     moderator,
1002     id,
1003     color: Colors.Gold
1004     });
1005     }
1006    
1007     async logBulkDeleteMessages({ messages, moderator, user, reason, guild, id, count, channel }: LogMessageBulkDelete) {
1008     const sendJSON = this.client.configManager.config[guild.id]?.logging?.bulk_delete_send_json;
1009    
1010     const message = await this.sendLogEmbed(
1011     guild,
1012     {
1013     user,
1014     title: "Messages deleted in bulk",
1015     footerText: "Deleted",
1016     reason: reason ?? null,
1017     moderator,
1018     id,
1019     color: Colors.DarkRed,
1020     fields: [
1021     {
1022     name: "Deleted Message Count",
1023     value: `${count}`
1024     },
1025     {
1026     name: "Channel",
1027     value: `${channel.toString()} (${channel.id})`
1028     }
1029     ]
1030     },
1031     sendJSON
1032     ? {
1033     files: [
1034     {
1035     attachment: this.generateBulkDeleteJSON(messages),
1036     name: "messages.json"
1037     }
1038     ]
1039     }
1040     : undefined
1041     );
1042    
1043     message
1044     ?.edit({
1045     embeds: [
1046     {
1047     ...(message?.embeds[0]?.data ?? {}),
1048     fields: [
1049     ...(message?.embeds[0].data.fields ?? []),
1050     {
1051     name: "Messages",
1052     value: `[Click here to view the deleted messages](${
1053     process.env.FRONTEND_URL
1054     }/view_deleted_messages?url=${encodeURIComponent(message.attachments.at(0)!.url)})`
1055     }
1056     ]
1057     }
1058     ]
1059     })
1060     .catch(logError);
1061     }
1062    
1063     async logMemberUnmute({
1064     moderator,
1065     member,
1066     reason,
1067     guild,
1068     id
1069     }: CommonUserActionOptions & { member: GuildMember; reason?: string }) {
1070     this.sendLogEmbed(guild, {
1071     user: member.user,
1072     title: "A member was unmuted",
1073     footerText: "Unmuted",
1074     reason: reason ?? null,
1075     moderator,
1076     id,
1077     color: Colors.Green
1078     });
1079     }
1080    
1081     async logBlockedWordOrToken({ guild, user, isToken, token, word, content }: BlockedTokenOrWordOptions) {
1082     this.sendLogEmbed(guild, {
1083     user,
1084     title: `Posted blocked ${isToken ? "token" : "word"}(s)`,
1085     footerText: "AutoMod",
1086     color: Colors.Yellow,
1087     fields: [
1088     {
1089     name: isToken ? "Token" : "Word",
1090     value: `||${escapeMarkdown((isToken ? token : word)!)}||`
1091     }
1092     ],
1093     options: {
1094     description: `${content}`
1095     }
1096     });
1097     }
1098    
1099     async logUserMassBan({ users, reason, guild, moderator, deleteMessageSeconds }: LogUserMassBanOptions) {
1100     await this.sendLogEmbed(guild, {
1101     title: "A massban was executed",
1102     footerText: "Banned",
1103     reason: reason ?? null,
1104     moderator,
1105     color: Colors.Red,
1106     fields: [
1107     {
1108     name: "Message Deletion Timeframe",
1109     value: deleteMessageSeconds
1110     ? formatDistanceToNowStrict(new Date(Date.now() - deleteMessageSeconds * 1000))
1111     : "*No timeframe provided*"
1112     }
1113     ],
1114     options: {
1115     description: `The following users were banned:\n\n${users.reduce(
1116     (acc, user) => acc + (acc === "" ? "" : "\n") + "<@" + user + "> (`" + user + "`)",
1117     ""
1118     )}`
1119     }
1120     });
1121     }
1122    
1123     async logMemberMassKick({ users, reason, guild, moderator }: Omit<LogUserMassBanOptions, "deleteMessageSeconds">) {
1124     await this.sendLogEmbed(guild, {
1125     title: "A masskick was executed",
1126     footerText: "Kicked",
1127     reason: reason ?? null,
1128     moderator,
1129     color: Colors.Orange,
1130     options: {
1131     description: `The following users were kicked:\n\n${users.reduce(
1132     (acc, user) => acc + (acc === "" ? "" : "\n") + "<@" + user + "> (`" + user + "`)",
1133     ""
1134     )}`
1135     }
1136     });
1137     }
1138    
1139     generateBulkDeleteJSON(messages: MessageResolvable[]) {
1140     const mappedMessages = ((messages instanceof Collection ? [...messages.values()] : messages) as Message[]).map(m => ({
1141     ...m,
1142     author: m.author,
1143     member: m.member,
1144     authorColor: m.member?.roles.highest.color,
1145     authorRoleIcon: m.member?.roles.highest.iconURL() ?? undefined,
1146     authorRoleName: m.member?.roles.highest.name ?? undefined,
1147     authorAvatarURL: m.author.displayAvatarURL(),
1148     mentions: {
1149     everyone: m.mentions.everyone,
1150     users: m.mentions.users.map(({ id, username }) => ({
1151     id,
1152     username
1153     })),
1154     members:
1155     m.mentions.members?.map(({ nickname, user }) => ({
1156     nickname: nickname ?? user.username,
1157     id: user.id
1158     })) ?? [],
1159     channels: (m.mentions.channels as Collection<string, GuildChannel>).map(({ id, name }) => ({ id, name })),
1160     roles: m.mentions.roles.map(({ id, name }) => ({ id, name }))
1161     }
1162     }));
1163    
1164     return Buffer.from(
1165     JSON.stringify(
1166     {
1167     messages: mappedMessages,
1168     generatedAt: new Date().toISOString(),
1169     channel: (messages.at(0) as Message)!.channel.toJSON({
1170     id: true,
1171     name: true,
1172     type: true
1173     }),
1174     guild: {
1175     id: (messages.at(0) as Message)!.guild!.id,
1176     name: (messages.at(0) as Message)!.guild!.name,
1177     iconURL: (messages.at(0) as Message)!.guild!.iconURL() ?? undefined
1178     },
1179     version: this.client.metadata.data.version
1180     },
1181     null,
1182     4
1183     )
1184     );
1185     }
1186     }
1187    
1188     interface LogMessageBulkDelete extends Omit<CommonUserActionOptions, "id"> {
1189     user?: User;
1190     reason?: string;
1191     count: number;
1192     channel: TextChannel;
1193     id?: string;
1194     messages: MessageResolvable[];
1195     }
1196    
1197     interface LogUserBanOptions extends BanOptions, CommonUserActionOptions {
1198     user: User;
1199     duration?: number;
1200     includeDeleteMessageSeconds?: boolean;
1201     }
1202    
1203     interface LogUserMassBanOptions extends BanOptions, Omit<CommonUserActionOptions, "id"> {
1204     users: string[];
1205     }
1206    
1207     interface LogUserUnbanOptions extends CommonUserActionOptions {
1208     user: User;
1209     reason?: string;
1210     }
1211    
1212     interface CommonUserActionOptions {
1213     moderator: User;
1214     guild: Guild;
1215     id: string;
1216     }
1217    
1218     interface BlockedTokenOrWordOptions {
1219     isToken: boolean;
1220     token?: string;
1221     word?: string;
1222     guild: Guild;
1223     user: User;
1224     content: string;
1225     }
1226    
1227     export interface CreateLogEmbedOptions {
1228     id?: string;
1229     title?: string;
1230     options?: EmbedData;
1231     moderator?: User;
1232     user?: User;
1233     fields?: APIEmbedField[];
1234     footerText?: string;
1235     reason?: string | null;
1236     timestamp?: Date | false | null;
1237     color?: ColorResolvable;
1238     showUserId?: boolean;
1239     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26