/[sudobot]/branches/7.x/src/commands/moderation/SnipeCommand.ts
ViewVC logotype

Annotation of /branches/7.x/src/commands/moderation/SnipeCommand.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: 7539 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 { EmbedBuilder, Message, PartialMessage, PermissionsBitField, Snowflake } from "discord.js";
22     import Command, { AnyCommandContext, ArgumentType, CommandMessage, CommandReturn, ValidationRule } from "../../core/Command";
23     import { GatewayEventListener } from "../../decorators/GatewayEventListener";
24     import { HasEventListeners } from "../../types/HasEventListeners";
25     import { isTextableChannel } from "../../utils/utils";
26    
27     interface MessageInfo<T extends boolean = false> extends Message<T> {
28     deletedAt?: Date;
29     }
30    
31     export default class SnipeCommand extends Command implements HasEventListeners {
32     public readonly name = "snipe";
33     public readonly validationRules: ValidationRule[] = [
34     {
35     types: [ArgumentType.Integer],
36     optional: true,
37     errors: {
38     "type:invalid": "You must provide a valid message index/number between 1-10!",
39     "number:range": "You must provide a valid message index/number between 1-10!"
40     },
41     number: {
42     max: 11,
43     min: 1
44     },
45     default: null,
46     name: "index"
47     }
48     ];
49     public readonly permissions = [PermissionsBitField.Flags.ManageMessages];
50     public readonly argumentSyntaxes = ["[index=1]"];
51     public readonly aliases = ["clearsnipe", "cs", "delsnipe", "csnipe", "s", "ces", "ceditsnipe", "es", "editsnipe", "esnipe"];
52     public readonly since: string = "4.4.0";
53     protected readonly lastDeletedMessages = new Map<Snowflake, Array<MessageInfo<boolean>>>();
54     protected readonly lastEditedMessages = new Map<Snowflake, Array<[MessageInfo<boolean>, MessageInfo<boolean>]>>();
55    
56     public readonly description = "Reposts the last deleted/edited message.";
57    
58     @GatewayEventListener("messageDelete")
59     onMessageDelete(message: Message<boolean> | PartialMessage) {
60     if (message.author?.bot || !message.content || !isTextableChannel(message.channel)) {
61     return;
62     }
63    
64     const deletedMessages = this.lastDeletedMessages.get(message.guildId!);
65    
66     if (deletedMessages === undefined) {
67     this.lastDeletedMessages.set(message.guildId!, [{ ...message, deletedAt: new Date() } as MessageInfo]);
68     } else {
69     if (deletedMessages.length > 10) deletedMessages.pop();
70     deletedMessages.unshift({ ...message, deletedAt: new Date() } as MessageInfo);
71     }
72     }
73    
74     @GatewayEventListener("messageUpdate")
75     onMessageUpdate(oldMessage: Message<boolean> | PartialMessage, newMessage: Message<boolean> | PartialMessage) {
76     if (
77     newMessage.author?.bot ||
78     !newMessage.content ||
79     !isTextableChannel(newMessage.channel) ||
80     oldMessage.content === newMessage.content
81     ) {
82     return;
83     }
84    
85     const editedMessages = this.lastEditedMessages.get(newMessage.guildId!);
86    
87     if (editedMessages === undefined) {
88     this.lastEditedMessages.set(newMessage.guildId!, [[oldMessage as MessageInfo, newMessage as MessageInfo]]);
89     } else {
90     if (editedMessages.length > 10) editedMessages.pop();
91     editedMessages.unshift([oldMessage as MessageInfo, newMessage as MessageInfo]);
92     }
93     }
94    
95     async execute(message: CommandMessage, context: AnyCommandContext): Promise<CommandReturn> {
96     const index = context.isLegacy ? (context.parsedNamedArgs.index ?? 1) - 1 : 0;
97     const editSnipe = context.isLegacy && ["es", "editsnipe", "esnipe", "ces", "ceditsnipe"].includes(context.argv[0]);
98     const messages = (editSnipe ? this.lastEditedMessages : this.lastDeletedMessages).get(message.guildId!);
99     const lastMessage = editSnipe
100     ? (messages?.[index] as [MessageInfo, MessageInfo])?.[1]
101     : (messages?.[index] as MessageInfo);
102    
103     if (messages?.length && index >= messages?.length) {
104     await this.error(
105     message,
106     `Invalid message index - only ${messages.length} ${editSnipe ? "edited" : "deleted"} message${
107     messages.length === 1 ? " is" : "s are"
108     } have been recorded so far.`
109     );
110     return;
111     }
112    
113     if (!lastMessage) {
114     await this.error(message, `No ${editSnipe ? "edited" : "deleted"} message was recorded yet.`);
115     return;
116     }
117    
118     if (context.isLegacy && ["clearsnipe", "cs", "delsnipe", "csnipe", "ces", "ceditsnipe"].includes(context.argv[0])) {
119     const hasValue = context.args[0] !== undefined;
120    
121     if (!hasValue) {
122     messages?.splice(0, messages.length);
123     await this.success(message, "Cleared sniped messages for this server.");
124     } else {
125     messages?.splice(index, 1);
126     await this.success(message, "Removed the given sniped message for this server.");
127     }
128    
129     return;
130     }
131    
132     const date = editSnipe ? (messages?.[index] as [Message, Message])[1].editedAt! : lastMessage.deletedAt;
133    
134     return {
135     __reply: true,
136     embeds: [
137     new EmbedBuilder({
138     author: {
139     name: lastMessage.author?.username ?? "Unknown",
140     iconURL: lastMessage.author?.displayAvatarURL()
141     },
142     color: Math.floor(Math.random() * 0xffffff),
143     footer: {
144     text: `Sniped${
145     date
146     ? ` • ${editSnipe ? "Edited" : "Deleted"} ${formatDistanceToNowStrict(date, { addSuffix: true })}`
147     : ""
148     } • ${messages?.length ?? 0} ${editSnipe ? "edited" : "deleted"} message${
149     messages?.length === 1 ? "" : "s"
150     } total`
151     },
152     ...(editSnipe
153     ? {
154     fields: [
155     {
156     name: "Before",
157     value: (messages?.[index] as [Message, Message])[0].content || "*No content*"
158     },
159     {
160     name: "After",
161     value: (messages?.[index] as [Message, Message])[1].content || "*No content*"
162     }
163     ]
164     }
165     : {
166     description: lastMessage.content!
167     })
168     })
169     ]
170     };
171     }
172     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26