Archived
CommandBot 3.0.0 - slash commands (merge pull request)
3.0.0 - Slash commands support, merging pull request to the master branch
This commit was merged in pull request #1.
This commit is contained in:
17 files changed
+999
-674
No files matched your search
+1
-2
@@ -1,4 +1,3 @@
|
||||
node_modules
|
||||
test
|
||||
dist
|
||||
package-create.bat
|
||||
dist
|
||||
Vendored
+2
-1
@@ -5,7 +5,8 @@
|
||||
"command": "yarn test",
|
||||
"name": "Start test env",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
"type": "node-terminal",
|
||||
"preLaunchTask": "Compile test bot"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+6
@@ -10,6 +10,12 @@
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Compile test bot",
|
||||
"type": "shell",
|
||||
"command": "cd test; tsc",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,329 +1,328 @@
|
||||
# CommandBot
|
||||
|
||||
> [IMPORTANT]
|
||||
> Upgrading to this version from 1.1.3 may require additional changes in your code since 2.0.0 is not fully backwards compatible
|
||||
A Discord.js based framework that makes creating Discord bots with support for slash commands easy and fast.
|
||||
|
||||
Discord.js framework that helps you build your own Discord bot.
|
||||
# Table of contents
|
||||
|
||||
Key features
|
||||
|
||||
- command system
|
||||
- auto-generated help command
|
||||
- command permissions
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Getting started](#getting-started)
|
||||
- [Installation](#installation)
|
||||
- [Creating bot instance](#creating-bot-instance)
|
||||
- [Bot instance](#bot-instance)
|
||||
- [CommandBot](#commandbot)
|
||||
- [Table of contents](#table-of-contents)
|
||||
- [Instalation](#installation)
|
||||
- [System requirements](#system-requirements)
|
||||
- [Creating a project](#creating-a-project)
|
||||
- [Registering Discord app](#registering-discord-app)
|
||||
- [Creating application](#creating-application)
|
||||
- [Commands](#commands)
|
||||
- [All parameters](#all-parameters)
|
||||
- [Creating and registering a command](#creating-and-registering-a-command)
|
||||
- [Command function](#command-function)
|
||||
- [Arguments](#arguments)
|
||||
- [Return](#return)
|
||||
- [Return value](#return-value)
|
||||
- [Errors](#errors)
|
||||
- [Command examples](#command-examples)
|
||||
- [Simple reply](#simple-reply)
|
||||
- [Example with arguments](#example-with-arguments)
|
||||
- [Parameters](#parameters)
|
||||
- [Types](#types)
|
||||
- [Defining](#defining)
|
||||
- [Reading input value](#reading-input-value)
|
||||
- [Events](#events)
|
||||
- [Customization and built-in messages](#customization-and-built-in-messages)
|
||||
- [Handling events](#handling-events)
|
||||
- [Event types](#event-types)
|
||||
- [Messages](#messages)
|
||||
- [Information messages](#information-messages)
|
||||
- [Help message](#help-message)
|
||||
- [System messages](#system-messages)
|
||||
- [Complete example](#complete-example)
|
||||
- [Issues](#issues)
|
||||
|
||||
## Getting started
|
||||
# Installation
|
||||
|
||||
### Installation
|
||||
## System requirements
|
||||
|
||||
1. Install this package using npm
|
||||
- _Node.js_ 16.6.0 or newer
|
||||
- _npm_ or _yarn_ package manager
|
||||
|
||||
```
|
||||
npm install commandbot@latest
|
||||
```
|
||||
## Creating a project
|
||||
|
||||
or yarn
|
||||
### Registering Discord app
|
||||
|
||||
1. Visit [Discord Developer Portal](https://discord.com/developers/) and create an app
|
||||
2. Navigate to the _Bot_ section and register a bot
|
||||
3. Navigate to _OAuth 2_ section, select _bot_ and _application.commands_ scopes and check bot permissions
|
||||
4. Copy the link and add your bot to the servers
|
||||
|
||||
### Creating application
|
||||
|
||||
1. Create empty directory
|
||||
2. Run `npm init -y` or `yarn init -y`
|
||||
3. Add the CommandBot package
|
||||
|
||||
```javascript
|
||||
// npm
|
||||
npm install commandbot@latest
|
||||
|
||||
// yarn
|
||||
yarn add commandbot@latest
|
||||
```
|
||||
|
||||
2. Import the package
|
||||
4. Create _index.js_ file
|
||||
5. Import the CommandBot package
|
||||
|
||||
```javascript
|
||||
//CommonJS
|
||||
// CommonJS
|
||||
const { Bot, Command } = require("commandbot");
|
||||
|
||||
//ES Modules (to use this option add "type": "module" to your package.json file)
|
||||
// ES Modules (to use ESM add "type": "module" to your package.json file)
|
||||
import { Bot, Command } from "commandbot";
|
||||
```
|
||||
|
||||
### Creating bot instance
|
||||
6. Initialize the bot instance
|
||||
|
||||
```javascript
|
||||
const bot = new Bot({
|
||||
name: "Command bot", // Name of your bot
|
||||
prefix: "!", // Prefix used to call commands
|
||||
argumentSeparator: ",", // Used to get arguments from message (optional)
|
||||
clientOptions: undefined, // Instance of ClientOptions from Discord.js (optional)
|
||||
token: "", // Bot token from Discord Developer Portal (optional, you can pass the token in the *start* method)
|
||||
});
|
||||
```
|
||||
|
||||
Start your bot using
|
||||
|
||||
```javascript
|
||||
bot.start();
|
||||
```
|
||||
|
||||
Optional arguments for this method
|
||||
|
||||
- **port** - _number_ - if specified, the app will create a http server that will be listening on the specified port
|
||||
- **token** - _string_ - app token from Discord Developer Portal
|
||||
|
||||
## Bot instance
|
||||
|
||||
The main object of this library has the following properties
|
||||
|
||||
- **name** - _string_ - bot's name specified in the constructor
|
||||
- **client** - _[Client](https://discord.js.org/#/docs/main/stable/class/Client)_ - Discord.js client instance
|
||||
- **commands** - _CommandManager_ - base of the command system (see [Commands](#commands))
|
||||
- **config** - _object_ - object that stores the login token and _helpMessage_ value from the constructor
|
||||
- **messages** - _SystemMessageManager_ - object instance containing all configuration parameters for built-in messages (see [Customization and built-in messages](#customization-and-built-in-messages))
|
||||
|
||||
## Commands
|
||||
|
||||
> [IMPORTANT]
|
||||
> All commands have to be declared above the _start_ method
|
||||
|
||||
### All parameters
|
||||
|
||||
- **name** - _string_ - command name (used to trigger the command)
|
||||
- **function** - _Function_ - function that will trigger when the commands gets called
|
||||
- **visible** - _boolean_ - show command in the _help message_ (optional, defaults to _true_)
|
||||
- **description** - _string_ - command description shown in the _help message_ (optional)
|
||||
- **usage** - _string_ - command usage description shown in the _help message_ (optional)
|
||||
- **permissionCheck** - _"ALL" | "ANY"_ - specifies if the caller has to have all of the specified permissions or any of that (optional, default value: "ANY")
|
||||
- **permissions** - _[PermissionResolvable](https://discord.js.org/#/docs/main/stable/typedef/PermissionResolvable)_ - permissions needed to run the command (optional)
|
||||
- **aliases** - _string | string[]_ - other words that can trigger the command with prefix (optional)
|
||||
- **keywords** - _string | string[]_ - other words that can trigger the command without prefix (optional)
|
||||
|
||||
### Command function
|
||||
|
||||
#### Arguments
|
||||
|
||||
- **m** - _[Message](https://discord.js.org/#/docs/main/stable/class/Message)_ - a message object instance containing message content, author and additional informations
|
||||
- **a** - _string[]_ - list of arguments passed with the message (splitted with _argumentSeparator_)
|
||||
|
||||
#### Return
|
||||
|
||||
If command function returns
|
||||
|
||||
- _string_ - the returned text will be sent as a reply to the command caller
|
||||
- _[MessageEmbed](https://discord.js.org/#/docs/main/stable/class/MessageEmbed)_ - the embedded content will be sent as a standalone message
|
||||
|
||||
#### Errors
|
||||
|
||||
If your command throws an error, it will get logged to the console and sent to the user in a message with embedded content. **The bot will not crash.** ([Example](https://raw.githubusercontent.com/GRZ4NA/CommandBot/master/assets/error_example.png))
|
||||
|
||||
### Command examples
|
||||
|
||||
#### Simple reply
|
||||
|
||||
1. Create a command
|
||||
|
||||
```javascript
|
||||
//CREATE COMMAND
|
||||
const command = new Command({
|
||||
name: "ping",
|
||||
function: (m, a) => {
|
||||
return "pong";
|
||||
name: "YOUR_BOT_NAME",
|
||||
prefix: "BOT_PREFIX",
|
||||
parameterSeparator: ",",
|
||||
clientOptions: {
|
||||
intents: [..."DISCORD_API_INTENTS"],
|
||||
},
|
||||
token: "DISCORD_BOT_TOKEN",
|
||||
applicationId: "APPLICATION_ID",
|
||||
});
|
||||
//APPEND IT TO THE COMMANDS LIST
|
||||
bot.commands.add(command);
|
||||
```
|
||||
|
||||
or
|
||||
Properties (\* - required):
|
||||
|
||||
- **name\*** - _string_ - bot name
|
||||
- **prefix** - _string_ - bot prefix to use with text commands (if undefined, only slash commands will be available)
|
||||
- **parameterSeparator** - _string_ - used to separate parameters from messages (default: ',')
|
||||
- **clientOptions** - _[ClientOptions](https://discord.js.org/#/docs/main/stable/typedef/ClientOptions)_ - Discord.js client options
|
||||
- **token\*** - _string_ - Discord bot token
|
||||
- **applicationId\*** - _string_ - Discord application ID
|
||||
|
||||
7. Create and add commands to the _Bot_ instance (see [Commands](#commands))
|
||||
8. Start your bot
|
||||
|
||||
```javascript
|
||||
bot.commands.add(
|
||||
new Command({
|
||||
name: "ping",
|
||||
function: (m, a) => {
|
||||
return "pong";
|
||||
},
|
||||
})
|
||||
bot.start(
|
||||
port, // If passed, the application will create a HTTP server
|
||||
true // If true or undefined, the app will register all slash commands in the Discord API
|
||||
);
|
||||
```
|
||||
|
||||
2. Call it from Discord text channel (example prefix: "!")
|
||||
# Commands
|
||||
|
||||
```
|
||||
!ping
|
||||
## Creating and registering a command
|
||||
|
||||
Result:
|
||||
@mention, pong
|
||||
```
|
||||
To create a command, initialize a _Command_ object
|
||||
|
||||
#### Example with arguments
|
||||
|
||||
1. Create a command
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const command = new Command({
|
||||
name: "repeat",
|
||||
function: async (m, a) => {
|
||||
if (a[0] && a[1]) {
|
||||
const count = parseInt(a[1]);
|
||||
if (!isNaN(count)) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await m.channel.send(a[0]);
|
||||
}
|
||||
return `Repeated "${a[0]}" ${count} times`;
|
||||
} else {
|
||||
throw new Error("Number of messages is incorrect");
|
||||
}
|
||||
const cmdGreet = new Command({
|
||||
name: "greet",
|
||||
parameters: [
|
||||
{
|
||||
name: "name",
|
||||
description: "Name that will be greeted",
|
||||
optional: true,
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
aliases: ["hello"],
|
||||
description: "Welcomes someone",
|
||||
usage: "[name]",
|
||||
permissionCheck: "ALL",
|
||||
permissions: ["SEND_MESSAGES"],
|
||||
guilds: undefined,
|
||||
visible: true,
|
||||
slash: true,
|
||||
announceSuccess: true,
|
||||
function: function(p. m) {
|
||||
if(p('name')) {
|
||||
return `Hello, ${p('name')}!`
|
||||
}
|
||||
},
|
||||
else {
|
||||
return 'Hello!'
|
||||
}
|
||||
}
|
||||
});
|
||||
bot.commands.add(command);
|
||||
|
||||
// Register
|
||||
bot.commands.add(cmdGreet)
|
||||
```
|
||||
|
||||
or
|
||||
Properties (\* - required):
|
||||
|
||||
- **name\*** - _string_ - command name
|
||||
- **parameters** - _ParameterSchema[]_ - array of parameters (see [Parameters](#parameters))
|
||||
- **aliases** - _string | string[]_ - array of alternative strings that can call this command (not available for slash commands)
|
||||
- **description** - _string_ - command description
|
||||
- **usage** - _string_ - command usage visible in the help message (if not defined, usage string will be automatically generated based on defined parameters)
|
||||
- **permissionCheck** - _"ALL" | "ANY"_ - whether to check if caller has all defined permission or at least one of them
|
||||
- **permissions** - _[PermissionResolvable](https://discord.js.org/#/docs/main/stable/typedef/PermissionResolvable) | (m?: [Message](https://discord.js.org/#/docs/main/stable/class/Message) | [CommandInteraction](https://discord.js.org/#/docs/main/stable/class/CommandInteraction)) => boolean_ - permissions required to run this command
|
||||
- **guilds** - _string[]_ - array of servers IDs in which this command will be available (if slash command)
|
||||
- **visible** - _boolean_ - whether this command is visible in the help message
|
||||
- **slash** - _boolean_ - whether this command should be registered as a slash command
|
||||
- **announceSuccess** - _boolean_ - whether a command reply should be sent automatically if no other response is defined or the reply should be deleted
|
||||
- **function\*** - _(p: function, m?: [Message](https://discord.js.org/#/docs/main/stable/class/Message) | [CommandInteraction](https://discord.js.org/#/docs/main/stable/class/CommandInteraction)) => void | [MessageEmbed]() | string | [ReplyMessageOptions](https://discord.js.org/#/docs/main/stable/typedef/ReplyMessageOptions)_ - function that will be executed on call
|
||||
|
||||
Register your command in bot client using:
|
||||
|
||||
```javascript
|
||||
bot.commands.add(
|
||||
new Command({
|
||||
name: "repeat",
|
||||
function: async (m, a) => {
|
||||
if (a[0] && a[1]) {
|
||||
const count = parseInt(a[1]);
|
||||
if (!isNaN(count)) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await m.channel.send(a[0]);
|
||||
}
|
||||
return `Repeated "${a[0]}" ${count} times`;
|
||||
} else {
|
||||
throw new Error("Number of messages is incorrect");
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
bot.commands.add(cmd);
|
||||
```
|
||||
|
||||
2. Call it from Discord text channel (example prefix: "!", example separator: "," [default])
|
||||
where (\* - required):
|
||||
|
||||
```
|
||||
!repeat test, 5
|
||||
- **cmd** - _Command_
|
||||
|
||||
Result:
|
||||
test
|
||||
test
|
||||
test
|
||||
test
|
||||
test
|
||||
@caller, Repeated "test" 5 times
|
||||
```
|
||||
## Command function
|
||||
|
||||
## Events
|
||||
### Arguments
|
||||
|
||||
Events are emitted using built-in event emitter **(since version 2.0.0)**. You can handle them using _on_ property
|
||||
- **p** - _function_ - call this function with parameter name to fetch parameter value
|
||||
- **m?** - _[Message](https://discord.js.org/#/docs/main/stable/class/Message) | [CommandInteraction](https://discord.js.org/#/docs/main/stable/class/CommandInteraction)_ - interaction object
|
||||
|
||||
- **READY** - emitted when the client successfully connect to Discord API
|
||||
- **MESSAGE** - emitted for every message sent in any text channel (a [Message](https://discord.js.org/#/docs/main/stable/class/Message) object is passed to the first argument)
|
||||
- **COMMAND** - emitted for every message that gets recognized as a command (a [Message](https://discord.js.org/#/docs/main/stable/class/Message) object is passed to the first argument and a CommandMessageStructure object to the second argument)
|
||||
- **ERROR** - emitted on permission and command errors (an error object is passed to the first argument)
|
||||
> [IMPORTANT]
|
||||
> Do not use client.on('message') and client.on('ready') event handler! This handler is a core part of the command system.
|
||||
### Return value
|
||||
|
||||
Example
|
||||
If function returns (also after resolving a _Promise_):
|
||||
|
||||
- **void** - If _announceSuccess_ property is _true_, bot will automatically send a SUCCESS message (see [Messages](#messages)). If command has been called using slash commands and _announceSuccess_ property is set to _false_, reply will be automatically deleted
|
||||
- **string** - this string will be sent in a reply
|
||||
- **[MessageEmbed](https://discord.js.org/#/docs/main/stable/class/MessageEmbed)** - embedded content will be sent in a reply
|
||||
- **[ReplyMessageOptions](https://discord.js.org/#/docs/main/stable/typedef/ReplyMessageOptions)** - these options will get used to send a reply
|
||||
|
||||
It is possible to manually send replies directly from the command function using the **m** argument. If you are using slash commands don't forget to use the _[editReply](https://discord.js.org/#/docs/main/stable/class/CommandInteraction?scrollTo=editReply)_ method instead of the _reply_ method since a **reply is already deferred** when a command function is being called (read more [here](https://discord.com/developers/docs/interactions/receiving-and-responding)) If you try to create a new reply, you app will throw an error that will result a crash. If you manually reply to a slash command interaction and return _void_ from the command function, a SUCCESS message will not be sent or reply will not get deleted (if you want to disable SUCCESS messages on prefix interactions set _announceSuccess_ property to _false_).
|
||||
|
||||
### Errors
|
||||
|
||||
If a command function will throw an error, it will automatically get caught and your bot will send an ERROR message (see [Messages](#messages)). The app **will not** crash.
|
||||
|
||||
## Parameters
|
||||
|
||||
### Types
|
||||
|
||||
- **string** - text value
|
||||
- **boolean** - True or False
|
||||
- **number** - number (double) value
|
||||
- **user** - _ObjectID_ object with ID value (shown as selection menu in Discord)
|
||||
- **role** - _ObjectID_ object with ID value (shown as selection menu in Discord)
|
||||
- **channel** - _ObjectID_ object with ID value (shown as selection menu in Discord)
|
||||
- **mentionable** - _ObjectID_ object with ID value (shown as selection menu in Discord)
|
||||
|
||||
To get an entity ID from _ObjectID_ use the _value_ property. You can also call _toObject_ method to retrieve full entity object from Discord API
|
||||
|
||||
```javascript
|
||||
bot.on("ready", () => {
|
||||
console.log("Bot is ready!");
|
||||
});
|
||||
ObjectID.toObject(g, "TYPE");
|
||||
```
|
||||
|
||||
## Customization and built-in messages
|
||||
where (\* - required):
|
||||
|
||||
All configuration parameters for messages are stored in _messages_ property (example: bot.messages)
|
||||
- **g\*** - _Guild_ - Guild object to fetch from
|
||||
- **"TYPE"\*** - _"user' | "role" | "channel"_ - defines the entity type
|
||||
|
||||
### Help message
|
||||
### Defining
|
||||
|
||||
Configuration parameters are stored in _messages.help_ property
|
||||
Example:
|
||||
|
||||
- **enabled** - _boolean_ - enables or disables the help message
|
||||
- **title** - _string_ - title of help message
|
||||
- **bottomText** - _string_ - text shown below the title
|
||||
- **color** - _[ColorResolvable](https://discord.js.org/#/docs/main/stable/typedef/ColorResolvable)_ - color of the embedded content
|
||||
- **description** - _string_ - command description (equivalent of the _description_ property in _Command_ object)
|
||||
- **usage** - _string_ - equivalent of the _usage_ property in _Command_ object (You can pass a command name, alias or keyword with the _help_ command to get detailed information about the specified command)
|
||||
```javascript
|
||||
{
|
||||
name: "user",
|
||||
description: "User to mention",
|
||||
optional: false,
|
||||
type: "user"
|
||||
}
|
||||
```
|
||||
|
||||
### System messages
|
||||
Properties (\* - required):
|
||||
|
||||
All options are stored in _messages.system_ property
|
||||
- **name\*** - _string_ - parameter name
|
||||
- **description** - _string_ - parameter description
|
||||
- **optional\*** - _boolean_ - whether this parameter is optional
|
||||
- **type\*** - _"string" | "boolean" | "number" | "user" | "role" | "channel" | "mentionable"_ - parameter type
|
||||
- **choices** - _string[]_ - parameter value choices (to use this, set type to "string")
|
||||
|
||||
There are 3 types of system messages
|
||||
### Reading input value
|
||||
|
||||
- Error message (**ERROR**)
|
||||
- Command not found message (**NOT_FOUND**)
|
||||
- Insufficient permissions message (**PERMISSION**)
|
||||
To read parameter values use a function that is passed in the first argument of a call function (defined in _function_ parameter in _Command_ object)
|
||||
|
||||
Each message can be customized using these properties
|
||||
```javascript
|
||||
p(query, returnType);
|
||||
```
|
||||
|
||||
- **enabled** - _boolean_ - enables or disables the system message
|
||||
where (\* - required):
|
||||
|
||||
- **query\*** - _string_ - parameter name
|
||||
- **returnType** - _"value" | "object"_ - whether to return only parameter value or a full object
|
||||
|
||||
```javascript
|
||||
function: (p, m) => {
|
||||
const userObj = p('user')
|
||||
if(userObj) {
|
||||
const user = user.toObject(m.guild, "user");
|
||||
if(user) {
|
||||
return `Hello, ${user.toString()}`
|
||||
}
|
||||
else {
|
||||
throw new Error('User not found')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Events
|
||||
|
||||
## Handling events
|
||||
|
||||
CommandBot is using _[EventEmitter](https://nodejs.org/api/events.html)_ that is built into Node.js. You can listen to events using the _on_ method.
|
||||
|
||||
```javascript
|
||||
bot.on(eventType, callbackFn);
|
||||
```
|
||||
|
||||
where (\* - required):
|
||||
|
||||
- **eventType\*** - _"READY" | "COMMAND" | "MESSAGE" | "ERROR"_ - type of event that you want to listen to
|
||||
- **callbackFn\*** - _Function_ - a function that will get executed when the event is emitted
|
||||
|
||||
## Event types
|
||||
|
||||
- **READY** - emitted when the app has finished its initialization process
|
||||
- **MESSAGE** - emitted when message is created (similar to _messageCreate_ event from Discord.js) (not emitted when command gets triggered) - a _[Message](https://discord.js.org/#/docs/main/stable/class/Message)_ object is being passed to the first argument
|
||||
- **COMMAND** - emitted when command gets triggered - a _[Message](https://discord.js.org/#/docs/main/stable/class/Message)_ or _[CommandInteraction](https://discord.js.org/#/docs/main/stable/class/CommandInteraction)_ object is being passed to the first argument; an object containing fetched _Command_ object and input parameters is being passed to the second
|
||||
- **ERROR** - emitted when a command function throws an error - an _Error_ object is being passed to the first argument
|
||||
|
||||
# Messages
|
||||
|
||||
## Information messages
|
||||
|
||||
There are 4 information messages:
|
||||
|
||||
- **ERROR** - "An error occurred" message (sent when error occurs)
|
||||
- **SUCCESS** - "Task completed successfully" message (sent after successfully executing a command function)
|
||||
- **PERMISSION** - "Insufficient permissions" message (sent when the caller doesn't have enough permissions)
|
||||
- **NOT_FOUND** - "Command not found" message (sent when someone tries to call a command that does not exist)
|
||||
|
||||
Each of these messages can be customized with these properties:
|
||||
|
||||
- **enabled** - _boolean_ - enables or disables the message
|
||||
- **title** - _string_ - title of the message
|
||||
- **bottomText** - _string_ - text shown below the title
|
||||
- **accentColor** - _[ColorResolvable](https://discord.js.org/#/docs/main/stable/typedef/ColorResolvable)_ - color of the embedded content
|
||||
- **showTimestamp** - _boolean_ - show time and date at the bottom of the embedded content
|
||||
- **footer** - _string_ - footer of the embedded content
|
||||
- **bottomText** - _string_ - text below the title (also known as description)
|
||||
- **accentColor** - _ColorResolvable_ - embed color
|
||||
- **showTimestamp** - _boolean_ - whether to show date and time in the footer
|
||||
- **footer** - _string_ - message footer
|
||||
|
||||
The _messages.system_ also contains a _deleteTimeout_ property. It specifies the time (in ms) after which a system message will be deleted. Set it to _Infinity_ to never delete messages (default value).
|
||||
## Help message
|
||||
|
||||
## Complete example
|
||||
You can customize the help message with these properties:
|
||||
|
||||
```javascript
|
||||
import { Bot, Command } from "commandbot";
|
||||
- **enabled** - _boolean_ - enables or disables the message
|
||||
- **title** - _string_ - title of the message
|
||||
- **bottomText** - _string_ - text below the title (also known as description)
|
||||
- **accentColor** - _ColorResolvable_ - embed color
|
||||
- **description** - _string_ - description shown in the message itself
|
||||
- **usage** - _string_ - usage displayed in the message itself
|
||||
- **visible** - _boolean_ - whether to show the help command in the list
|
||||
|
||||
const bot = new Bot({
|
||||
name: "CommandBot",
|
||||
prefix: "!",
|
||||
});
|
||||
# Issues
|
||||
|
||||
bot.messages.help.accentColor = "#ff0000";
|
||||
bot.messages.system.NOT_FOUND.accentColor = "BLUE";
|
||||
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.
|
||||
|
||||
//Command triggers: !ping, !pong, pingpong
|
||||
bot.commands.add(
|
||||
new Command({
|
||||
name: "ping",
|
||||
aliases: "pong",
|
||||
keywords: "pingpong",
|
||||
function: (m, a) => {
|
||||
return "Bot ping: " + bot.client.ws.ping + "ms";
|
||||
},
|
||||
})
|
||||
);
|
||||
bot.commands.add(
|
||||
new Command({
|
||||
name: "repeat",
|
||||
function: async (m, a) => {
|
||||
if (a[0] && a[1]) {
|
||||
const count = parseInt(a[1]);
|
||||
if (!isNaN(count)) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await m.channel.send(a[0]);
|
||||
}
|
||||
return `Repeated "${a[0]}" ${count} times`;
|
||||
} else {
|
||||
throw new Error("Number of messages is incorrect");
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
Thank you.
|
||||
|
||||
bot.start(3000, "TOKEN");
|
||||
```
|
||||
Created with ❤️ by [GRZANA](https://github.com/GRZ4NA)
|
||||
@@ -2,6 +2,7 @@ import { Bot } from "./src/Bot.js";
|
||||
import { Command } from "./src/Command.js";
|
||||
import { ParameterResolvable, ParameterType } from "./src/types.js";
|
||||
import { MessageEmbed } from "discord.js";
|
||||
import { ObjectID, InputParameter } from "./src/Parameter.js";
|
||||
|
||||
export default Bot;
|
||||
export { Bot, Command, MessageEmbed, ParameterResolvable, ParameterType };
|
||||
export { Bot, Command, MessageEmbed, ParameterResolvable, ParameterType, ObjectID, InputParameter };
|
||||
@@ -0,0 +1,3 @@
|
||||
@echo off
|
||||
echo { "type": "commonjs" } > dist/cjs/package.json
|
||||
echo { "type": "module" } > dist/mjs/package.json
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "commandbot",
|
||||
"version": "3.0.0-beta2a",
|
||||
"version": "3.0.0",
|
||||
"description": "A framework that helps you create your own Discord bot easier.",
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
@@ -27,7 +27,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/GRZ4NA/CommandBot#readme",
|
||||
"dependencies": {
|
||||
"discord.js": "^12.5.3"
|
||||
"axios": "^0.21.1",
|
||||
"discord.js": "^13.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^15.12.4",
|
||||
|
||||
+167
-76
@@ -1,24 +1,18 @@
|
||||
import { Client, Message } from "discord.js";
|
||||
import axios from "axios";
|
||||
import { Client, CommandInteraction, GuildMember, Intents, Message } from "discord.js";
|
||||
import { EventEmitter } from "events";
|
||||
import * as http from "http";
|
||||
import { Command } from "./Command.js";
|
||||
import { CommandManager } from "./CommandManager.js";
|
||||
import {
|
||||
CommandMessageStructure,
|
||||
InitOptions,
|
||||
HelpMessageParams,
|
||||
} from "./types.js";
|
||||
import { PermissionsError } from "./errors.js";
|
||||
import { OperationSuccess, PermissionsError } from "./errors.js";
|
||||
import { HelpMessage } from "./Help.js";
|
||||
import * as http from "http";
|
||||
import { SystemMessageManager } from "./SystemMessage.js";
|
||||
import { EventEmitter } from "events";
|
||||
import { CommandMessageStructure, InitOptions, HelpMessageParams } from "./types.js";
|
||||
|
||||
export declare interface Bot {
|
||||
on(event: "READY", listener: Function): this;
|
||||
on(event: "MESSAGE", listener: (m: Message) => void): this;
|
||||
on(
|
||||
event: "COMMAND",
|
||||
listener: (m: Message, cmdMsg: CommandMessageStructure) => void
|
||||
): this;
|
||||
on(event: "COMMAND", listener: (m: Message | CommandInteraction, cmdMsg: CommandMessageStructure) => void): this;
|
||||
on(event: "ERROR", listener: (e: any) => void): this;
|
||||
}
|
||||
|
||||
@@ -27,6 +21,7 @@ export class Bot extends EventEmitter {
|
||||
client: Client;
|
||||
commands: CommandManager;
|
||||
token: string;
|
||||
applicationId: string;
|
||||
messages: {
|
||||
help: HelpMessageParams;
|
||||
system: SystemMessageManager;
|
||||
@@ -35,22 +30,41 @@ export class Bot extends EventEmitter {
|
||||
/**
|
||||
* Bot instance initializer
|
||||
* @constructor
|
||||
* @param {ConstructorOptions} options - instance properties
|
||||
* @param {InitOptions} options - instance properties
|
||||
* @param {string} options.name - name of your bot
|
||||
* @param {string} options.prefix - prefix used to call commands
|
||||
* @param {string} [options.argumentSeparator=','] - used to get parameters from message
|
||||
* @param {ClientOptions} [options.clientOptions] - client options from Discord.js
|
||||
* @param {string} [options.token] - bot token from Discord Developer Portal
|
||||
* @param {string} options.token - bot token from Discord Developer Portal
|
||||
* @param {string} options.applicationId - Discord application ID used to register slash commands
|
||||
*/
|
||||
constructor(options: InitOptions) {
|
||||
super();
|
||||
this.name = options.name;
|
||||
this.client = new Client(options.clientOptions);
|
||||
this.commands = new CommandManager(
|
||||
options.prefix,
|
||||
options.argumentSeparator
|
||||
this.client = new Client(
|
||||
options.clientOptions || {
|
||||
intents: [
|
||||
Intents.FLAGS.GUILDS,
|
||||
Intents.FLAGS.GUILD_BANS,
|
||||
Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS,
|
||||
Intents.FLAGS.GUILD_INTEGRATIONS,
|
||||
Intents.FLAGS.GUILD_INVITES,
|
||||
Intents.FLAGS.GUILD_MEMBERS,
|
||||
Intents.FLAGS.GUILD_MESSAGES,
|
||||
Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
|
||||
Intents.FLAGS.GUILD_MESSAGE_TYPING,
|
||||
Intents.FLAGS.GUILD_PRESENCES,
|
||||
Intents.FLAGS.GUILD_VOICE_STATES,
|
||||
Intents.FLAGS.GUILD_WEBHOOKS,
|
||||
Intents.FLAGS.DIRECT_MESSAGES,
|
||||
Intents.FLAGS.DIRECT_MESSAGE_REACTIONS,
|
||||
Intents.FLAGS.DIRECT_MESSAGE_TYPING,
|
||||
],
|
||||
}
|
||||
);
|
||||
this.token = options.token || "";
|
||||
this.commands = new CommandManager(options.prefix, options.parameterSeparator);
|
||||
this.token = options.token;
|
||||
this.applicationId = options.applicationId;
|
||||
this.messages = {
|
||||
help: {
|
||||
enabled: true,
|
||||
@@ -59,6 +73,7 @@ export class Bot extends EventEmitter {
|
||||
color: "#ff5500",
|
||||
usage: "[command name (optional)]",
|
||||
bottomText: "List of all available commands",
|
||||
visible: true,
|
||||
},
|
||||
system: new SystemMessageManager(this.name),
|
||||
};
|
||||
@@ -67,80 +82,121 @@ export class Bot extends EventEmitter {
|
||||
/**
|
||||
* Starts your Discord bot
|
||||
* @param {number} [port] - if specified, the app will create a http server that will be listening on the specified port
|
||||
* @param {string} [token] - app token from Discord Developer Portal
|
||||
* @param {boolean} [register] - if true, the bot will register all slash commands in Discord API
|
||||
* @returns *Promise<boolean>*
|
||||
*/
|
||||
async start(port?: number, token?: string): Promise<boolean> {
|
||||
async start(port?: number, register?: boolean): Promise<boolean> {
|
||||
try {
|
||||
console.log(`\nBot name: ${this.name}`);
|
||||
console.log(`Prefix: ${this.commands.prefix} \n`);
|
||||
this.token = token || this.token;
|
||||
console.log(`Prefix: ${this.commands.prefix || "/ (only slash commands)"} \n`);
|
||||
if (this.token === "") {
|
||||
throw new ReferenceError(
|
||||
'No token specified. Please pass your Discord application token as an argument to the "start" method or in the constructor'
|
||||
);
|
||||
throw new ReferenceError('No token specified. Please pass your Discord application token as an argument to the "start" method or in the constructor');
|
||||
}
|
||||
if (port) {
|
||||
process.stdout.write(
|
||||
`Creating http server on port ${port}... `
|
||||
);
|
||||
process.stdout.write(`Creating http server on port ${port}... `);
|
||||
http.createServer().listen(port);
|
||||
console.log("✔");
|
||||
}
|
||||
if (this.messages.help.enabled === true) {
|
||||
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);
|
||||
}
|
||||
process.stdout.write("Connecting to Discord... ");
|
||||
await this.client.login(this.token);
|
||||
this.client.on("ready", () => {
|
||||
console.log("✔\n");
|
||||
this.client.login(this.token);
|
||||
this.client.on("ready", async () => {
|
||||
if (register === undefined || register === true) {
|
||||
console.log("✔");
|
||||
this.register();
|
||||
} else {
|
||||
console.log("✔\n");
|
||||
}
|
||||
this.emit("READY");
|
||||
});
|
||||
this.client.on("message", async (m) => {
|
||||
const cmdMsg = this.commands.fetch(m);
|
||||
if (cmdMsg) {
|
||||
this.emit("COMMAND", m, cmdMsg);
|
||||
try {
|
||||
this.client.on("messageCreate", async (m) => {
|
||||
let cmdMsg: CommandMessageStructure | null = null;
|
||||
try {
|
||||
cmdMsg = this.commands.fetchFromMessage(m);
|
||||
if (cmdMsg) {
|
||||
this.emit("COMMAND", m, cmdMsg);
|
||||
await cmdMsg.command.start(m, cmdMsg.parameters);
|
||||
} catch (e) {
|
||||
if (e instanceof PermissionsError) {
|
||||
this.messages.system.send(
|
||||
"PERMISSION",
|
||||
{
|
||||
user: m.member || undefined,
|
||||
command: cmdMsg.command,
|
||||
},
|
||||
m.channel
|
||||
);
|
||||
} else {
|
||||
this.messages.system.send(
|
||||
"ERROR",
|
||||
{
|
||||
command: cmdMsg.command,
|
||||
user: m.member || undefined,
|
||||
error: e,
|
||||
},
|
||||
m.channel
|
||||
);
|
||||
console.error(e);
|
||||
}
|
||||
this.emit("ERROR", e);
|
||||
return;
|
||||
} else if (this.commands.prefix && m.content.startsWith(this.commands.prefix)) {
|
||||
this.emit("MESSAGE", m);
|
||||
await this.messages.system.send("NOT_FOUND", { phrase: m.content, user: m.member || undefined }, m);
|
||||
} else {
|
||||
this.emit("MESSAGE", m);
|
||||
}
|
||||
} else if (m.content.startsWith(this.commands.prefix)) {
|
||||
this.emit("MESSAGE", m);
|
||||
this.messages.system.send(
|
||||
"NOT_FOUND",
|
||||
{ phrase: m.content, user: m.member || undefined },
|
||||
m.channel
|
||||
);
|
||||
} else {
|
||||
this.emit("MESSAGE", m);
|
||||
} catch (e) {
|
||||
if (e instanceof PermissionsError) {
|
||||
await this.messages.system.send(
|
||||
"PERMISSION",
|
||||
{
|
||||
user: m.member || undefined,
|
||||
command: cmdMsg?.command,
|
||||
},
|
||||
m
|
||||
);
|
||||
this.emit("ERROR", e);
|
||||
} else if (e instanceof OperationSuccess) {
|
||||
await this.messages.system.send("SUCCESS", undefined, m);
|
||||
} else {
|
||||
await this.messages.system.send(
|
||||
"ERROR",
|
||||
{
|
||||
command: cmdMsg?.command,
|
||||
user: m.member || undefined,
|
||||
error: e,
|
||||
},
|
||||
m
|
||||
);
|
||||
this.emit("ERROR", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
this.client.on("interactionCreate", async (i) => {
|
||||
let cmd: CommandMessageStructure | null = null;
|
||||
try {
|
||||
if (!i.isCommand()) return;
|
||||
cmd = this.commands.fetchFromInteraction(i);
|
||||
if (cmd) {
|
||||
this.emit("COMMAND", i, cmd);
|
||||
await cmd.command.start(i, cmd.parameters);
|
||||
} else {
|
||||
await this.messages.system.send(
|
||||
"NOT_FOUND",
|
||||
{
|
||||
phrase: i.commandName,
|
||||
user: i.member as GuildMember,
|
||||
},
|
||||
i as CommandInteraction
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof PermissionsError) {
|
||||
await this.messages.system.send(
|
||||
"PERMISSION",
|
||||
{
|
||||
user: (i.member as GuildMember) || undefined,
|
||||
command: cmd?.command,
|
||||
},
|
||||
i as CommandInteraction
|
||||
);
|
||||
this.emit("ERROR", e);
|
||||
} else if (e instanceof OperationSuccess) {
|
||||
await this.messages.system.send("SUCCESS", undefined, i as CommandInteraction);
|
||||
} else {
|
||||
await this.messages.system.send(
|
||||
"ERROR",
|
||||
{
|
||||
command: cmd?.command,
|
||||
user: (i.member as GuildMember) || undefined,
|
||||
error: e,
|
||||
},
|
||||
i as CommandInteraction
|
||||
);
|
||||
this.emit("ERROR", e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@@ -150,6 +206,41 @@ export class Bot extends EventEmitter {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers commands in Discord API
|
||||
* @returns *Promise<void>*
|
||||
*/
|
||||
async register(): Promise<void> {
|
||||
process.stdout.write("Registering commands... ");
|
||||
await axios.put(
|
||||
`https://discord.com/api/v8/applications/${this.applicationId}/commands`,
|
||||
this.commands.list.filter((c) => !Array.isArray(c.guilds) && c.slash).map((c) => c.toCommandObject()),
|
||||
{ headers: { Authorization: `Bot ${this.token}` } }
|
||||
);
|
||||
const guilds: any = {};
|
||||
await this.commands.list
|
||||
.filter((c) => Array.isArray(c.guilds) && c.guilds.length > 0)
|
||||
.map((c) => {
|
||||
c.guilds?.map(async (g) => {
|
||||
if (!(await this.client.guilds.fetch(g))) {
|
||||
throw new Error(`"${g}" for "${c.name}" command is not a valid guild ID`);
|
||||
}
|
||||
if (guilds[g]) {
|
||||
guilds[g].push(c.toCommandObject());
|
||||
} else {
|
||||
guilds[g] = [];
|
||||
guilds[g].push(c.toCommandObject());
|
||||
}
|
||||
});
|
||||
});
|
||||
for (let i in guilds) {
|
||||
await axios.put(`https://discord.com/api/v8/applications/${this.applicationId}/guilds/${i}/commands`, guilds[i], {
|
||||
headers: { Authorization: `Bot ${this.token}` },
|
||||
});
|
||||
}
|
||||
console.log("✔\n");
|
||||
}
|
||||
}
|
||||
|
||||
export default Bot;
|
||||
+136
-59
@@ -1,98 +1,173 @@
|
||||
//IMPORTS
|
||||
import { Permissions, Message, MessageEmbed } from "discord.js";
|
||||
import {
|
||||
CommandBuilder,
|
||||
PermissionCheckTypes,
|
||||
ParameterResolvable,
|
||||
} from "./types.js";
|
||||
import { MissingParameterError, PermissionsError } from "./errors.js";
|
||||
import { Parameter, ProcessArgument } from "./Parameter.js";
|
||||
import { Permissions, Message, MessageEmbed, CommandInteraction, GuildMember, ReplyMessageOptions, PermissionResolvable } from "discord.js";
|
||||
import { CommandBuilder, ParameterResolvable, PermissionCheckTypes } from "./types.js";
|
||||
import { OperationSuccess, PermissionsError } from "./errors.js";
|
||||
import { DefaultParameter, InputParameter, Parameter } from "./Parameter.js";
|
||||
|
||||
//CLASSES
|
||||
export class Command {
|
||||
name: string;
|
||||
parameters: Parameter[];
|
||||
aliases: string[];
|
||||
keywords: string[];
|
||||
aliases?: string[];
|
||||
description: string;
|
||||
usage: string;
|
||||
usage?: string;
|
||||
permissionCheck: PermissionCheckTypes;
|
||||
permissions: Permissions;
|
||||
permissions?: Permissions | ((m?: Message | CommandInteraction) => boolean);
|
||||
guilds?: string[];
|
||||
visible: boolean;
|
||||
slash: boolean;
|
||||
announceSuccess: boolean;
|
||||
private function: (
|
||||
message?: Message,
|
||||
cmdParams?: ParameterResolvable[]
|
||||
) => void | string | MessageEmbed | Promise<void | string | MessageEmbed>;
|
||||
params: (query: string, returnType?: "value" | "object") => ParameterResolvable | InputParameter | null,
|
||||
interaction?: Message | CommandInteraction
|
||||
) => void | string | MessageEmbed | ReplyMessageOptions | Promise<void | string | MessageEmbed | ReplyMessageOptions>;
|
||||
|
||||
/**
|
||||
* Command constructor
|
||||
* @constructor
|
||||
* @param {CommandBuilder} options - all command properties
|
||||
* @param {string} options.name - command name (used to trigger the command)
|
||||
* @param {string | string[]} [options.aliases] - other words that can trigger the command with prefix
|
||||
* @param {string | string[]} [options.keywords] - other words that can trigger the command without prefix
|
||||
* @param {ParameterSchema} options.parameters - all parameters that can be passed with the command
|
||||
* @param {string | string[]} [options.aliases] - other words that can trigger the command with prefix (not used in slash commands)
|
||||
* @param {string} [options.description="No description"] - command description shown in the help message
|
||||
* @param {string} [options.usage] - command usage description shown in the help message
|
||||
* @param {string} [options.usage] - command usage description shown in the help message (usage message can be automatically generated using parameters)
|
||||
* @param {PermissionCheckTypes} [options.permissionCheck='ANY'] - specifies if the caller has to have all of the specified permissions or any of that
|
||||
* @param {PermissionResolvable} [options.permissions] - permissions needed to run the command
|
||||
* @param {boolean} [options.visible=true] - show command in the help message
|
||||
* @param {boolean} [options.visible=true] - show command in the help message (visible as a slash command)
|
||||
* @param {boolean} [options.slash=true] - whether the command should be available as a slash command
|
||||
* @param {Function} options.function - function that will trigger when the commands gets called
|
||||
*/
|
||||
constructor(options: CommandBuilder) {
|
||||
this.name = options.name.split(" ").join("_");
|
||||
this.parameters = options.parameters || [];
|
||||
if (options.parameters == "no_input" || !options.parameters) {
|
||||
this.parameters = [];
|
||||
} else if (options.parameters == "simple") {
|
||||
this.parameters = [new DefaultParameter()];
|
||||
} else {
|
||||
this.parameters = options.parameters.map((ps) => new Parameter(ps));
|
||||
}
|
||||
this.aliases = Command.processPhrase(options.aliases);
|
||||
this.keywords = Command.processPhrase(options.keywords);
|
||||
this.description = options.description || "No description";
|
||||
this.usage = options.usage || this.generateUsageFromArguments();
|
||||
this.permissionCheck =
|
||||
options.permissionCheck == "ALL" || options.permissionCheck == "ANY"
|
||||
? options.permissionCheck
|
||||
: "ANY";
|
||||
this.permissions = new Permissions(options.permissions || 0);
|
||||
this.visible = options.visible != undefined ? options.visible : true;
|
||||
this.permissionCheck = options.permissionCheck == "ALL" || options.permissionCheck == "ANY" ? options.permissionCheck : "ANY";
|
||||
this.permissions = options.permissions ? (options.permissions instanceof Function ? options.permissions : new Permissions(options.permissions)) : undefined;
|
||||
this.guilds = options.guilds;
|
||||
this.visible = options.visible !== undefined ? options.visible : true;
|
||||
this.slash = options.slash !== undefined ? options.slash : true;
|
||||
this.announceSuccess = options.announceSuccess !== undefined ? options.announceSuccess : true;
|
||||
this.function = options.function;
|
||||
if (!/^[\w-]{1,32}$/.test(this.name)) {
|
||||
throw new Error(`Incorrect command name: ${this.name}`);
|
||||
}
|
||||
if (this.description.length > 100) {
|
||||
throw new Error("Command description is too long");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the command
|
||||
* @param {Message} [message] - a *Message* object used to check caller's permissions. It will get passed to the execution function (specified in *function* property of command's constructor)
|
||||
* @param {string[]} [cmdParams] - list of processed parameters passed in a Discord message
|
||||
* @param {Message | CommandInteraction} [interaction] - a *Message* or *CommandInteraction* object used to check caller's permissions. It will get passed to the execution function (specified in *function* property of command's constructor)
|
||||
* @param {InputParameter[]} [cmdParams] - list of processed parameters passed in a Discord message
|
||||
* @returns *Promise<void>*
|
||||
*/
|
||||
async start(message?: Message, cmdParams?: string[]): Promise<void> {
|
||||
const memberPermissions: Readonly<Permissions> =
|
||||
message?.member?.permissions || new Permissions(0);
|
||||
async start(interaction?: Message | CommandInteraction, cmdParams?: InputParameter[]): Promise<void> {
|
||||
const paramFindFn = function (query: string, returnType?: "value" | "object") {
|
||||
return returnType === "object" ? cmdParams?.find((p) => p.name === query) || null : cmdParams?.find((p) => p.name === query)?.value || null;
|
||||
};
|
||||
const memberPermissions: Readonly<Permissions> = (interaction?.member?.permissions as Permissions) || new Permissions();
|
||||
if (
|
||||
this.permissionCheck == "ALL"
|
||||
? memberPermissions.has(this.permissions, true)
|
||||
: memberPermissions.any(this.permissions, true)
|
||||
!this.permissions ||
|
||||
(this.permissions instanceof Function && this.permissions(interaction)) ||
|
||||
(!(this.permissions instanceof Function)
|
||||
? this.permissionCheck == "ALL"
|
||||
? memberPermissions.has(this.permissions as PermissionResolvable, true)
|
||||
: memberPermissions.any(this.permissions as PermissionResolvable, true)
|
||||
: false)
|
||||
) {
|
||||
let inputArguments: ParameterResolvable[] = cmdParams || [];
|
||||
if (this.parameters.length > 0) {
|
||||
this.parameters.map((a, i) => {
|
||||
if (!inputArguments[i] && !a.optional) {
|
||||
throw new MissingParameterError(a);
|
||||
} else if (inputArguments[i]) {
|
||||
inputArguments[i] = ProcessArgument(
|
||||
inputArguments[i] as string,
|
||||
a.type
|
||||
);
|
||||
}
|
||||
});
|
||||
if (this.guilds && !this.guilds.find((g) => g == interaction?.guild?.id)) {
|
||||
throw new Error("This command is not available here");
|
||||
}
|
||||
const fnResult = await this.function(message, cmdParams);
|
||||
if (typeof fnResult == "string") {
|
||||
await message?.reply(fnResult);
|
||||
if (!this.slash && interaction instanceof CommandInteraction) {
|
||||
throw new Error("This command is not available as a slash command");
|
||||
}
|
||||
if (interaction instanceof CommandInteraction) {
|
||||
await interaction.deferReply();
|
||||
}
|
||||
const fnResult = await this.function.call(this, paramFindFn, interaction);
|
||||
if (
|
||||
fnResult instanceof Object &&
|
||||
("content" in (fnResult as any) ||
|
||||
"embeds" in (fnResult as any) ||
|
||||
"files" in (fnResult as any) ||
|
||||
"components" in (fnResult as any) ||
|
||||
"sticker" in (fnResult as any))
|
||||
) {
|
||||
if (interaction instanceof Message) await interaction.reply(fnResult as ReplyMessageOptions);
|
||||
else if (interaction instanceof CommandInteraction) await interaction.editReply(fnResult as ReplyMessageOptions);
|
||||
} else if (typeof fnResult == "string") {
|
||||
if (interaction instanceof Message) await interaction?.reply({ content: fnResult });
|
||||
else if (interaction instanceof CommandInteraction)
|
||||
await interaction.editReply({
|
||||
content: fnResult,
|
||||
});
|
||||
} else if (fnResult instanceof MessageEmbed) {
|
||||
await message?.channel.send(fnResult);
|
||||
if (interaction instanceof Message) await interaction?.reply({ embeds: [fnResult] });
|
||||
else if (interaction instanceof CommandInteraction) await interaction.editReply({ embeds: [fnResult] });
|
||||
} else if (this.announceSuccess && (interaction instanceof CommandInteraction ? !interaction.replied : true)) {
|
||||
throw new OperationSuccess(this);
|
||||
} else if (interaction instanceof CommandInteraction && !interaction.replied) {
|
||||
await interaction.deleteReply();
|
||||
}
|
||||
} else {
|
||||
throw new PermissionsError(this, message?.member);
|
||||
throw new PermissionsError(this, interaction?.member as GuildMember);
|
||||
}
|
||||
}
|
||||
|
||||
private static processPhrase(phrase?: string | string[]): string[] {
|
||||
/**
|
||||
* Returns object that is ready to be registered in the Discord API
|
||||
* @returns {Object} object
|
||||
*/
|
||||
toCommandObject() {
|
||||
let options: any[] = [];
|
||||
if (this.parameters) {
|
||||
options = this.parameters.map((p) => {
|
||||
let type = 3;
|
||||
if (!/^[\w-]{1,32}$/.test(p.name)) {
|
||||
throw new Error(`Failed to register ${p.name} parameter for ${this.name}: The option name is incorrect`);
|
||||
}
|
||||
if ((p.description || "No description").length > 100) {
|
||||
throw new Error(`Failed to register ${p.name} parameter for ${this.name}: The description is too long`);
|
||||
}
|
||||
if (p.type == "boolean") type = 5;
|
||||
else if (p.type == "user") type = 6;
|
||||
else if (p.type == "channel") type = 7;
|
||||
else if (p.type == "role") type = 8;
|
||||
else if (p.type == "mentionable") type = 9;
|
||||
else if (p.type == "number") type = 10;
|
||||
return {
|
||||
name: p.name,
|
||||
description: p.description || "No description",
|
||||
required: !p.optional,
|
||||
type: p.choices ? 3 : type,
|
||||
choices:
|
||||
p.choices?.map((c) => {
|
||||
return { name: c, value: c };
|
||||
}) || [],
|
||||
};
|
||||
});
|
||||
}
|
||||
return {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
options: options,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string|string[]} phrase?
|
||||
* @returns string
|
||||
*/
|
||||
private static processPhrase(phrase?: string | string[]): string[] | undefined {
|
||||
if (Array.isArray(phrase)) {
|
||||
const buff = phrase.map((p) => {
|
||||
return p.split(" ").join("_");
|
||||
@@ -108,17 +183,19 @@ export class Command {
|
||||
buff.push(phrase.split(" ").join("_"));
|
||||
return buff;
|
||||
} else {
|
||||
return [];
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns string
|
||||
*/
|
||||
private generateUsageFromArguments(): string {
|
||||
let usageTemplate: string = "";
|
||||
this.parameters.map((e) => {
|
||||
usageTemplate += `[${e.name} (${e.type}${
|
||||
e.optional ? ", optional" : ""
|
||||
})] `;
|
||||
});
|
||||
this.parameters &&
|
||||
this.parameters.map((e) => {
|
||||
usageTemplate += `[${e.name} (${e.choices ? e.choices.join(" / ") : e.type}${e.optional ? ", optional" : ""})] `;
|
||||
});
|
||||
return usageTemplate;
|
||||
}
|
||||
}
|
||||
+128
-88
@@ -1,66 +1,41 @@
|
||||
import { Message } from "discord.js";
|
||||
import { CommandInteraction, Message } from "discord.js";
|
||||
import { MissingParameterError } from "./errors.js";
|
||||
import { BooleanParameter, InputParameter, NumberParameter, ObjectParameter, StringParameter } from "./Parameter.js";
|
||||
import { Command } from "./Command.js";
|
||||
import {
|
||||
CommandMessageStructure,
|
||||
GetMode,
|
||||
PhraseOccurrenceData,
|
||||
} from "./types.js";
|
||||
import { CommandMessageStructure, PhraseOccurrenceData } from "./types.js";
|
||||
|
||||
export class CommandManager {
|
||||
list: Command[];
|
||||
prefix: string;
|
||||
argumentSeparator: string;
|
||||
prefix?: string;
|
||||
parameterSeparator: string;
|
||||
|
||||
constructor(prefix: string, argumentSeparator?: string) {
|
||||
/**
|
||||
* @param {string} prefix?
|
||||
* @param {string} parameterSeparator?
|
||||
*/
|
||||
constructor(prefix?: string, parameterSeparator?: string) {
|
||||
this.list = [];
|
||||
this.prefix = prefix;
|
||||
this.argumentSeparator = argumentSeparator || ",";
|
||||
this.parameterSeparator = parameterSeparator || ",";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the command by name, alias or keyword
|
||||
* @param {string} phrase - command name, alias or keyword
|
||||
* @param {GetMode} [mode='ALL'] - specifies which types of command triggers will be used to find the command [*NO_PREFIX* - only keywords; *PREFIX* - names and aliases]
|
||||
* @returns *Command* | *null*
|
||||
*/
|
||||
get(phrase: string, mode?: GetMode): Command | null {
|
||||
if (!mode) mode = "ALL";
|
||||
get(phrase: string): Command | null {
|
||||
let command: Command | null = null;
|
||||
this.list.map((c) => {
|
||||
switch (mode) {
|
||||
case "PREFIX":
|
||||
if (c.name == phrase) {
|
||||
command = c;
|
||||
}
|
||||
c.aliases.map((a) => {
|
||||
if (a == phrase) {
|
||||
command = c;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "NO_PREFIX":
|
||||
c.keywords.map((k) => {
|
||||
if (k == phrase) {
|
||||
command = c;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "ALL":
|
||||
if (c.name == phrase) {
|
||||
command = c;
|
||||
}
|
||||
c.aliases.map((a) => {
|
||||
if (a == phrase) {
|
||||
command = c;
|
||||
}
|
||||
});
|
||||
c.keywords.map((k) => {
|
||||
if (k == phrase) {
|
||||
command = c;
|
||||
}
|
||||
});
|
||||
break;
|
||||
if (c.name == phrase) {
|
||||
command = c;
|
||||
}
|
||||
c.aliases &&
|
||||
c.aliases.map((a) => {
|
||||
if (a == phrase) {
|
||||
command = c;
|
||||
}
|
||||
});
|
||||
});
|
||||
return command;
|
||||
}
|
||||
@@ -75,33 +50,26 @@ export class CommandManager {
|
||||
if (!(command instanceof Command)) {
|
||||
throw new TypeError("Inavlid argument type");
|
||||
}
|
||||
const nameOccurrence: PhraseOccurrenceData | null =
|
||||
this.findPhraseOccurrence(command.name);
|
||||
const nameOccurrence: PhraseOccurrenceData | null = this.findPhraseOccurrence(command.name);
|
||||
if (nameOccurrence) {
|
||||
throw new Error(
|
||||
`The name "${command.name}" has already been registered as ${nameOccurrence.type} in the "${nameOccurrence.command.name}" command.`
|
||||
);
|
||||
throw new Error(`The name "${command.name}" has already been registered as ${nameOccurrence.type} in the "${nameOccurrence.command.name}" command.`);
|
||||
}
|
||||
command.aliases.map((a, i, ar) => {
|
||||
const aliasOccurrence: PhraseOccurrenceData | null =
|
||||
this.findPhraseOccurrence(a);
|
||||
if (aliasOccurrence) {
|
||||
console.warn(
|
||||
`[⚠ WARN] The name "${a}" is already registered as ${aliasOccurrence.type} in the "${aliasOccurrence.command.name}" command. It will be removed from the "${command.name}" command.`
|
||||
);
|
||||
ar.splice(i, 1);
|
||||
}
|
||||
});
|
||||
this.list.map((c) => {
|
||||
command.keywords.map((k, i, a) => {
|
||||
if (c.keywords.indexOf(k) != -1) {
|
||||
command.aliases &&
|
||||
command.aliases.map((a, i, ar) => {
|
||||
const aliasOccurrence: PhraseOccurrenceData | null = this.findPhraseOccurrence(a);
|
||||
if (aliasOccurrence) {
|
||||
console.warn(
|
||||
`[⚠ WARN] The name "${k}" is already a registered KEYWORD for the "${c.name}" command. It will be removed from the "${command.name}" command`
|
||||
`[⚠ WARN] The name "${a}" is already registered as ${aliasOccurrence.type} in the "${aliasOccurrence.command.name}" command. It will be removed from the "${command.name}" command.`
|
||||
);
|
||||
a.splice(i, 1);
|
||||
ar.splice(i, 1);
|
||||
}
|
||||
});
|
||||
command.parameters &&
|
||||
command.parameters.map((p) => {
|
||||
if (p.type != "string" && p.choices) {
|
||||
throw new Error('Parameter with defined choices must have a "string" type');
|
||||
}
|
||||
});
|
||||
});
|
||||
this.list.push(command);
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -115,33 +83,51 @@ export class CommandManager {
|
||||
* @param {Message} message - *Message* object
|
||||
* @returns *CommandMessagesStructure* | *null*
|
||||
*/
|
||||
fetch(message: Message): CommandMessageStructure | null {
|
||||
if (!message.author.bot) {
|
||||
let prefix = false;
|
||||
if (message.content.startsWith(this.prefix)) {
|
||||
prefix = true;
|
||||
}
|
||||
const content = prefix
|
||||
? message.content.replace(this.prefix, "")
|
||||
: message.content;
|
||||
fetchFromMessage(message: Message): CommandMessageStructure | null {
|
||||
if (!this.prefix) return null;
|
||||
if (!message.author.bot && message.content.startsWith(this.prefix)) {
|
||||
const content = message.content.replace(this.prefix, "");
|
||||
const name = content.split(" ")[0];
|
||||
const command = this.get(name, prefix ? "PREFIX" : "NO_PREFIX");
|
||||
const command = this.get(name);
|
||||
if (command) {
|
||||
const argumentsText = content.replace(name, "");
|
||||
const paramsList = argumentsText
|
||||
.split(this.argumentSeparator)
|
||||
.map((a) => {
|
||||
return a.replace(" ", "");
|
||||
});
|
||||
if (
|
||||
(paramsList[0] == "" || paramsList[0] == " ") &&
|
||||
paramsList.length == 1
|
||||
) {
|
||||
const paramsList = argumentsText.split(this.parameterSeparator).map((a) => {
|
||||
return a.replace(" ", "");
|
||||
});
|
||||
if ((paramsList[0] == "" || paramsList[0] == " ") && paramsList.length == 1) {
|
||||
paramsList.splice(0, 1);
|
||||
}
|
||||
const parameters: InputParameter[] = [];
|
||||
command.parameters.map((p, i) => {
|
||||
if (!p.optional && !paramsList[i]) {
|
||||
throw new MissingParameterError(p);
|
||||
} else if (p.optional && !paramsList[i]) {
|
||||
return;
|
||||
}
|
||||
switch (p.type) {
|
||||
case "mentionable":
|
||||
case "channel":
|
||||
case "role":
|
||||
case "user":
|
||||
parameters.push(new ObjectParameter(p, paramsList[i]));
|
||||
break;
|
||||
case "string":
|
||||
parameters.push(new StringParameter(p, paramsList[i]));
|
||||
break;
|
||||
case "boolean":
|
||||
parameters.push(new BooleanParameter(p, paramsList[i]));
|
||||
break;
|
||||
case "number":
|
||||
parameters.push(new NumberParameter(p, paramsList[i]));
|
||||
break;
|
||||
default:
|
||||
parameters.push(new InputParameter(p, paramsList[i]));
|
||||
break;
|
||||
}
|
||||
});
|
||||
return {
|
||||
command: command,
|
||||
parameters: paramsList,
|
||||
parameters: parameters,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
@@ -151,6 +137,60 @@ export class CommandManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches command and parameters from the given *CommandInteraction* object
|
||||
* @param {CommandInteraction} interaction - interaction object
|
||||
* @returns *CommandMessagesStructure* | *null*
|
||||
*/
|
||||
fetchFromInteraction(interaction: CommandInteraction): CommandMessageStructure | null {
|
||||
const cmd = this.get(interaction.commandName);
|
||||
if (cmd) {
|
||||
if (interaction.options?.data) {
|
||||
const paramsList = interaction.options.data;
|
||||
const parameters: InputParameter[] = [];
|
||||
cmd.parameters.map((p) => {
|
||||
const inputParam = paramsList.find((d) => d.name == p.name);
|
||||
if (!p.optional && !inputParam) {
|
||||
throw new MissingParameterError(p);
|
||||
} else if (p.optional && !inputParam) {
|
||||
return;
|
||||
}
|
||||
switch (p.type) {
|
||||
case "mentionable":
|
||||
case "channel":
|
||||
case "role":
|
||||
case "user":
|
||||
parameters.push(new ObjectParameter(p, inputParam?.value));
|
||||
break;
|
||||
case "string":
|
||||
parameters.push(new StringParameter(p, inputParam?.value));
|
||||
break;
|
||||
case "boolean":
|
||||
parameters.push(new BooleanParameter(p, inputParam?.value));
|
||||
break;
|
||||
case "number":
|
||||
parameters.push(new NumberParameter(p, inputParam?.value));
|
||||
break;
|
||||
default:
|
||||
parameters.push(new InputParameter(p, inputParam?.value));
|
||||
break;
|
||||
}
|
||||
});
|
||||
return {
|
||||
command: cmd,
|
||||
parameters: parameters,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} phrase?
|
||||
* @returns PhraseOccurrenceData
|
||||
*/
|
||||
private findPhraseOccurrence(phrase?: string): PhraseOccurrenceData | null {
|
||||
let returnValue: PhraseOccurrenceData | null = null;
|
||||
this.list.map((c) => {
|
||||
@@ -159,7 +199,7 @@ export class CommandManager {
|
||||
command: c,
|
||||
type: "NAME",
|
||||
};
|
||||
} else if (c.aliases.indexOf(phrase || "") != -1) {
|
||||
} else if (c.aliases && c.aliases.indexOf(phrase || "") != -1) {
|
||||
returnValue = {
|
||||
command: c,
|
||||
type: "ALIAS",
|
||||
|
||||
+24
-49
@@ -4,74 +4,53 @@ import { CommandManager } from "./CommandManager.js";
|
||||
import { HelpMessageParams } from "./types.js";
|
||||
|
||||
export class HelpMessage extends Command {
|
||||
constructor(
|
||||
cmdManager: CommandManager,
|
||||
params: HelpMessageParams,
|
||||
botName?: string
|
||||
) {
|
||||
constructor(cmdManager: CommandManager, params: HelpMessageParams, botName?: string) {
|
||||
super({
|
||||
name: "help",
|
||||
usage: params.usage,
|
||||
permissions: 0,
|
||||
permissions: undefined,
|
||||
description: params.description,
|
||||
visible: params.visible,
|
||||
parameters: [
|
||||
{
|
||||
name: "Command name",
|
||||
name: "command_name",
|
||||
description: "Name of the command that you want to get details about",
|
||||
optional: true,
|
||||
type: "TEXT",
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
function: (_, a) => {
|
||||
function: (param, _) => {
|
||||
const helpMsg = new MessageEmbed();
|
||||
helpMsg.setColor(params.color);
|
||||
helpMsg.setTimestamp();
|
||||
helpMsg.setFooter(botName || "");
|
||||
if (helpMsg != null) {
|
||||
if (a && a[0]) {
|
||||
const cmd: Command | null = cmdManager.get(
|
||||
a[0].toString(),
|
||||
"ALL"
|
||||
);
|
||||
if (param("command_name")) {
|
||||
const cmd: Command | null = cmdManager.get(param("command_name")?.toString() || "");
|
||||
if (cmd) {
|
||||
helpMsg.setTitle(
|
||||
`${cmd.name} ${cmd.visible ? "" : "[HIDDEN]"}`
|
||||
);
|
||||
helpMsg.setTitle(`${cmd.name} ${cmd.visible ? "" : "[HIDDEN]"}`);
|
||||
helpMsg.setDescription(cmd.description);
|
||||
if (cmd.usage)
|
||||
helpMsg.addField(
|
||||
"Usage:",
|
||||
`${cmdManager.prefix}${cmd.name} ${cmd.usage}`,
|
||||
false
|
||||
);
|
||||
if (cmd.permissions.toArray(false).length > 0) {
|
||||
let permList: string = "";
|
||||
cmd.permissions.toArray(false).map((p) => {
|
||||
permList += p + "\n";
|
||||
});
|
||||
helpMsg.addField(
|
||||
"Permissions:",
|
||||
permList,
|
||||
false
|
||||
);
|
||||
if (cmd.usage) helpMsg.addField("Usage:", `${cmdManager.prefix || "/"}${cmd.name} ${cmd.usage}`, false);
|
||||
if (cmd.permissions) {
|
||||
if (cmd.permissions instanceof Function) {
|
||||
helpMsg.addField("Permissions:", "Custom", false);
|
||||
} else if (cmd.permissions.toArray(false).length > 0) {
|
||||
let permList: string = "";
|
||||
cmd.permissions.toArray(false).map((p) => {
|
||||
permList += p + "\n";
|
||||
});
|
||||
helpMsg.addField("Permissions:", permList, false);
|
||||
}
|
||||
}
|
||||
if (cmd.aliases.length > 0) {
|
||||
if (cmd.aliases && cmd.aliases.length > 0) {
|
||||
let aList: string = "";
|
||||
cmd.aliases.map((a) => {
|
||||
aList += a + "\n";
|
||||
});
|
||||
helpMsg.addField("Aliases:", aList, false);
|
||||
}
|
||||
if (cmd.keywords.length > 0) {
|
||||
let kwrdList: string = "";
|
||||
cmd.keywords.map((k) => {
|
||||
kwrdList += k + "\n";
|
||||
});
|
||||
helpMsg.addField("Keywords:", kwrdList, false);
|
||||
}
|
||||
} else {
|
||||
throw new ReferenceError(
|
||||
`Command "${a[0]}" does not exist`
|
||||
);
|
||||
throw new ReferenceError(`Command "${param("command_name")}" does not exist`);
|
||||
}
|
||||
} else {
|
||||
helpMsg.setTitle(params.title);
|
||||
@@ -79,11 +58,7 @@ export class HelpMessage extends Command {
|
||||
|
||||
cmdManager.list.map((c) => {
|
||||
if (c.visible) {
|
||||
helpMsg.addField(
|
||||
`${cmdManager.prefix}${c.name} ${c.usage}`,
|
||||
c.description,
|
||||
false
|
||||
);
|
||||
helpMsg.addField(`${cmdManager.prefix || "/"}${c.name} ${c.usage}`, c.description, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+109
-27
@@ -1,33 +1,115 @@
|
||||
import { ParameterTypeError } from "./errors.js";
|
||||
import { CategoryChannel, Guild, GuildMember, NewsChannel, Role, StageChannel, StoreChannel, TextChannel, VoiceChannel } from "discord.js";
|
||||
import { ParameterType, ParameterResolvable } from "./types.js";
|
||||
export interface Parameter {
|
||||
export interface ParameterSchema {
|
||||
name: string;
|
||||
description?: string;
|
||||
optional: boolean;
|
||||
type: ParameterType;
|
||||
choices?: string[];
|
||||
}
|
||||
export const ProcessArgument = (
|
||||
a: string,
|
||||
type: ParameterType
|
||||
): ParameterResolvable => {
|
||||
switch (type) {
|
||||
case "TEXT":
|
||||
return a;
|
||||
case "NUMBER":
|
||||
const numF = parseFloat(a);
|
||||
if (isNaN(numF)) {
|
||||
throw new ParameterTypeError(a, type);
|
||||
} else {
|
||||
return numF;
|
||||
}
|
||||
case "TRUE/FALSE":
|
||||
if (a.toLowerCase() == "true") {
|
||||
return true;
|
||||
} else if (a.toLowerCase() == "false") {
|
||||
return false;
|
||||
} else {
|
||||
throw new ParameterTypeError(a, type);
|
||||
}
|
||||
default:
|
||||
throw new TypeError("Invalied type specified");
|
||||
export class Parameter {
|
||||
name: string;
|
||||
description: string;
|
||||
optional: boolean;
|
||||
type: ParameterType;
|
||||
choices?: string[];
|
||||
|
||||
constructor(options: ParameterSchema) {
|
||||
this.name = options.name;
|
||||
this.description = options.description || "No description";
|
||||
this.optional = options.optional;
|
||||
this.type = options.type;
|
||||
this.choices = options.choices;
|
||||
if (!/^[\w-]{1,32}$/.test(this.name)) {
|
||||
throw new Error(`Parameter name ${this.name} doesn't match the pattern`);
|
||||
}
|
||||
if (this.description.length > 100) {
|
||||
throw new Error(`Parameter ${this.name}: Description too long`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
export class DefaultParameter extends Parameter {
|
||||
constructor() {
|
||||
super({
|
||||
name: "input",
|
||||
description: "No description",
|
||||
type: "string",
|
||||
optional: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
export class InputParameter extends Parameter {
|
||||
value: ParameterResolvable;
|
||||
|
||||
constructor(parameter: Parameter, value: ParameterResolvable) {
|
||||
super(parameter);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
export class StringParameter extends InputParameter {
|
||||
constructor(parameter: Parameter, value: ParameterResolvable) {
|
||||
if (parameter.type != "string") {
|
||||
throw new Error(`Parameter type mismatch`);
|
||||
}
|
||||
if (parameter.choices) {
|
||||
const match = parameter.choices.filter((c) => c.toLowerCase() === value?.toString().toLowerCase());
|
||||
if (match.length === 0) {
|
||||
throw new Error(`Parameter "${parameter.name}" has incorrect value. Please enter one of the following values: ${parameter.choices.join(", ")}`);
|
||||
} else {
|
||||
super(parameter, match[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super(parameter, value?.toString());
|
||||
}
|
||||
}
|
||||
export class BooleanParameter extends InputParameter {
|
||||
constructor(parameter: Parameter, value: ParameterResolvable) {
|
||||
if (parameter.type != "boolean") {
|
||||
throw new Error(`Parameter type mismatch`);
|
||||
}
|
||||
if (value?.toString().toLowerCase() === "true") {
|
||||
super(parameter, true);
|
||||
} else if (value?.toString().toLowerCase() === "false") {
|
||||
super(parameter, false);
|
||||
} else {
|
||||
throw new Error(`Cannot convert "${parameter.name}" parameter to boolean. Please enter either "true" or "false".`);
|
||||
}
|
||||
}
|
||||
}
|
||||
export class NumberParameter extends InputParameter {
|
||||
constructor(parameter: Parameter, value: ParameterResolvable) {
|
||||
if (parameter.type != "number") {
|
||||
throw new Error(`Parameter type mismatch`);
|
||||
}
|
||||
super(parameter, parseFloat(value ? value.toString() : ""));
|
||||
}
|
||||
}
|
||||
export class ObjectParameter extends InputParameter {
|
||||
constructor(parameter: Parameter, value: ParameterResolvable) {
|
||||
if (parameter.type != "channel" && parameter.type != "mentionable" && parameter.type != "user" && parameter.type != "role") {
|
||||
throw new Error(`Parameter type mismatch`);
|
||||
}
|
||||
super(parameter, new ObjectID(value?.toString() || ""));
|
||||
}
|
||||
}
|
||||
export class ObjectID {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
this.id = id.replace(">", "").replace("<@!", "").replace("<#!", "");
|
||||
}
|
||||
async toObject(
|
||||
guild: Guild,
|
||||
type: "channel" | "user" | "role"
|
||||
): Promise<Role | TextChannel | VoiceChannel | CategoryChannel | GuildMember | NewsChannel | StoreChannel | StageChannel | null> {
|
||||
switch (type) {
|
||||
case "channel":
|
||||
return (await guild.channels.fetch(this.id.toString() || "")) || null;
|
||||
case "role":
|
||||
return (await guild.roles.fetch(this.id.toString() || "")) || null;
|
||||
case "user":
|
||||
return (await guild.members.fetch(this.id.toString() || "")) || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
-63
@@ -1,17 +1,8 @@
|
||||
import {
|
||||
ColorResolvable,
|
||||
DMChannel,
|
||||
GuildMember,
|
||||
Message,
|
||||
MessageEmbed,
|
||||
NewsChannel,
|
||||
TextChannel,
|
||||
User,
|
||||
} from "discord.js";
|
||||
import { ColorResolvable, CommandInteraction, GuildMember, Message, MessageEmbed, User } from "discord.js";
|
||||
import { Command } from "./Command.js";
|
||||
import { PermissionsError } from "./errors.js";
|
||||
|
||||
export type MessageType = "PERMISSION" | "ERROR" | "NOT_FOUND";
|
||||
export type MessageType = "PERMISSION" | "ERROR" | "NOT_FOUND" | "SUCCESS";
|
||||
export interface SystemMessageAppearance {
|
||||
enabled: boolean;
|
||||
title: string;
|
||||
@@ -31,6 +22,7 @@ export class SystemMessageManager {
|
||||
PERMISSION: SystemMessageAppearance;
|
||||
ERROR: SystemMessageAppearance;
|
||||
NOT_FOUND: SystemMessageAppearance;
|
||||
SUCCESS: SystemMessageAppearance;
|
||||
deleteTimeout: number;
|
||||
|
||||
constructor(botName?: string) {
|
||||
@@ -57,6 +49,13 @@ export class SystemMessageManager {
|
||||
showTimestamp: true,
|
||||
footer: botName,
|
||||
};
|
||||
this.SUCCESS = {
|
||||
enabled: true,
|
||||
title: "✅ Task completed successfully",
|
||||
accentColor: "#00ff00",
|
||||
showTimestamp: true,
|
||||
footer: botName,
|
||||
};
|
||||
this.deleteTimeout = Infinity;
|
||||
}
|
||||
|
||||
@@ -64,56 +63,35 @@ export class SystemMessageManager {
|
||||
* Generates and sends a system message
|
||||
* @param {MessageType} type - 'ERROR' | 'PERMISSION' | 'NOT_FOUND'
|
||||
* @param {SystemMessageData} [data] - additional data to include in the message
|
||||
* @param {TextChannel | DMChannel | NewsChannel} [channel] - if specified, the generated message will be sent in this channel
|
||||
* @param {Message | CommandInteraction} [interaction] - if specified, the generated message will be sent in this channel
|
||||
* @returns *Promise<MessageEmbed | Message | void>*
|
||||
*/
|
||||
async send(
|
||||
type: MessageType,
|
||||
data?: SystemMessageData,
|
||||
channel?: TextChannel | DMChannel | NewsChannel
|
||||
): Promise<MessageEmbed | Message | void> {
|
||||
async send(type: MessageType, data?: SystemMessageData, interaction?: Message | CommandInteraction): Promise<MessageEmbed | Message | void> {
|
||||
if (this[type]) {
|
||||
if (this[type].enabled === false) {
|
||||
return;
|
||||
}
|
||||
const embed = new MessageEmbed();
|
||||
embed.setTitle(this[type].title);
|
||||
if (this[type].bottomText)
|
||||
embed.setDescription(this[type].bottomText);
|
||||
if (this[type].bottomText) embed.setDescription(this[type].bottomText || "");
|
||||
embed.setColor(this[type].accentColor || "#000");
|
||||
if (this[type].showTimestamp) embed.setTimestamp();
|
||||
if (this[type].footer) embed.setFooter(this[type].footer);
|
||||
if (this[type].footer) embed.setFooter(this[type].footer || "");
|
||||
if (data) {
|
||||
switch (type) {
|
||||
case "ERROR":
|
||||
if (data.command) {
|
||||
embed.addField(
|
||||
"Command name:",
|
||||
data.command.name,
|
||||
false
|
||||
);
|
||||
embed.addField("Command name:", data.command.name, false);
|
||||
}
|
||||
if (data.error) {
|
||||
if (data.error instanceof Error) {
|
||||
embed.addField(
|
||||
"Error details:",
|
||||
data.error.toString(),
|
||||
false
|
||||
);
|
||||
embed.addField("Error details:", data.error.toString(), false);
|
||||
} else if (typeof data.error == "string") {
|
||||
embed.addField(
|
||||
"Error details:",
|
||||
data.error,
|
||||
false
|
||||
);
|
||||
embed.addField("Error details:", data.error, false);
|
||||
}
|
||||
}
|
||||
if (data.user) {
|
||||
embed.addField(
|
||||
"User:",
|
||||
data.user.toString(),
|
||||
false
|
||||
);
|
||||
embed.addField("User:", data.user.toString(), false);
|
||||
}
|
||||
break;
|
||||
case "NOT_FOUND":
|
||||
@@ -123,41 +101,37 @@ export class SystemMessageManager {
|
||||
break;
|
||||
case "PERMISSION":
|
||||
if (data.user) {
|
||||
embed.addField(
|
||||
"User:",
|
||||
data.user.toString(),
|
||||
false
|
||||
);
|
||||
embed.addField("User:", data.user.toString(), false);
|
||||
}
|
||||
if (data.command) {
|
||||
embed.addField(
|
||||
"Command name:",
|
||||
data.command.name,
|
||||
false
|
||||
);
|
||||
embed.addField("Command name:", data.command.name, false);
|
||||
if (data.command.permissions) {
|
||||
let permList: string = "";
|
||||
data.command.permissions
|
||||
.toArray(false)
|
||||
.map((p) => {
|
||||
permList += p + "\n";
|
||||
});
|
||||
embed.addField(
|
||||
"Required permissions:",
|
||||
permList,
|
||||
false
|
||||
);
|
||||
data.command.permissions instanceof Function
|
||||
? (() => {
|
||||
permList = "Custom";
|
||||
})()
|
||||
: data.command.permissions.toArray(false).map((p) => {
|
||||
permList += p + "\n";
|
||||
});
|
||||
embed.addField("Required permissions:", permList, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "SUCCESS":
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (channel) {
|
||||
const message = await channel.send(embed);
|
||||
if (interaction && !(interaction instanceof CommandInteraction)) {
|
||||
const message = await interaction.reply({ embeds: [embed] });
|
||||
if (this.deleteTimeout != Infinity && message.deletable) {
|
||||
await message.delete({ timeout: this.deleteTimeout });
|
||||
setTimeout(async () => {
|
||||
await message.delete();
|
||||
}, this.deleteTimeout || 0);
|
||||
}
|
||||
return message;
|
||||
} else if (interaction && interaction instanceof CommandInteraction) {
|
||||
interaction.replied || interaction.deferred ? await interaction.editReply({ embeds: [embed] }) : await interaction.reply({ embeds: [embed] });
|
||||
} else {
|
||||
return embed;
|
||||
}
|
||||
|
||||
@@ -39,3 +39,10 @@ export class MissingParameterError extends ReferenceError {
|
||||
return `Your request is missing a "${this.argument.name}" parameter which is not optional`;
|
||||
}
|
||||
}
|
||||
|
||||
export class OperationSuccess {
|
||||
command: Command;
|
||||
constructor(c: Command) {
|
||||
this.command = c;
|
||||
}
|
||||
}
|
||||
+18
-14
@@ -1,31 +1,34 @@
|
||||
import { ClientOptions, ColorResolvable } from "discord.js";
|
||||
import { Parameter } from "./Parameter.js";
|
||||
import { ClientOptions, ColorResolvable, CommandInteraction, ReplyMessageOptions } from "discord.js";
|
||||
import { InputParameter, ObjectID, ParameterSchema } from "./Parameter.js";
|
||||
import { PermissionResolvable, Message, MessageEmbed } from "discord.js";
|
||||
import type { Command } from "./Command.js";
|
||||
|
||||
export interface InitOptions {
|
||||
name: string;
|
||||
prefix: string;
|
||||
argumentSeparator?: string;
|
||||
prefix?: string;
|
||||
parameterSeparator?: string;
|
||||
clientOptions?: ClientOptions;
|
||||
token?: string;
|
||||
token: string;
|
||||
applicationId: string;
|
||||
}
|
||||
export type GetMode = "ALL" | "PREFIX" | "NO_PREFIX";
|
||||
export type PermissionCheckTypes = "ALL" | "ANY";
|
||||
export interface CommandBuilder {
|
||||
name: string;
|
||||
parameters?: Parameter[];
|
||||
parameters?: ParameterSchema[] | "simple" | "no_input";
|
||||
aliases?: string[] | string;
|
||||
keywords?: string[] | string;
|
||||
description?: string;
|
||||
usage?: string;
|
||||
permissionCheck?: PermissionCheckTypes;
|
||||
permissions?: PermissionResolvable;
|
||||
permissions?: PermissionResolvable | ((m?: Message | CommandInteraction) => boolean);
|
||||
guilds?: string[];
|
||||
visible?: boolean;
|
||||
slash?: boolean;
|
||||
announceSuccess?: boolean;
|
||||
function: (
|
||||
message?: Message,
|
||||
cmdParams?: ParameterResolvable[]
|
||||
) => void | string | MessageEmbed | Promise<void | string | MessageEmbed>;
|
||||
params: (query: string, returnType?: "value" | "object") => ParameterResolvable | InputParameter | null,
|
||||
interaction?: Message | CommandInteraction
|
||||
) => void | string | MessageEmbed | ReplyMessageOptions | Promise<void | string | MessageEmbed | ReplyMessageOptions>;
|
||||
}
|
||||
export interface PhraseOccurrenceData {
|
||||
command: Command;
|
||||
@@ -33,7 +36,7 @@ export interface PhraseOccurrenceData {
|
||||
}
|
||||
export interface CommandMessageStructure {
|
||||
command: Command;
|
||||
parameters: string[];
|
||||
parameters: InputParameter[];
|
||||
}
|
||||
export interface HelpMessageParams {
|
||||
enabled: boolean;
|
||||
@@ -42,6 +45,7 @@ export interface HelpMessageParams {
|
||||
color: ColorResolvable;
|
||||
description: string;
|
||||
usage: string;
|
||||
visible: boolean;
|
||||
}
|
||||
export type ParameterType = "TEXT" | "TRUE/FALSE" | "NUMBER";
|
||||
export type ParameterResolvable = string | boolean | number;
|
||||
export type ParameterType = "string" | "boolean" | "number" | "user" | "role" | "channel" | "mentionable";
|
||||
export type ParameterResolvable = string | boolean | number | ObjectID | undefined;
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
"pretty": true,
|
||||
"lib": ["esnext"]
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
"exclude": ["node_modules", "dist", "test"]
|
||||
}
|
||||
@@ -2,10 +2,21 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@discordjs/collection@^0.1.6":
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.1.6.tgz#9e9a7637f4e4e0688fd8b2b5c63133c91607682c"
|
||||
integrity sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ==
|
||||
"@discordjs/builders@^0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.4.0.tgz#bb41573ce4824aa9194a53b52c29c5219b610010"
|
||||
integrity sha512-EiwLltKph6TSaPJIzJYdzNc1PnA2ZNaaE0t0ODg3ghnpVHqfgd0YX9/srsleYHW2cw1sfIq+kbM+h0etf7GWLA==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^4.0.1"
|
||||
discord-api-types "^0.22.0"
|
||||
ow "^0.27.0"
|
||||
ts-mixer "^6.0.0"
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@discordjs/collection@^0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.2.1.tgz#ea4bc7b41b7b7b6daa82e439141222ec95c469b2"
|
||||
integrity sha512-vhxqzzM8gkomw0TYRF3tgx7SwElzUlXT/Aa41O7mOcyN6wIJfj5JmDWaO5XGKsGSsNx7F3i5oIlrucCCWV1Nog==
|
||||
|
||||
"@discordjs/form-data@^3.0.1":
|
||||
version "3.0.1"
|
||||
@@ -16,6 +27,16 @@
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
"@sapphire/async-queue@^1.1.4":
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.1.4.tgz#ae431310917a8880961cebe8e59df6ffa40f2957"
|
||||
integrity sha512-fFrlF/uWpGOX5djw5Mu2Hnnrunao75WGey0sP0J3jnhmrJ5TAPzHYOmytD5iN/+pMxS+f+u/gezqHa9tPhRHEA==
|
||||
|
||||
"@sindresorhus/is@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5"
|
||||
integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==
|
||||
|
||||
"@types/node@*":
|
||||
version "16.4.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.10.tgz#e57e2a54fc6da58da94b3571b1cb456d39f88597"
|
||||
@@ -26,25 +47,30 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.5.tgz#7b5b3532053fd14c771ad6598a4ee2c7a85aceca"
|
||||
integrity sha512-6ewfMNmkumZieB/EeJ4cdP1bbJyOlOt5MTwbKMr7WKxyCt2j09H8YWRK6zOd/Jh35Vu/gls39ZUmeu4vHu1WKQ==
|
||||
|
||||
"@types/ws@^7.4.5":
|
||||
"@types/ws@^7.4.5", "@types/ws@^7.4.7":
|
||||
version "7.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
|
||||
integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
abort-controller@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||
dependencies:
|
||||
follow-redirects "^1.10.0"
|
||||
|
||||
callsites@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
@@ -57,24 +83,46 @@ delayed-stream@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
discord.js@^12.5.3:
|
||||
version "12.5.3"
|
||||
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-12.5.3.tgz#56820d473c24320871df9ea0bbc6b462f21cf85c"
|
||||
integrity sha512-D3nkOa/pCkNyn6jLZnAiJApw2N9XrIsXUAdThf01i7yrEuqUmDGc7/CexVWwEcgbQR97XQ+mcnqJpmJ/92B4Aw==
|
||||
dependencies:
|
||||
"@discordjs/collection" "^0.1.6"
|
||||
"@discordjs/form-data" "^3.0.1"
|
||||
abort-controller "^3.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
prism-media "^1.2.9"
|
||||
setimmediate "^1.0.5"
|
||||
tweetnacl "^1.0.3"
|
||||
ws "^7.4.4"
|
||||
discord-api-types@^0.22.0:
|
||||
version "0.22.0"
|
||||
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.22.0.tgz#34dc57fe8e016e5eaac5e393646cd42a7e1ccc2a"
|
||||
integrity sha512-l8yD/2zRbZItUQpy7ZxBJwaLX/Bs2TGaCthRppk8Sw24LOIWg12t9JEreezPoYD0SQcC2htNNo27kYEpYW/Srg==
|
||||
|
||||
event-target-shim@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
discord.js@^13.0.1:
|
||||
version "13.0.1"
|
||||
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.0.1.tgz#58f009706d1d7587fe9ff6c6c781e4540ae085f9"
|
||||
integrity sha512-pEODCFfxypBnGEYpSgjkn1jt70raCS1um7Zp0AXEfW1DcR29wISzQ/WeWdnjP5KTXGi0LTtkRiUjOsMgSoukxA==
|
||||
dependencies:
|
||||
"@discordjs/builders" "^0.4.0"
|
||||
"@discordjs/collection" "^0.2.1"
|
||||
"@discordjs/form-data" "^3.0.1"
|
||||
"@sapphire/async-queue" "^1.1.4"
|
||||
"@types/ws" "^7.4.7"
|
||||
discord-api-types "^0.22.0"
|
||||
node-fetch "^2.6.1"
|
||||
ws "^7.5.1"
|
||||
|
||||
dot-prop@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083"
|
||||
integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==
|
||||
dependencies:
|
||||
is-obj "^2.0.0"
|
||||
|
||||
follow-redirects@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
|
||||
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
|
||||
|
||||
is-obj@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
|
||||
integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
mime-db@1.49.0:
|
||||
version "1.49.0"
|
||||
@@ -93,22 +141,39 @@ node-fetch@^2.6.1:
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
prism-media@^1.2.9:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/prism-media/-/prism-media-1.3.1.tgz#418acd2b122bedea2e834056d678f9a5ad2943ae"
|
||||
integrity sha512-nyYAa3KB4qteJIqdguKmwxTJgy55xxUtkJ3uRnOvO5jO+frci+9zpRXw6QZVcfDeva3S654fU9+26P2OSTzjHw==
|
||||
ow@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/ow/-/ow-0.27.0.tgz#d44da088e8184fa11de64b5813206f9f86ab68d0"
|
||||
integrity sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^4.0.1"
|
||||
callsites "^3.1.0"
|
||||
dot-prop "^6.0.1"
|
||||
lodash.isequal "^4.5.0"
|
||||
type-fest "^1.2.1"
|
||||
vali-date "^1.0.0"
|
||||
|
||||
setimmediate@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||
ts-mixer@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.0.tgz#4e631d3a36e3fa9521b973b132e8353bc7267f9f"
|
||||
integrity sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ==
|
||||
|
||||
tweetnacl@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
||||
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
||||
tslib@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
|
||||
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
|
||||
|
||||
ws@^7.4.4:
|
||||
type-fest@^1.2.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
|
||||
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
|
||||
|
||||
vali-date@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6"
|
||||
integrity sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=
|
||||
|
||||
ws@^7.5.1:
|
||||
version "7.5.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
|
||||
integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
|
||||
Reference in new issue
Block a user