Replace all instances of this.specialArgs with this.options, update docs
This commit is contained in:
parent
d469d36a59
commit
df91afee1f
14 changed files with 50 additions and 32 deletions
|
@ -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 = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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!";
|
||||||
|
|
|
@ -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("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n");
|
let newCaption = newArgs.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
top: (this.options.case ? topText : topText.toUpperCase()).replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
||||||
bottom: bottomText ? (this.specialArgs.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n") : "",
|
bottom: bottomText ? (this.options.case ? bottomText : bottomText.toUpperCase()).replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ class MotivateCommand extends ImageCommand {
|
||||||
return {
|
return {
|
||||||
top: topText.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
top: topText.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
||||||
bottom: bottomText ? bottomText.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n") : "",
|
bottom: bottomText ? bottomText.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").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"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
caption: newArgs.replaceAll("&", "&").replaceAll(">", ">").replaceAll("<", "<").replaceAll("\"", """).replaceAll("'", "'").replaceAll("\\n", "\n"),
|
||||||
pos: isNaN(position) ? 0.5 : position
|
pos: isNaN(position) ? 0.5 : position
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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".
|
|
@ -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.
|
|
@ -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
|
||||||
|
|
|
@ -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).
|
Loading…
Reference in a new issue