/[sudobot]/branches/8.x/src/commands/moderation/ModStatsCommand.ts
ViewVC logotype

Contents of /branches/8.x/src/commands/moderation/ModStatsCommand.ts

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (show annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: application/typescript
File size: 12561 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 /**
2 * This file is part of SudoBot.
3 *
4 * Copyright (C) 2021-2024 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 { Infraction, InfractionType } from "@prisma/client";
21 import {
22 ActionRowBuilder,
23 EmbedBuilder,
24 PermissionFlagsBits,
25 SlashCommandBuilder,
26 StringSelectMenuBuilder,
27 User,
28 time
29 } from "discord.js";
30 import Command, { ArgumentType, BasicCommandContext, CommandMessage, CommandReturn, ValidationRule } from "../../core/Command";
31 import Pagination from "../../utils/Pagination";
32
33 export default class ModStatsCommand extends Command {
34 public readonly name = "modstats";
35 public readonly validationRules: ValidationRule[] = [
36 {
37 types: [ArgumentType.User],
38 entity: {
39 notNull: true
40 },
41 optional: true,
42 errors: {
43 "entity:null": "No such user found. Please mention a valid user.",
44 "type:invalid": "Please mention a valid user."
45 },
46 name: "user"
47 }
48 ];
49 public readonly permissions = [
50 PermissionFlagsBits.BanMembers,
51 PermissionFlagsBits.ModerateMembers,
52 PermissionFlagsBits.ManageMessages
53 ];
54 public readonly permissionMode = "or";
55 public readonly aliases = ["modstat", "moderatorstats", "moderatorstat"];
56 public readonly slashCommandBuilder = new SlashCommandBuilder().addUserOption(option =>
57 option.setName("user").setDescription("The moderator user to view the stats of. Defaults to the command executor.")
58 );
59 public readonly description = "View the statistics of a moderator.";
60 public readonly since = "8.1.0";
61 public readonly argumentSyntaxes = ["[user]"];
62
63 async execute(message: CommandMessage, context: BasicCommandContext): Promise<CommandReturn> {
64 await this.deferIfInteraction(message);
65 const user =
66 (context.isLegacy ? context.parsedNamedArgs.user : context.options.getUser("user")) ?? (message.member!.user as User);
67 const infractionCount = await this.client.prisma.infraction.count({
68 where: {
69 moderatorId: user.id,
70 guildId: message.guildId!
71 }
72 });
73
74 if (infractionCount === 0) {
75 await this.error(message, "This moderator has not given any infractions in this server.");
76 return;
77 }
78
79 const pagination = new Pagination(null, {
80 channelId: message.channelId!,
81 guildId: message.guildId!,
82 limit: 4,
83 userId: message.member!.user.id,
84 client: this.client,
85 timeout: 180_000,
86 removeComponentsOnDisable: true,
87 metadata: {
88 sort: "desc",
89 filter: "all"
90 },
91 extraActionRows() {
92 const sortMode: string = (pagination as Pagination<Infraction>).getMetadata("sort");
93 const filterMode: string = (pagination as Pagination<Infraction>).getMetadata("filter");
94
95 return [
96 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
97 new StringSelectMenuBuilder()
98 .setCustomId("infraction_sort")
99 .setMinValues(1)
100 .setMaxValues(1)
101 .addOptions(
102 {
103 label: "Sort by date (Ascending)",
104 value: "asc",
105 description: "Sort the infractions by their date",
106 emoji: "📅",
107 default: sortMode === "asc"
108 },
109 {
110 label: "Sort by date (Descending)",
111 value: "desc",
112 description: "Sort the infractions by their date, in descending order",
113 emoji: "📅",
114 default: sortMode === "desc"
115 }
116 )
117 ),
118 new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
119 new StringSelectMenuBuilder()
120 .setCustomId("infraction_filter")
121 .setMinValues(1)
122 .setMaxValues(1)
123 .addOptions(
124 {
125 label: "Show all",
126 value: "all",
127 description: "Show all infractions",
128 emoji: "🔢",
129 default: filterMode === "all"
130 },
131 {
132 label: "Show from the last 24 hours",
133 value: "day",
134 description: "Show all infractions from the last 24 hours",
135 emoji: "🔢",
136 default: filterMode === "day"
137 },
138 {
139 label: "Show from the last week",
140 value: "week",
141 description: "Show all infractions from the last 7 days",
142 emoji: "🔢",
143 default: filterMode === "week"
144 },
145 {
146 label: "Show from the last month",
147 value: "month",
148 description: "Show all infractions from the last 30 days",
149 emoji: "🔢",
150 default: filterMode === "month"
151 },
152 {
153 label: "Show from the last year",
154 value: "year",
155 description: "Show all infractions from the last year",
156 emoji: "🔢",
157 default: filterMode === "year"
158 }
159 )
160 )
161 ];
162 },
163 async onInteraction(interaction) {
164 if (!interaction.isStringSelectMenu()) {
165 return;
166 }
167
168 const { customId, values } = interaction;
169
170 if (customId === "infraction_sort") {
171 await pagination.update(interaction, { sort: values[0] as "asc" | "desc" });
172 } else if (customId === "infraction_filter") {
173 pagination.update(interaction, { filter: values[0] as "all" | "day" | "week" | "month" | "year" });
174 }
175 },
176 fetchData(options): Promise<Infraction[]> {
177 const { limit, offset } = options;
178 const filter: string = (pagination as Pagination<Infraction>).getMetadata("filter");
179
180 return this.client.prisma.infraction.findMany({
181 where: {
182 moderatorId: user.id,
183 guildId: message.guildId!,
184 createdAt: {
185 gte:
186 filter === "all"
187 ? new Date(0)
188 : new Date(
189 Date.now() -
190 (filter === "day"
191 ? 86400000
192 : filter === "week"
193 ? 604800000
194 : filter === "month"
195 ? 2592000000
196 : 31536000000)
197 )
198 }
199 },
200 skip: offset,
201 take: limit,
202 orderBy: {
203 createdAt: pagination.getMetadata<"asc" | "desc">("sort")
204 }
205 });
206 },
207 maxData: (): Promise<number> => {
208 const filter: string = (pagination as Pagination<Infraction>).getMetadata("filter");
209
210 return this.client.prisma.infraction.count({
211 where: {
212 moderatorId: user.id,
213 guildId: message.guildId!,
214 createdAt: {
215 gte:
216 filter === "all"
217 ? new Date(0)
218 : new Date(
219 Date.now() -
220 (filter === "day"
221 ? 86400000
222 : filter === "week"
223 ? 604800000
224 : filter === "month"
225 ? 2592000000
226 : 31536000000)
227 )
228 }
229 },
230 orderBy: {
231 createdAt: pagination.getMetadata("sort")
232 }
233 });
234 },
235 embedBuilder({ data, currentPage, maxPages }): EmbedBuilder {
236 let description = "";
237
238 for (const infraction of data) {
239 description += `### Infraction #${infraction.id}\n`;
240 description += `**Type**: ${
241 infraction.type === InfractionType.BULK_DELETE_MESSAGE
242 ? "Bulk message delete"
243 : infraction.type[0] + infraction.type.substring(1).toLowerCase().replace(/_/g, " ")
244 }\n`;
245 description += `**Moderator**: <@${infraction.moderatorId}> (${infraction.moderatorId})\n`;
246 description += `**Reason**:\n${
247 infraction.reason ? `\`\`\`\n${infraction.reason}\n\`\`\`` : "*No reason provided*"
248 }\n`;
249 description += `**Created At**: ${time(infraction.createdAt, "D")} (${time(infraction.createdAt, "R")})\n`;
250 description += `**Updated At**: ${time(infraction.updatedAt, "D")} (${time(infraction.updatedAt, "R")})\n`;
251 description += "\n";
252 }
253
254 return new EmbedBuilder({
255 author: {
256 name: `Infractions given by ${user.username}`,
257 icon_url: user.displayAvatarURL()
258 },
259 description,
260 footer: {
261 text: `Page ${currentPage} of ${maxPages} • ${(
262 pagination as Pagination<Infraction>
263 ).getEntryCount()} infractions total`
264 },
265 color: 0x007bff
266 }).setTimestamp();
267 }
268 });
269
270 const options = await pagination.getMessageOptions();
271 const reply = await this.deferredReply(message, options);
272 await pagination.start(reply);
273 }
274 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26