CommandBot 3.1.1 (merging)

CommandBot 3.1.1 - improved code safety, warnings in README
This commit was merged in pull request #3.
This commit is contained in:
GRZANA authored and GitHub committed 2021-08-31 20:26:04 +02:00
commit fccc73f854
9 files changed
+68 -43

No files matched your search

+4
View File
@@ -117,6 +117,8 @@ bot.start(
); );
``` ```
> **WARNING!** All commands have to be added to the instance **before starting the bot**. Adding commands while the bot is running is not possible and can cause issues.
# Commands # Commands
## Creating and registering a command ## Creating and registering a command
@@ -340,6 +342,8 @@ You can customize the help message with these properties (\* - required):
- **usage\*** - _string_ - usage displayed in the message itself - **usage\*** - _string_ - usage displayed in the message itself
- **visible\*** - _boolean_ - whether to show the help command in the list - **visible\*** - _boolean_ - whether to show the help command in the list
> **WARNING!** You can't customize these messages after starting the bot. Changing these properties while the bot is running will have no effect.
# Issues # Issues
Since this package is created by only 1 person it may contain some bugs or behave weirdly. If you notice any problem, typo etc, please create an issue in the _Issues_ tab on GitHub. Since this package is created by only 1 person it may contain some bugs or behave weirdly. If you notice any problem, typo etc, please create an issue in the _Issues_ tab on GitHub.
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "commandbot", "name": "commandbot",
"version": "3.1.0", "version": "3.1.1",
"description": "A framework that helps you create your own Discord bot easier.", "description": "A framework that helps you create your own Discord bot easier.",
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"module": "dist/mjs/index.js", "module": "dist/mjs/index.js",
+13 -8
View File
@@ -26,32 +26,32 @@ export class Bot extends EventEmitter {
* Bot name * Bot name
* @type {string} * @type {string}
*/ */
name: string; public readonly name: string;
/** /**
* Discord.js {@link Client} instance * Discord.js {@link Client} instance
* @type {Client} * @type {Client}
*/ */
client: Client; public readonly client: Client;
/** /**
* Instance command manager * Instance command manager
* @type {CommandManager} * @type {CommandManager}
*/ */
commands: CommandManager; public readonly commands: CommandManager;
/** /**
* Discord bot token * Discord bot token
* @type {string} * @type {string}
*/ */
token: string; public readonly token: string;
/** /**
* Discord API application ID * Discord API application ID
* @type {string} * @type {string}
*/ */
applicationId: string; public readonly applicationId: string;
/** /**
* Built-in messages configuration * Built-in messages configuration
* @type {Object} * @type {Object}
*/ */
messages: { public readonly messages: {
/** /**
* Help message configuration * Help message configuration
* @type {HelpMessageParams} * @type {HelpMessageParams}
@@ -132,6 +132,7 @@ export class Bot extends EventEmitter {
const helpMsg: Command = new HelpMessage(this.commands, this.messages.help, this.name); const helpMsg: Command = new HelpMessage(this.commands, this.messages.help, this.name);
this.commands.add(helpMsg); this.commands.add(helpMsg);
} }
this.commands.freezeList();
process.stdout.write("Connecting to Discord... "); process.stdout.write("Connecting to Discord... ");
this.client.login(this.token); this.client.login(this.token);
this.client.on("ready", async () => { this.client.on("ready", async () => {
@@ -247,11 +248,15 @@ export class Bot extends EventEmitter {
process.stdout.write("Registering commands... "); process.stdout.write("Registering commands... ");
await axios.put( await axios.put(
`https://discord.com/api/v8/applications/${this.applicationId}/commands`, `https://discord.com/api/v8/applications/${this.applicationId}/commands`,
this.commands.list.filter((c) => !Array.isArray(c.guilds) && c.slash).map((c) => c.toCommandObject()), this.commands
.getList()
.filter((c) => !Array.isArray(c.guilds) && c.slash)
.map((c) => c.toCommandObject()),
{ headers: { Authorization: `Bot ${this.token}` } } { headers: { Authorization: `Bot ${this.token}` } }
); );
const guilds: any = {}; const guilds: any = {};
await this.commands.list await this.commands
.getList()
.filter((c) => Array.isArray(c.guilds) && c.guilds.length > 0) .filter((c) => Array.isArray(c.guilds) && c.guilds.length > 0)
.map((c) => { .map((c) => {
c.guilds?.map(async (g) => { c.guilds?.map(async (g) => {
+12 -12
View File
@@ -14,58 +14,58 @@ export class Command {
* Command name * Command name
* @type {string} * @type {string}
*/ */
name: string; public readonly name: string;
/** /**
* List of parameters that can passed to this command * List of parameters that can passed to this command
* @type {Array} {@link Parameter} * @type {Array} {@link Parameter}
*/ */
parameters: Parameter[]; public readonly parameters: Parameter[];
/** /**
* List of different names that can be used to invoke a command (when using prefix interactions) * List of different names that can be used to invoke a command (when using prefix interactions)
* @type {Array} *string* * @type {Array} *string*
*/ */
aliases?: string[]; public readonly aliases?: string[];
/** /**
* Command description displayed in the help message (Default description: "No description") * Command description displayed in the help message (Default description: "No description")
* @type {string} * @type {string}
*/ */
description: string; public readonly description: string;
/** /**
* Command usage displayed in the help message * Command usage displayed in the help message
* @type {string} * @type {string}
*/ */
usage?: string; public readonly usage?: string;
/** /**
* Whether to check if a caller has all defined in *permissions* property permissions or at least one of them (doesn't apply to custom function permissions) * Whether to check if a caller has all defined in *permissions* property permissions or at least one of them (doesn't apply to custom function permissions)
* @type {PermissionCheckTypes} "ALL" | "ANY" * @type {PermissionCheckTypes} "ALL" | "ANY"
*/ */
permissionCheck: PermissionCheckTypes; public readonly permissionCheck: PermissionCheckTypes;
/** /**
* Command permissions (if *undefined*, no permissions check will be performed) * Command permissions (if *undefined*, no permissions check will be performed)
* @type {PermissionResolvable} * @type {PermissionResolvable}
* @type {Function} should return boolean value * @type {Function} should return boolean value
*/ */
permissions?: Permissions | ((m?: Message | CommandInteraction) => boolean); public readonly permissions?: Permissions | ((m?: Message | CommandInteraction) => boolean);
/** /**
* List of Discord guild (server) IDs in which this command can be used * List of Discord guild (server) IDs in which this command can be used
* @type {Array} *string* * @type {Array} *string*
*/ */
guilds?: string[]; public readonly guilds?: string[];
/** /**
* Whether this command is visible in the help message (default: true) * Whether this command is visible in the help message (default: true)
* @type {boolean} * @type {boolean}
*/ */
visible: boolean; public readonly visible: boolean;
/** /**
* Whether this command should be registered as a slash command (default: true) * Whether this command should be registered as a slash command (default: true)
* @type {boolean} * @type {boolean}
*/ */
slash: boolean; public readonly slash: boolean;
/** /**
* Whether to send a SUCCESS message if no other response is defined (default: true) * Whether to send a SUCCESS message if no other response is defined (default: true)
* @type {boolean} * @type {boolean}
*/ */
announceSuccess: boolean; public readonly announceSuccess: boolean;
/** /**
* Command execution function (triggered when someone invokes the command) * Command execution function (triggered when someone invokes the command)
* @type {Function} * @type {Function}
@@ -73,7 +73,7 @@ export class Command {
* @param {Message | CommandInteraction} i - interaction object * @param {Message | CommandInteraction} i - interaction object
* @private * @private
*/ */
private function: ( private readonly function: (
params: (query: string, returnType?: "value" | "object") => ParameterResolvable | InputParameter | null, params: (query: string, returnType?: "value" | "object") => ParameterResolvable | InputParameter | null,
interaction?: Message | CommandInteraction interaction?: Message | CommandInteraction
) => void | string | MessageEmbed | ReplyMessageOptions | Promise<void | string | MessageEmbed | ReplyMessageOptions>; ) => void | string | MessageEmbed | ReplyMessageOptions | Promise<void | string | MessageEmbed | ReplyMessageOptions>;
+19 -3
View File
@@ -12,17 +12,17 @@ export class CommandManager {
* List of commands registered in the manager * List of commands registered in the manager
* @type {Array} {@link Command} * @type {Array} {@link Command}
*/ */
list: Command[]; private readonly list: Command[];
/** /**
* Prefix used as a way to trigger the bot using messages * Prefix used as a way to trigger the bot using messages
* @type {string} * @type {string}
*/ */
prefix?: string; public readonly prefix?: string;
/** /**
* Separator used to split user input to a list of {@link InputParameter}s (applies to prefix interactions) * Separator used to split user input to a list of {@link InputParameter}s (applies to prefix interactions)
* @type {string} * @type {string}
*/ */
parameterSeparator: string; public readonly parameterSeparator: string;
/** /**
* @constructor * @constructor
@@ -56,6 +56,15 @@ export class CommandManager {
return command; return command;
} }
/**
* Get list (array) of all {@link Command}s registered in a manager
* @returns {Command[]} Array of {@link Command}s registered in a manager
*/
getList(): Command[] {
const list = [...this.list];
return list;
}
/** /**
* Adds the given {@link Command} to the instance manager with initial processing * Adds the given {@link Command} to the instance manager with initial processing
* @param {Command} command - {@link Command} instance object * @param {Command} command - {@link Command} instance object
@@ -203,6 +212,13 @@ export class CommandManager {
return null; return null;
} }
freezeList(): void {
if (!Object.isFrozen(this.list)) {
Object.freeze(this.list);
}
return;
}
private findPhraseOccurrence(phrase?: string): PhraseOccurrenceData | null { private findPhraseOccurrence(phrase?: string): PhraseOccurrenceData | null {
let returnValue: PhraseOccurrenceData | null = null; let returnValue: PhraseOccurrenceData | null = null;
this.list.map((c) => { this.list.map((c) => {
+1 -1
View File
@@ -61,7 +61,7 @@ export class HelpMessage extends Command {
helpMsg.setTitle(params.title); helpMsg.setTitle(params.title);
helpMsg.setDescription(params.bottomText); helpMsg.setDescription(params.bottomText);
cmdManager.list.map((c) => { cmdManager.getList().map((c) => {
if ( if (
c.visible && c.visible &&
((Array.isArray(c.guilds) && c.guilds.length > 0 && c.guilds.find((g) => i?.guild?.id === g)) || !Array.isArray(c.guilds) || c.guilds.length === 0) ((Array.isArray(c.guilds) && c.guilds.length > 0 && c.guilds.find((g) => i?.guild?.id === g)) || !Array.isArray(c.guilds) || c.guilds.length === 0)
+7 -7
View File
@@ -37,26 +37,26 @@ export class Parameter {
* Parameter name * Parameter name
* @type {string} * @type {string}
*/ */
name: string; public readonly name: string;
/** /**
* Parameter description * Parameter description
* @type {string} * @type {string}
*/ */
description: string; public readonly description: string;
/** /**
* Whether this parameter is optional * Whether this parameter is optional
* @type {boolean} * @type {boolean}
*/ */
optional: boolean; public readonly optional: boolean;
/** /**
* Parameter input type * Parameter input type
* @type {ParameterType} * @type {ParameterType}
*/ */
type: ParameterType; public readonly type: ParameterType;
/** /**
* List of value choices (available only when type is set to "STRING") * List of value choices (available only when type is set to "STRING")
*/ */
choices?: string[]; public readonly choices?: string[];
constructor(options: ParameterSchema) { constructor(options: ParameterSchema) {
this.name = options.name; this.name = options.name;
@@ -93,7 +93,7 @@ export class InputParameter extends Parameter {
* Input value * Input value
* @type {ParameterResolvable} * @type {ParameterResolvable}
*/ */
value: ParameterResolvable; public readonly value: ParameterResolvable;
constructor(parameter: Parameter, value: ParameterResolvable) { constructor(parameter: Parameter, value: ParameterResolvable) {
super(parameter); super(parameter);
@@ -148,7 +148,7 @@ export class ObjectParameter extends InputParameter {
} }
} }
export class ObjectID { export class ObjectID {
id: string; public readonly id: string;
constructor(id: string) { constructor(id: string) {
this.id = id.replace(">", "").replace("<@!", "").replace("<#!", ""); this.id = id.replace(">", "").replace("<@!", "").replace("<#!", "");
} }
+5 -5
View File
@@ -84,27 +84,27 @@ export class SystemMessageManager {
* "Insufficient permissions" message * "Insufficient permissions" message
* @type {SystemMessageAppearance} * @type {SystemMessageAppearance}
*/ */
PERMISSION: SystemMessageAppearance; public PERMISSION: SystemMessageAppearance;
/** /**
* Error message * Error message
* @type {SystemMessageAppearance} * @type {SystemMessageAppearance}
*/ */
ERROR: SystemMessageAppearance; public ERROR: SystemMessageAppearance;
/** /**
* "Command not found" message * "Command not found" message
* @type {SystemMessageAppearance} * @type {SystemMessageAppearance}
*/ */
NOT_FOUND: SystemMessageAppearance; public NOT_FOUND: SystemMessageAppearance;
/** /**
* "Task completed successfully" message * "Task completed successfully" message
* @type {SystemMessageAppearance} * @type {SystemMessageAppearance}
*/ */
SUCCESS: SystemMessageAppearance; public SUCCESS: SystemMessageAppearance;
/** /**
* Global time (in ms) after a message gets deleted * Global time (in ms) after a message gets deleted
* @type {number} * @type {number}
*/ */
deleteTimeout: number; public deleteTimeout: number;
constructor(botName?: string) { constructor(botName?: string) {
this.PERMISSION = { this.PERMISSION = {
+6 -6
View File
@@ -9,8 +9,8 @@ import { ParameterType } from "./types.js";
* @exports * @exports
*/ */
export class PermissionsError extends Error { export class PermissionsError extends Error {
private command: Command; private readonly command: Command;
private user: GuildMember | null; private readonly user: GuildMember | null;
constructor(command: Command, user?: GuildMember | null) { constructor(command: Command, user?: GuildMember | null) {
super(); super();
this.command = command; this.command = command;
@@ -27,8 +27,8 @@ export class PermissionsError extends Error {
* @exports * @exports
*/ */
export class ParameterTypeError extends TypeError { export class ParameterTypeError extends TypeError {
private stringContent: string; private readonly stringContent: string;
private type: ParameterType; private readonly type: ParameterType;
constructor(s: string, type: ParameterType) { constructor(s: string, type: ParameterType) {
super(); super();
this.stringContent = s; this.stringContent = s;
@@ -45,7 +45,7 @@ export class ParameterTypeError extends TypeError {
* @exports * @exports
*/ */
export class MissingParameterError extends ReferenceError { export class MissingParameterError extends ReferenceError {
private argument: Parameter; private readonly argument: Parameter;
constructor(a: Parameter) { constructor(a: Parameter) {
super(); super();
this.argument = a; this.argument = a;
@@ -60,7 +60,7 @@ export class MissingParameterError extends ReferenceError {
* @exports * @exports
*/ */
export class OperationSuccess { export class OperationSuccess {
command: Command; public readonly command: Command;
constructor(c: Command) { constructor(c: Command) {
this.command = c; this.command = c;
} }