Allow classic commands in DMs even when disabled, add channel name to music end message, better check for API_TYPE, update docs
This commit is contained in:
parent
50bff306c0
commit
e474d838b0
11 changed files with 94 additions and 35 deletions
8
app.js
8
app.js
|
@ -71,12 +71,14 @@ const services = [
|
|||
if (process.env.METRICS && process.env.METRICS !== "") services.push({ name: "prometheus", ServiceWorker: PrometheusWorker });
|
||||
|
||||
const intents = [
|
||||
"guilds",
|
||||
"guildVoiceStates",
|
||||
"guildMessages",
|
||||
"directMessages"
|
||||
];
|
||||
if (types.classic) intents.push("messageContent");
|
||||
if (types.classic) {
|
||||
intents.push("guilds");
|
||||
intents.push("guildMessages");
|
||||
intents.push("messageContent");
|
||||
}
|
||||
|
||||
const Admiral = new Fleet({
|
||||
BotWorker: Shard,
|
||||
|
|
|
@ -18,7 +18,7 @@ class StopCommand extends MusicCommand {
|
|||
players.delete(this.channel.guild.id);
|
||||
queues.delete(this.channel.guild.id);
|
||||
this.success = true;
|
||||
return "🔊 The current voice channel session has ended.";
|
||||
return `🔊 The voice channel session in \`${this.connection.voiceChannel.name}\` has ended.`;
|
||||
}
|
||||
|
||||
static description = "Stops the music";
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
# Config
|
||||
esmBot uses environment variables for configuration. To make managing them easier, a `.env` file is included with the bot and can be used to load the variables on bot startup.
|
||||
esmBot uses a mix of environment variables and JSON for configuration.
|
||||
|
||||
Here's an overview of the environment variables required to run the bot:
|
||||
## Environment Variables (.env)
|
||||
To make managing environment variables easier, an example `.env` file is included with the bot at `.env.example` and can be used to load the variables on startup.
|
||||
|
||||
### Required
|
||||
- `NODE_ENV`: Used for tuning the bot to different environments. If you don't know what to set it to, leave it as is.
|
||||
- `TOKEN`: Your bot's token. You can find this [here](https://discord.com/developers/applications) under your application's Bot tab.
|
||||
- `DB`: The database connection string. By default the `sqlite` and `postgresql` protocols are available, but this can be expanded by putting proper DB driver scripts into `utils/database/`. You can also set this to `dummy` to make the bot not use a database at all.
|
||||
- `OWNER`: Your Discord user ID. This is used for granting yourself access to certain management commands. Adding multiple users is supported by separating the IDs with a comma; however, this is not recommended for security purposes.
|
||||
- `PREFIX`: The bot's default command prefix. Note that servers can set their own individual prefixes via the `prefix` command.
|
||||
- `PREFIX`: The bot's default command prefix for classic commands. Note that servers can set their own individual prefixes via the `prefix` command.
|
||||
|
||||
Here's an overview of the variables that are not necessarily required for the bot to run, but can greatly enhance its functionality:
|
||||
### Optional
|
||||
These 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.
|
||||
- `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.
|
||||
|
@ -19,7 +22,60 @@ Here's an overview of the variables that are not necessarily required for the bo
|
|||
- `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.
|
||||
- `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.
|
||||
- `API_TYPE`: 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 `config/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".
|
||||
- `ADMIN_SERVER`: A server to limit owner-only commands to.
|
||||
- `ADMIN_SERVER`: A Discord server/guild ID to limit owner-only commands such as eval to.
|
||||
|
||||
## JSON
|
||||
The JSON-based configuration files are located in `config/`.
|
||||
|
||||
### commands.json
|
||||
```js
|
||||
{
|
||||
"types": {
|
||||
"classic": false, // Enable/disable "classic" (prefixed) commands, note that classic commands in direct messages will still work
|
||||
"application": true // Enable/disable application commands (slash and context menu commands)
|
||||
},
|
||||
"blacklist": [
|
||||
// Names of commands that you don't want the bot to load
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### messages.json
|
||||
```js
|
||||
{
|
||||
"emotes": [
|
||||
// Discord emote strings to use in the "Processing... this may take a while" messages, e.g. "<a:processing:818243325891051581>" or "⚙️"
|
||||
],
|
||||
"messages": [
|
||||
// Strings to use in the bot's activity message/playing status
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### servers.json
|
||||
```js
|
||||
{
|
||||
"lava": [ // Objects containing info for connecting to Lavalink audio server(s)
|
||||
{
|
||||
"name": "test", // A human-friendly name for the server
|
||||
"url": "localhost:2333", // IP address/domain name and port for the server
|
||||
"auth": "youshallnotpass", // Password/authorization code for the server
|
||||
"local": false // Whether or not the esmBot "assets" folder is located next to the Lavalink jar file
|
||||
}
|
||||
],
|
||||
"image": [ // Objects containing info for connecting to WS image server(s)
|
||||
{
|
||||
"server": "localhost", // IP address or domain name for the server
|
||||
"auth": "verycoolpass100", // Password/authorization code for the server
|
||||
"tls": false // Whether or not this is a secure TLS/wss connection
|
||||
}
|
||||
],
|
||||
"searx": [
|
||||
// URLs for Searx/SearXNG instances used for image/YouTube searches, e.g. "https://searx.projectlounge.pw"
|
||||
// Note: instances must support getting results over JSON
|
||||
]
|
||||
}
|
||||
```
|
|
@ -1,5 +1,5 @@
|
|||
# Custom Commands
|
||||
esmBot has a flexible command handler, allowing you to create new commands and categories simply by creating new files. This page will provide a reference for creating new commands.
|
||||
esmBot has a powerful and flexible command handler, allowing you to create new commands and categories simply by creating new files. This page will provide a reference for creating new commands.
|
||||
|
||||
## Directory Structure
|
||||
The bot loads commands from subdirectories inside of the `commands` directory, which looks something like this by default:
|
||||
|
@ -19,6 +19,9 @@ commands/
|
|||
```
|
||||
As you can see, each command is grouped into categories, which are represented by subdirectories. To create a new category, you can simply create a new directory inside of the `commands` directory, and to create a new command, you can create a new JS file under one of those subdirectories.
|
||||
|
||||
!!! tip
|
||||
The `message` category is special; commands in here act as right-click context menu message commands instead of "classic" or slash commands.
|
||||
|
||||
## Commnand Structure
|
||||
It's recommended to use the `Command` class located in `classes/command.js` to create a new command in most cases. This class provides various parameters and fields that will likely be useful when creating a command. Here is a simple example of a working command file:
|
||||
```js
|
||||
|
@ -46,7 +49,7 @@ The parameters available to your command consist of the following:
|
|||
- `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`](https://danclay.github.io/eris-fleet/classes/IPC.html) instance, useful for communication between worker processes.
|
||||
- `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.type`: The type of message that activated the command. Can be "classic" (a regular message) or "application" (slash/context menu 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.
|
||||
|
@ -59,10 +62,11 @@ Some options are only available depending on the context/original message type,
|
|||
- `this.content`: A string of the raw content of the command message, excluding the prefix and command name.
|
||||
- `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:
|
||||
The options only available with application (slash and context menu) 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.
|
||||
- `this.success`: A boolean value that causes the bot to respond with a normal message when `true`, or an "ephemeral" message (a message that's only visible to the person who ran the command) when `false`.
|
||||
|
||||
Some static fields are also available and can be set depending on your command. These fields are listed below:
|
||||
|
||||
|
@ -80,6 +84,7 @@ static flags = [{
|
|||
```
|
||||
- `slashAllowed`: Specifies whether or not the command is available via slash commands.
|
||||
- `directAllowed`: Specifies whether or not a command is available in direct messages.
|
||||
- `adminOnly`: Specifies whether or not a command should be limited to the bot owner(s).
|
||||
|
||||
## 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.
|
|
@ -16,7 +16,7 @@ You should then modify the `config/servers.json` file to change the IP addresses
|
|||
```json
|
||||
{
|
||||
"lava": [
|
||||
{ "name": "localhost", "url": "localhost:2333", "auth": "youshallnotpass", "local": true }
|
||||
{ "name": "localhost", "url": "lavalink:2333", "auth": "youshallnotpass", "local": true }
|
||||
],
|
||||
"image": [
|
||||
{ "server": "api", "auth": "verycoolpass100", "tls": false }
|
||||
|
|
|
@ -37,7 +37,7 @@ Choose the distro you're using below for insallation instructions.
|
|||
```sh
|
||||
sudo pacman -S git curl cmake pango ffmpeg npm imagemagick libvips sqlite3 libltdl noto-fonts-emoji gobject-introspection libcgif libimagequant meson
|
||||
```
|
||||
You'll also need to install [`ttf-ms-fonts`](https://aur.archlinux.org/packages/ttf-ms-fonts/) from the AUR.
|
||||
You'll also need to install [`ttf-ms-win10-auto`](https://aur.archlinux.org/packages/ttf-ms-win10-auto/) from the AUR.
|
||||
|
||||
***
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ export default async (client, cluster, worker, ipc, member, oldChannel) => {
|
|||
players.delete(connection.originalChannel.guild.id);
|
||||
queues.delete(connection.originalChannel.guild.id);
|
||||
skipVotes.delete(connection.originalChannel.guild.id);
|
||||
client.createMessage(connection.originalChannel.id, "🔊 The current voice channel session has ended.");
|
||||
client.createMessage(connection.originalChannel.id, `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`);
|
||||
}
|
||||
});
|
||||
} else if (member.id === connection.host) {
|
||||
|
@ -74,7 +74,7 @@ export default async (client, cluster, worker, ipc, member, oldChannel) => {
|
|||
players.delete(connection.originalChannel.guild.id);
|
||||
queues.delete(connection.originalChannel.guild.id);
|
||||
skipVotes.delete(connection.originalChannel.guild.id);
|
||||
client.createMessage(connection.originalChannel.id, "🔊 The current voice channel session has ended.");
|
||||
client.createMessage(connection.originalChannel.id, `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`);
|
||||
} else {
|
||||
const randomMember = random(members);
|
||||
players.set(connection.voiceChannel.guild.id, { player: connection.player, type: connection.type, host: randomMember.id, voiceChannel: connection.voiceChannel, originalChannel: connection.originalChannel, loop: connection.loop, shuffle: connection.shuffle, playMessage: connection.playMessage });
|
||||
|
@ -92,7 +92,7 @@ export default async (client, cluster, worker, ipc, member, oldChannel) => {
|
|||
players.delete(connection.originalChannel.guild.id);
|
||||
queues.delete(connection.originalChannel.guild.id);
|
||||
skipVotes.delete(connection.originalChannel.guild.id);
|
||||
await client.createMessage(connection.originalChannel.id, "🔊 The current voice channel session has ended.");
|
||||
await client.createMessage(connection.originalChannel.id, `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
5
shard.js
5
shard.js
|
@ -78,10 +78,7 @@ class Shard extends BaseClusterWorker {
|
|||
log("log", `Loading event from ${file}...`);
|
||||
const eventArray = file.split("/");
|
||||
const eventName = eventArray[eventArray.length - 1].split(".")[0];
|
||||
if (eventName === "messageCreate" && !types.classic) {
|
||||
log("warn", `Skipped loading event from ${file} because classic commands are disabled...`);
|
||||
continue;
|
||||
} else if (eventName === "interactionCreate" && !types.application) {
|
||||
if (eventName === "interactionCreate" && !types.application) {
|
||||
log("warn", `Skipped loading event from ${file} because application commands are disabled`);
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -14,17 +14,19 @@ class AwaitRejoin extends EventEmitter {
|
|||
this.listener = (member, newChannel) => this.verify(member, newChannel);
|
||||
this.bot.on("voiceChannelJoin", this.listener);
|
||||
this.bot.on("voiceChannelSwitch", this.listener);
|
||||
setTimeout(() => this.stop(), 10000);
|
||||
this.stopTimeout = setTimeout(() => this.stop(), 10000);
|
||||
this.checkInterval = setInterval(() => this.verify({ id: memberID }, channel, true), 1000);
|
||||
}
|
||||
|
||||
verify(member, channel, checked) {
|
||||
if (this.channel.id === channel.id) {
|
||||
if ((this.member === member.id && this.channel.voiceMembers.has(member.id)) || (this.anyone && !checked)) {
|
||||
clearTimeout(this.stopTimeout);
|
||||
this.rejoined = true;
|
||||
this.stop(member);
|
||||
return true;
|
||||
} else if (this.anyone && (!checked || this.channel.voiceMembers.size > 1)) {
|
||||
clearTimeout(this.stopTimeout);
|
||||
this.rejoined = true;
|
||||
this.stop(random(this.channel.voiceMembers.filter((i) => i.id !== this.bot.user.id && !i.bot)));
|
||||
return true;
|
||||
|
|
|
@ -11,7 +11,7 @@ import EventEmitter from "events";
|
|||
|
||||
// only requiring this to work around an issue regarding worker threads
|
||||
const nodeRequire = createRequire(import.meta.url);
|
||||
if (process.env.API_TYPE === "none") {
|
||||
if (!process.env.API_TYPE || process.env.API_TYPE === "none") {
|
||||
nodeRequire(`../../build/${process.env.DEBUG && process.env.DEBUG === "true" ? "Debug" : "Release"}/image.node`);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ export async function checkStatus() {
|
|||
const response = await request(`http://${node.url}/version`, { headers: { authorization: node.auth } }).then(res => res.body.text());
|
||||
if (response) newNodes.push(node);
|
||||
} catch {
|
||||
logger.error(`Failed to get status of Lavalink node ${node.host}.`);
|
||||
logger.error(`Failed to get status of Lavalink node ${node.url}.`);
|
||||
}
|
||||
}
|
||||
nodes = newNodes;
|
||||
|
@ -230,8 +230,10 @@ export async function nextSong(client, options, connection, track, info, music,
|
|||
const newTrack = await connection.node.rest.decode(newQueue[0]);
|
||||
nextSong(client, options, connection, newQueue[0], newTrack, music, voiceChannel, host, player.loop, player.shuffle, track);
|
||||
try {
|
||||
if (newQueue[0] !== track && playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete();
|
||||
if (newQueue[0] !== track && player.playMessage.channel.messages.has(player.playMessage.id)) await player.playMessage.delete();
|
||||
if (options.type === "classic") {
|
||||
if (newQueue[0] !== track && playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete();
|
||||
if (newQueue[0] !== track && player.playMessage.channel.messages.has(player.playMessage.id)) await player.playMessage.delete();
|
||||
}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
@ -241,19 +243,14 @@ export async function nextSong(client, options, connection, track, info, music,
|
|||
players.delete(voiceChannel.guild.id);
|
||||
queues.delete(voiceChannel.guild.id);
|
||||
skipVotes.delete(voiceChannel.guild.id);
|
||||
const content = "🔊 The current voice channel session has ended.";
|
||||
const content = `🔊 The voice channel session in \`${voiceChannel.name}\` has ended.`;
|
||||
if (options.type === "classic") {
|
||||
await client.createMessage(options.channel.id, content);
|
||||
} else {
|
||||
await options.interaction.createMessage(content);
|
||||
}
|
||||
try {
|
||||
if (playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete();
|
||||
if (player?.playMessage.channel.messages.has(player.playMessage.id)) await player.playMessage.delete();
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (options.type === "classic") {
|
||||
try {
|
||||
if (playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete();
|
||||
if (player?.playMessage.channel.messages.has(player.playMessage.id)) await player.playMessage.delete();
|
||||
|
|
Loading…
Reference in a new issue