Replace all instances of this.specialArgs with this.options, update docs

This commit is contained in:
Essem 2022-06-24 22:03:34 -05:00
parent d469d36a59
commit df91afee1f
No known key found for this signature in database
GPG key ID: 7D497397CC3A2A8C
14 changed files with 50 additions and 32 deletions

View file

@ -13,7 +13,7 @@ class Command {
this.author = options.message.author; this.author = options.message.author;
this.member = options.message.member; this.member = options.message.member;
this.content = options.content; this.content = options.content;
this.specialArgs = this.options = options.specialArgs; this.options = options.specialArgs;
this.reference = { this.reference = {
messageReference: { messageReference: {
channelID: this.channel.id, channelID: this.channel.id,
@ -31,13 +31,13 @@ class Command {
this.channel = options.interaction.channel; this.channel = options.interaction.channel;
this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user; this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user;
if (options.interaction.data.options) { if (options.interaction.data.options) {
this.specialArgs = this.options = options.interaction.data.options.reduce((obj, item) => { this.options = options.interaction.data.options.reduce((obj, item) => {
obj[item.name] = item.value; obj[item.name] = item.value;
return obj; return obj;
}, {}); }, {});
this.optionsArray = options.interaction.data.options; this.optionsArray = options.interaction.data.options;
} else { } else {
this.specialArgs = this.options = {}; this.options = {};
} }
} }
} }

View file

@ -3,7 +3,7 @@ const mentionRegex = /^<?[@#]?[&!]?(\d+)>?$/;
class AvatarCommand extends Command { class AvatarCommand extends Command {
async run() { async run() {
const member = this.specialArgs.member ?? this.args[0]; const member = this.options.member ?? this.args[0];
const self = await this.client.getRESTUser(this.author.id); const self = await this.client.getRESTUser(this.author.id);
if (this.type === "classic" && this.message.mentions[0]) { if (this.type === "classic" && this.message.mentions[0]) {
return this.message.mentions[0].dynamicAvatarURL(null, 512); return this.message.mentions[0].dynamicAvatarURL(null, 512);

View file

@ -3,7 +3,7 @@ const mentionRegex = /^<?[@#]?[&!]?(\d+)>?$/;
class BannerCommand extends Command { class BannerCommand extends Command {
async run() { async run() {
const member = this.specialArgs.member ?? this.args[0]; const member = this.options.member ?? this.args[0];
const self = await this.client.getRESTUser(this.author.id); const self = await this.client.getRESTUser(this.author.id);
if (this.type === "classic" && this.message.mentions[0]) { if (this.type === "classic" && this.message.mentions[0]) {
return this.message.mentions[0].dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!"; return this.message.mentions[0].dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!";

View file

@ -5,10 +5,10 @@ class CaptionCommand extends ImageCommand {
params(url) { params(url) {
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
let newCaption = newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"); let newCaption = newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n");
if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.specialArgs.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`; if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.options.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`;
return { return {
caption: newCaption, caption: newCaption,
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "futura" font: this.options.font && allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "futura"
}; };
} }

View file

@ -7,8 +7,8 @@ class CaptionTwoCommand extends ImageCommand {
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
return { return {
caption: newArgs && newArgs.trim() ? newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "), caption: newArgs && newArgs.trim() ? newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "),
top: !!this.specialArgs.top, top: !!this.options.top,
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "helvetica" font: this.options.font && allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "helvetica"
}; };
} }

View file

@ -6,9 +6,9 @@ class MemeCommand extends ImageCommand {
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
const [topText, bottomText] = newArgs.split(/(?<!\\),/).map(elem => elem.trim()); const [topText, bottomText] = newArgs.split(/(?<!\\),/).map(elem => elem.trim());
return { return {
top: (this.specialArgs.case ? topText : topText.toUpperCase()).replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"), top: (this.options.case ? topText : topText.toUpperCase()).replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"),
bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : "", bottom: bottomText ? (this.options.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : "",
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "impact" font: this.options.font && allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "impact"
}; };
} }

View file

@ -8,7 +8,7 @@ class MotivateCommand extends ImageCommand {
return { return {
top: topText.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"), top: topText.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"),
bottom: bottomText ? bottomText.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : "", bottom: bottomText ? bottomText.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n") : "",
font: this.specialArgs.font && allowedFonts.includes(this.specialArgs.font.toLowerCase()) ? this.specialArgs.font.toLowerCase() : "times" font: this.options.font && allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "times"
}; };
} }

View file

@ -3,7 +3,7 @@ import ImageCommand from "../../classes/imageCommand.js";
class SnapchatCommand extends ImageCommand { class SnapchatCommand extends ImageCommand {
params(url) { params(url) {
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" "); const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
const position = parseFloat(this.specialArgs.position); const position = parseFloat(this.options.position);
return { return {
caption: newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"), caption: newArgs.replaceAll("&", "&amp;").replaceAll(">", "&gt;").replaceAll("<", "&lt;").replaceAll("\"", "&quot;").replaceAll("'", "&apos;").replaceAll("\\n", "\n"),
pos: isNaN(position) ? 0.5 : position pos: isNaN(position) ? 0.5 : position

View file

@ -3,12 +3,12 @@ import ImageCommand from "../../classes/imageCommand.js";
class SpeechBubbleCommand extends ImageCommand { class SpeechBubbleCommand extends ImageCommand {
params() { params() {
return { return {
water: this.specialArgs.alpha ? "assets/images/speech.png" : "assets/images/speechbubble.png", water: this.options.alpha ? "assets/images/speech.png" : "assets/images/speechbubble.png",
gravity: "north", gravity: "north",
resize: true, resize: true,
yscale: 0.2, yscale: 0.2,
alpha: this.specialArgs.alpha, alpha: this.options.alpha,
flip: this.specialArgs.flip flip: this.options.flip
}; };
} }

View file

@ -2,7 +2,7 @@ import ImageCommand from "../../classes/imageCommand.js";
class UncaptionCommand extends ImageCommand { class UncaptionCommand extends ImageCommand {
params() { params() {
const tolerance = parseFloat(this.specialArgs.tolerance); const tolerance = parseFloat(this.options.tolerance);
return { return {
tolerance: isNaN(tolerance) ? 0.95 : tolerance tolerance: isNaN(tolerance) ? 0.95 : tolerance
}; };

View file

@ -11,9 +11,12 @@ Here's an overview of the environment variables required to run the bot:
Here's an overview of the variables that are not necessarily required for the bot to run, but can greatly enhance its functionality: Here's an overview of the variables that are not necessarily required for the bot to run, but can greatly enhance its functionality:
- `STAYVC`: Set this to true if you want the bot to stay in voice chat after playing music/a sound effect. You can make it leave by using the stop command. - `STAYVC`: Set this to true if you want the bot to stay in voice chat after playing music/a sound effect. You can make it leave by using the stop command.
- `DBL`: An API token from [Top.gg](https://top.gg/). Unnecessary for most users since Top.gg tends to ban forks of bots like esmBot from their list. - `DBL`: An API token from [Top.gg](https://top.gg/). Unnecessary for most users since Top.gg tends to ban forks of bots like esmBot from their list.
- `TENOR`: An API token from [Tenor](https://tenor.com/gifapi). This is not required for using GIFs from Tenor; however, it can greatly reduce resource usage from converting said GIFs. - `TENOR`: An API token from [Tenor](https://tenor.com/gifapi). This is required for using GIFs from Tenor.
- `OUTPUT`: A directory to output the help documentation in Markdown format to. It's recommended to set this to a directory being served by a web server. - `OUTPUT`: A directory to output the help documentation in Markdown format to. It's recommended to set this to a directory being served by a web server.
- `TEMPDIR`: A directory that will store generated images larger than 8MB. It's recommended to set this to a directory being served by a web server. - `TEMPDIR`: A directory that will store generated images larger than 8MB. It's recommended to set this to a directory being served by a web server.
- `TMP_DOMAIN`: The root domain/directory that the images larger than 8MB are stored at. Example: `https://projectlounge.pw/tmp` - `TMP_DOMAIN`: The root domain/directory that the images larger than 8MB are stored at. Example: `https://projectlounge.pw/tmp`
- `THRESHOLD`: A filesize threshold that the bot will start deleting old files in `TEMPDIR` at.
- `METRICS`: The HTTP port to serve [Prometheus](https://prometheus.io/)-compatible metrics on. - `METRICS`: The HTTP port to serve [Prometheus](https://prometheus.io/)-compatible metrics on.
- `API`: Set this to true if you plan on using the image API. Images will be requested from the URLs specified in the `image` block of `servers.json`. - `API`: Set this to "none" if you want to process all images locally. Alternatively, set it to "ws" to use an image API server specified in the `image` block of `servers.json`, or "azure" to use the Azure Functions-based API.
- `AZURE_URL`: Your Azure webhook URL. Only applies if `API` is set to "azure".
- `AZURE_PASS`: An optional password used for Azure requests. Only applies if `API` is set to "azure".

View file

@ -40,27 +40,41 @@ As you can see, the first thing we do is import the Command class. We then creat
The default command name is the same as the filename that you save it as, excluding the `.js` file extension. If you ever want to change the name of the command, just rename the file. The default command name is the same as the filename that you save it as, excluding the `.js` file extension. If you ever want to change the name of the command, just rename the file.
The parameters available to your command consist of the following: The parameters available to your command consist of the following:
- `this.client`: An instance of an Eris `Client`, useful for getting info or performing lower-level communication with the Discord API. Documentation for this type can be found [here](https://abal.moe/Eris/docs/Client). - `this.client`: An instance of an Eris [`Client`](https://abal.moe/Eris/docs/Client), useful for getting info or performing lower-level communication with the Discord API.
- `this.cluster`: The ID of the eris-fleet cluster that the command is being run from. This should be a number greater than or equal to 0. - `this.cluster`: The ID of the eris-fleet cluster that the command is being run from. This should be a number greater than or equal to 0.
- `this.worker`: The ID of the current eris-fleet worker. This should be a number greater than or equal to 0. - `this.worker`: The ID of the current eris-fleet worker. This should be a number greater than or equal to 0.
- `this.ipc`: An eris-fleet `IPC` instance, useful for communication between worker processes. Documentation for this type can be found [here](https://danclay.github.io/eris-fleet/classes/IPC.html). - `this.ipc`: An eris-fleet [`IPC`](https://danclay.github.io/eris-fleet/classes/IPC.html) instance, useful for communication between worker processes.
- `this.message`: An Eris `Message` object of the message that the command was run from, useful for interaction and getting info about the channel/server that the command was run in. Documentation for this type can be found [here](https://abal.moe/Eris/docs/Message). - `this.origOptions`: The raw options object provided to the command by the command handler.
- `this.type`: The type of message that activated the command. Can be "classic" (a regular message) or "application" (slash commands).
- `this.channel`: An Eris [`TextChannel`](https://abal.moe/Eris/docs/TextChannel) object of the channel that the command was run in, useful for getting info about a server and how to respond to a message.
- `this.author`: An Eris [`User`](https://abal.moe/Eris/docs/User) object of the user who ran the command, or a [`Member`](https://abal.moe/Eris/docs/Member) object identical to `this.member` if run in a server as a slash command.
- `this.member`: An Eris [`Member`](https://abal.moe/Eris/docs/Member) object of the server member who ran the command. When running the command outside of a server, this parameter is undefined when run as a "classic" command or a [`User`](https://abal.moe/Eris/docs/User) object identical to `this.author` when run as a slash command.
- `this.options`: When run as a "classic" command, this is an object of special arguments (e.g. `--argument=true`) passed to the command. These arguments are stored in a key/value format, so following the previous example, `this.options.argument` would return true. When run as a slash command, this is an object of every argument passed to the command.
Some options are only available depending on the context/original message type, which can be checked with `this.type`. The options only available with "classic" messages are listed below:
- `this.message`: An Eris [`Message`](https://abal.moe/Eris/docs/Message) object of the message that the command was run from, useful for interaction.
- `this.args`: An array of text arguments passed to the command. - `this.args`: An array of text arguments passed to the command.
- `this.content`: A string of the raw content of the command message, excluding the prefix and command name. - `this.content`: A string of the raw content of the command message, excluding the prefix and command name.
- `this.specialArgs`: An object of special arguments (e.g. `--argument=true`) passed to the command. These arguments are stored in a key/value format, so following the previous example, `this.specialArgs.argument` would return true.
- `this.reference`: An object that's useful if you ever decide to reply to a user inside the command. You can use [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to combine your message content with this parameter. - `this.reference`: An object that's useful if you ever decide to reply to a user inside the command. You can use [`Object.assign`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to combine your message content with this parameter.
The options only available with "application"/slash commands are listed below:
- `this.interaction`: An Eris [`CommandInteraction`](https://abal.moe/Eris/docs/CommandInteraction) object of the incoming slash command data.
- `this.optionsArray`: A raw array of command options. Should rarely be used.
Some static fields are also available and can be set depending on your command. These fields are listed below: Some static fields are also available and can be set depending on your command. These fields are listed below:
- `description`: Your command's description, which is shown in the help command. - `description`: Your command's description, which is shown in the help command.
- `aliases`: An array of command aliases. People will be able to run the command using these as well as the normal command name. - `aliases`: An array of command aliases. People will be able to run the command using these as well as the normal command name.
- `arguments`: An array of command argument types, which are shown in the help command. - `arguments`: An array of command argument types, which are shown in the help command.
- `flags`: An array of objects specifying command flags, or special arguments, that will be shown when running `help <command>` Example: - `flags`: An array of objects specifying command flags, or special arguments, that will be shown when running `help <command>` or a slash command. Example:
```js ```js
static flags = [{ static flags = [{
name: "argument", name: "argument",
description: "Does a thing" type: Constants.ApplicationCommandOptionTypes.STRING, // translates to 3, see https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type
description: "Does a thing",
...
}]; }];
``` ```
- `slashAllowed`: Specifies whether or not the command is available via slash commands.
## The `run` Function ## The `run` Function
The main JS code of your command is specified in the `run` function. This function should return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) of your command output, which is why the `run` function [is an async function by default](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). The return value inside the `Promise` should be either a string or an object; you should return a string whenever you intend to reply with plain text, or an object if you intend to reply with something else, such as an embed or attachment. The main JS code of your command is specified in the `run` function. This function should return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) of your command output, which is why the `run` function [is an async function by default](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). The return value inside the `Promise` should be either a string or an object; you should return a string whenever you intend to reply with plain text, or an object if you intend to reply with something else, such as an embed or attachment.

View file

@ -2,16 +2,17 @@
Here are some instructions for setting up PostgreSQL for use with esmBot. Here are some instructions for setting up PostgreSQL for use with esmBot.
**1. Install PostgreSQL.** **1. Install PostgreSQL.**
#### Alpine
```sh
sudo apk add postgresql
```
#### Debian/Ubuntu #### Debian/Ubuntu
```sh ```sh
sudo apt-get install postgresql postgresql-client sudo apt-get install postgresql postgresql-client
``` ```
#### Alpine
```sh
doas apk add postgresql
```
#### Arch/Manjaro #### Arch/Manjaro
```sh ```sh
sudo pacman -S postgresql sudo pacman -S postgresql

View file

@ -9,7 +9,7 @@ Recommended system requirements:
If you want to run the bot on Windows, [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) is recommended. This guide is somewhat Linux-centric, so for now you're mostly on your own if you decide not to use WSL. If you want to run the bot on Windows, [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) is recommended. This guide is somewhat Linux-centric, so for now you're mostly on your own if you decide not to use WSL.
If you have any further questions regarding setup, feel free to ask in the #self-hosting channel on the [esmBot Support server](https://projectlounge.pw/support). If you have any further questions regarding setup, feel free to ask in the #self-hosting-support channel on the [esmBot Support server](https://projectlounge.pw/support).
## Setup ## Setup
#### 1. Install the required native dependencies. #### 1. Install the required native dependencies.
@ -202,4 +202,4 @@ Make sure Lavalink is running and started up completely. The bot skips loading s
*** ***
If you have any further questions regarding self-hosting, feel free to ask in the #self-hosting channel on the [esmBot Support server](https://projectlounge.pw/support). If you have any further questions regarding self-hosting, feel free to ask in the #self-hosting-support channel on the [esmBot Support server](https://projectlounge.pw/support).