/[sudobot]/branches/8.x/src/commands/settings/HelpCommand.ts
ViewVC logotype

Annotation of /branches/8.x/src/commands/settings/HelpCommand.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: 16727 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 {
21     ActionRowBuilder,
22     ButtonBuilder,
23     ButtonStyle,
24     Collection,
25     EmbedBuilder,
26     PermissionResolvable,
27     SlashCommandBuilder,
28     escapeCodeBlock,
29     escapeInlineCode
30     } from "discord.js";
31     import Command, {
32     ArgumentType,
33     BasicCommandContext,
34     CommandMessage,
35     CommandReturn,
36     ValidationRule
37     } from "../../core/Command";
38     import { GatewayEventListener } from "../../decorators/GatewayEventListener";
39     import { log } from "../../utils/Logger";
40     import Pagination from "../../utils/Pagination";
41     import { DOCS_URL, GITHUB_URL, WEBSITE_URL } from "../../utils/links";
42     import { forceGetPermissionNames, getComponentEmojiResolvable } from "../../utils/utils";
43    
44     export interface CommandInfo {
45     name: string;
46     aliases: string[];
47     group: string;
48     description?: string;
49     detailedDescription?: string;
50     systemAdminOnly: boolean;
51     beta: boolean;
52     argumentSyntaxes?: string[];
53     botRequiredPermissions?: PermissionResolvable[];
54     availableOptions?: Record<string, string>;
55     since: string;
56     supportsInteractions: boolean;
57     supportsLegacy: boolean;
58     }
59    
60     export default class HelpCommand extends Command {
61     public readonly name = "help";
62     public readonly validationRules: ValidationRule[] = [
63     {
64     types: [ArgumentType.String],
65     name: "command",
66     optional: true
67     },
68     {
69     types: [ArgumentType.String],
70     name: "subcommand",
71     optional: true
72     }
73     ];
74     public readonly permissions = [];
75    
76     public readonly description = "Shows this help information.";
77     public readonly detailedDescription =
78     "Shows documentation about the bot's commands. You can even get information about individual commands by running `help <command>` where `<command>` is the command name.";
79    
80     public readonly argumentSyntaxes = ["[command]"];
81    
82     public readonly commandInformation = new Collection<string, string[]>();
83     public readonly slashCommandBuilder = new SlashCommandBuilder()
84     .addStringOption(option =>
85     option.setName("command").setDescription("Shows help for this command")
86     )
87     .addStringOption(option =>
88     option.setName("subcommand").setDescription("Shows help for this subcommand")
89     );
90    
91     @GatewayEventListener("ready")
92     async onReady() {
93     log("Attempting to read and extract meta info from all the loaded commands...");
94    
95     for await (const command of this.client.commands.values()) {
96     if (command.name.includes("__")) continue;
97     const commands = this.commandInformation.get(command.group) ?? [];
98    
99     if (commands.includes(command.name)) {
100     continue;
101     }
102    
103     commands.push(command.name);
104     this.commandInformation.set(command.group, commands);
105     }
106    
107     for (const group of this.commandInformation.keys()) {
108     this.commandInformation.get(group)?.sort((a, b) => a.localeCompare(b, ["en-US"]));
109     }
110    
111     log("Successfully read metadata of " + this.commandInformation.size + " commands");
112     }
113    
114     async execute(message: CommandMessage, context: BasicCommandContext): Promise<CommandReturn> {
115     await this.deferIfInteraction(message);
116     const commandName = context.isLegacy
117     ? context.parsedNamedArgs.command
118     : context.options.getString("command");
119     const subcommand = context.isLegacy
120     ? context.parsedNamedArgs.subcommand
121     : context.options.getString("subcommand");
122    
123     const config = this.client.configManager.config[message.guildId!];
124    
125     if (!config) {
126     await this.error(
127     message,
128     "This server isn't configured. Please ask a system administrator to configure this server."
129     );
130     return;
131     }
132    
133     if (!commandName) {
134     const pagination = new Pagination([...this.commandInformation.entries()], {
135     channelId: message.channelId!,
136     client: this.client,
137     guildId: message.guildId!,
138     limit: 1,
139     timeout: 200_000,
140     userId: message.member!.user.id,
141     embedBuilder: ({ currentPage, maxPages, data: [[group, commandNames]] }) => {
142     let description: string = `Run \`${config.prefix}help <commandName>\` to get help about a specific command.\n\`<...>\` means required argument, \`[...]\` means optional argument.\n\n`;
143     description += `**${group}**\n\`${commandNames.join("`, `")}\`\n\n`;
144    
145     return new EmbedBuilder({
146     author: {
147     name: "Help",
148     iconURL: this.client.user?.displayAvatarURL() ?? undefined
149     },
150     color: 0x007bff,
151     description,
152     footer: {
153     text: `Page ${currentPage} of ${maxPages}`
154     }
155     }).setTimestamp();
156     },
157     messageOptions: {
158     components: [
159     new ActionRowBuilder<ButtonBuilder>().addComponents(
160     new ButtonBuilder()
161     .setStyle(ButtonStyle.Link)
162     .setEmoji("📘")
163     .setURL(DOCS_URL)
164     .setLabel("Documentation"),
165     new ButtonBuilder()
166     .setStyle(ButtonStyle.Link)
167     .setEmoji(
168     getComponentEmojiResolvable(this.client, "github") ?? "☄️"
169     )
170     .setURL(GITHUB_URL)
171     .setLabel("GitHub"),
172     new ButtonBuilder()
173     .setStyle(ButtonStyle.Link)
174     .setEmoji("🌍")
175     .setURL(WEBSITE_URL)
176     .setLabel("Website")
177     )
178     ]
179     }
180     });
181    
182     const reply = await this.deferredReply(message, await pagination.getMessageOptions(1));
183     await pagination.start(reply);
184     } else {
185     const name = subcommand ? `${commandName} ${subcommand}` : commandName;
186     const rootCommandName = this.client.commands.get(commandName)?.name ?? commandName;
187     const command =
188     this.client.commands.get(
189     subcommand ? `${rootCommandName}__${subcommand}` : commandName
190     ) ?? this.client.commands.get(commandName);
191     const hasSubcommand =
192     subcommand && this.client.commands.has(`${rootCommandName}__${subcommand}`);
193     const subcommandMeta = hasSubcommand ? command : command?.subcommandsMeta[subcommand];
194    
195     if (
196     (!subcommand && !command) ||
197     (subcommand && !hasSubcommand && !command?.subcommandsMeta[subcommand])
198     ) {
199     await this.error(
200     message,
201     subcommand
202     ? `No command \`${commandName}\` or no subcommand \`${subcommand}\` exists.`
203     : `No command named \`${escapeInlineCode(
204     escapeCodeBlock(commandName)
205     )}\` exists!`
206     );
207     return;
208     }
209    
210     const options =
211     command?.availableOptions || subcommandMeta?.availableOptions
212     ? Object.entries(
213     subcommandMeta?.availableOptions ?? command?.availableOptions ?? {}
214     )
215     : [];
216    
217     await this.deferredReply(message, {
218     embeds: [
219     new EmbedBuilder({
220     title: `${config.prefix}${name}${command?.beta ? " [BETA]" : ""}`,
221     color: 0x007bff,
222     fields: [
223     {
224     name: "Name",
225     value: `\`${(subcommand ? name : command?.name)?.replace(
226     "__",
227     " "
228     )}\``,
229     inline: true
230     },
231     {
232     name: "Group",
233     value: `\`${command?.group}\``,
234     inline: true
235     },
236     ...(command?.aliases.length
237     ? [
238     {
239     name: "Aliases",
240     value: command?.aliases
241     ?.filter(c => c !== command?.name && c !== name)
242     .map(c => `\`${c.replace("__", " ")}\``)
243     .join("\n")
244     }
245     ]
246     : []),
247     {
248     name: "Description",
249     value:
250     command?.detailedDescription ??
251     command?.description ??
252     "*No description available*"
253     },
254     {
255     name: "Syntax",
256     value: `\`\`\`\n${
257     command?.argumentSyntaxes || subcommandMeta?.argumentSyntaxes
258     ? (
259     subcommandMeta?.argumentSyntaxes ??
260     command?.argumentSyntaxes ??
261     []
262     )
263     .map(s => `${config.prefix}${name} ${s}`)
264     .join("\n")
265     : `${config.prefix}${name}`
266     }\n\`\`\``
267     },
268     ...(command?.subcommands.length &&
269     !hasSubcommand &&
270     !command?.subcommandsMeta[subcommand]
271     ? [
272     {
273     name: "Subcommands",
274     value: `Run \`${config.prefix}help ${
275     command?.name
276     } <subcommand>\` to see information about specific subcommands.\n\n* ${command.subcommands
277     .map(s => `\`${s}\``)
278     .join("\n* ")}`
279     }
280     ]
281     : []),
282     ...((
283     subcommandMeta?.botRequiredPermissions ??
284     command?.botRequiredPermissions
285     )?.length
286     ? [
287     {
288     name: "Required Bot Permissions",
289     value:
290     "`" +
291     forceGetPermissionNames(
292     subcommandMeta?.botRequiredPermissions ??
293     command?.botRequiredPermissions ??
294     []
295     ).join("`\n`") +
296     "`",
297     inline: true
298     }
299     ]
300     : []),
301     ...((subcommandMeta?.permissions ?? command?.permissions)?.length
302     ? [
303     {
304     name: "Required User Permissions",
305     value:
306     "`" +
307     forceGetPermissionNames(
308     subcommandMeta?.permissions ??
309     command?.permissions ??
310     []
311     ).join(
312     `\`\n${
313     command?.permissionMode === "or" ? "or, " : ""
314     }\``
315     ) +
316     "`",
317     inline: true
318     }
319     ]
320     : []),
321     ...(options.length > 0
322     ? [
323     {
324     name: "Options",
325     value: options
326     .map(
327     ([name, description]) =>
328     `* \`${name}\` - ${description}`
329     )
330     .join("\n")
331     }
332     ]
333     : []),
334     {
335     name: "Mode",
336     value: `${this.emoji(
337     subcommandMeta?.supportsLegacy ?? command?.supportsLegacy
338     ? "check"
339     : "error"
340     )} Legacy\n${this.emoji(
341     subcommandMeta?.supportsInteractions ??
342     command?.supportsInteractions
343     ? "check"
344     : "error"
345     )} Interaction-based`
346     },
347     {
348     name: "Other Information",
349     value: `Available since \`${
350     subcommandMeta?.since ?? command?.since
351     }\`.\n${
352     subcommandMeta?.beta ?? command?.beta
353     ? "This command is under beta testing.\n"
354     : ""
355     }${
356     subcommandMeta?.systemAdminOnly ?? command?.systemAdminOnly
357     ? "This command can only be used by the System Administrators of the bot.\n"
358     : ""
359     }`,
360     inline: true
361     }
362     ]
363     }).setTimestamp()
364     ]
365     });
366     }
367     }
368     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26