Initial Oceanic port (still unfinished), update packages, remove serverinfo and userinfo

This commit is contained in:
Essem 2022-09-23 22:25:16 -05:00
parent bf90ae108a
commit 888f2f8b4a
No known key found for this signature in database
GPG key ID: 7D497397CC3A2A8C
49 changed files with 573 additions and 515 deletions

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
# Logs
logs
*.log
*.log.gz
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View file

@ -7,7 +7,6 @@ esmBot uses the following user-related info:
+ User IDs (needed for many reasons such as the tag commands and replying to users)
+ Avatars (needed for some embeds and the avatar command)
+ Usernames (for embeds and avatar command)
+ Discriminators (userinfo command)
+ Permissions (for checking if a user has perms to run some commands)
+ Whether the user is a bot (needed to prevent other bots from running commands)
@ -17,8 +16,6 @@ esmBot uses the following guild-related info:
+ Guild IDs (for guild-specific settings)
+ Guild channel IDs (for getting where to send a message, storing disabled channels)
+ List of members (for getting permissions and obtaining user objects by ID)
+ Guild names (for embeds)
+ Icons (embeds)
Out of these, **only guild and channel IDs are stored in the database** for configuration info and storing disabled channels/commands, prefixes, and tags.

View file

@ -2,7 +2,7 @@
[![esmBot Support](https://discordapp.com/api/guilds/592399417676529688/embed.png)](https://discord.gg/esmbot) ![GitHub license](https://img.shields.io/github/license/esmBot/esmBot.svg)
esmBot is an easily-extendable, multipurpose, and entertainment-focused Discord bot made using [Eris](https://abal.moe/Eris/) with image, music, and utility commands, alongside many others.
esmBot is an easily-extendable, multipurpose, and entertainment-focused Discord bot made using [Oceanic](https://oceanic.ws) with image, music, and utility commands, alongside many others.
[![Top.gg](https://top.gg/api/widget/429305856241172480.svg)](https://top.gg/bot/429305856241172480)

59
app.js
View file

@ -21,7 +21,7 @@ import { generateList, createPage } from "./utils/help.js";
import { reloadImageConnections } from "./utils/image.js";
// main services
import Eris from "eris";
import { Client } from "oceanic.js";
import pm2 from "pm2";
// some utils
import { promises, readFileSync } from "fs";
@ -71,13 +71,13 @@ esmBot ${esmBotVersion} (${process.env.GIT_REV})
});
const intents = [
"guildVoiceStates",
"directMessages"
"GUILD_VOICE_STATES",
"DIRECT_MESSAGES"
];
if (types.classic) {
intents.push("guilds");
intents.push("guildMessages");
intents.push("messageContent");
intents.push("GUILDS");
intents.push("GUILD_MESSAGES");
intents.push("MESSAGE_CONTENT");
}
// PM2-specific handling
@ -92,7 +92,7 @@ if (process.env.PM2_USAGE) {
switch (packet.data?.type) {
case "reload":
var path = paths.get(packet.data.message);
await load(bot, path, await checkStatus(), true);
await load(client, path, await checkStatus(), true);
break;
case "soundreload":
var soundStatus = await checkStatus();
@ -104,10 +104,10 @@ if (process.env.PM2_USAGE) {
await reloadImageConnections();
break;
case "broadcastStart":
startBroadcast(bot, packet.data.message);
startBroadcast(client, packet.data.message);
break;
case "broadcastEnd":
endBroadcast(bot);
endBroadcast(client);
break;
case "serverCounts":
pm2.sendDataToProcessId(0, {
@ -115,8 +115,8 @@ if (process.env.PM2_USAGE) {
type: "process:msg",
data: {
type: "serverCounts",
guilds: bot.guilds.size,
shards: bot.shards.size
guilds: client.guilds.size,
shards: client.shards.size
},
topic: true
}, (err) => {
@ -142,35 +142,46 @@ if (!types.classic && !types.application) {
process.exit(1);
}
const bot = new Eris(`Bot ${process.env.TOKEN}`, {
const client = new Client({
auth: `Bot ${process.env.TOKEN}`,
allowedMentions: {
everyone: false,
roles: false,
users: true,
repliedUser: true
},
restMode: true,
gateway: {
concurrency: "auto",
maxShards: "auto",
messageLimit: 50,
intents,
connectionTimeout: 30000
presence: {
status: "dnd",
activities: [{
type: "GAME",
name: "Starting esmBot..."
}]
},
intents
},
collectionLimits: {
messages: 50
}
});
bot.once("ready", async () => {
client.once("ready", async () => {
// register commands and their info
const soundStatus = await checkStatus();
logger.log("info", "Attempting to load commands...");
for await (const commandFile of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./commands/"))) {
logger.log("main", `Loading command from ${commandFile}...`);
try {
await load(bot, commandFile, soundStatus);
await load(client, commandFile, soundStatus);
} catch (e) {
logger.error(`Failed to register command from ${commandFile}: ${e}`);
}
}
if (types.application) {
try {
await send(bot);
await send(client);
} catch (e) {
logger.log("error", e);
logger.log("error", "Failed to send command data to Discord, slash/message commands may be unavailable.");
@ -192,7 +203,7 @@ bot.once("ready", async () => {
continue;
}
const { default: event } = await import(file);
bot.on(eventName, event.bind(null, bot));
client.on(eventName, event.bind(null, client));
}
logger.log("info", "Finished loading events.");
@ -204,10 +215,10 @@ bot.once("ready", async () => {
}
// connect to lavalink
if (!status && !connected) connect(bot);
if (!status && !connected) connect(client);
checkBroadcast(bot);
activityChanger(bot);
checkBroadcast(client);
activityChanger(client);
logger.log("info", "Started esmBot.");
});
@ -224,4 +235,4 @@ async function* getFiles(dir) {
}
}
bot.connect();
client.connect();

View file

@ -8,15 +8,16 @@ class Command {
if (options.type === "classic") {
this.message = options.message;
this.channel = options.message.channel;
this.guild = options.message.guild;
this.author = options.message.author;
this.member = options.message.member;
this.content = options.content;
this.options = options.specialArgs;
this.reference = {
messageReference: {
channelID: this.channel.id,
channelID: this.message.channelID,
messageID: this.message.id,
guildID: this.channel.guild ? this.channel.guild.id : undefined,
guildID: this.message.guildID ?? undefined,
failIfNotExists: false
},
allowedMentions: {
@ -27,13 +28,14 @@ class Command {
this.interaction = options.interaction;
this.args = [];
this.channel = options.interaction.channel;
this.guild = options.interaction.guild;
this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user;
if (options.interaction.data.options) {
this.options = options.interaction.data.options.reduce((obj, item) => {
this.options = options.interaction.data.options.raw.reduce((obj, item) => {
obj[item.name] = item.value;
return obj;
}, {});
this.optionsArray = options.interaction.data.options;
this.optionsArray = options.interaction.data.options.raw;
} else {
this.options = {};
}
@ -46,9 +48,10 @@ class Command {
async acknowledge() {
if (this.type === "classic") {
await this.client.sendChannelTyping(this.channel.id);
const channel = this.channel ?? await this.client.rest.channels.get(this.message.channelID);
await channel.sendTyping();
} else if (!this.interaction.acknowledged) {
await this.interaction.acknowledge();
await this.interaction.defer();
}
}

View file

@ -73,7 +73,7 @@ class ImageCommand extends Command {
let status;
if (imageParams.params.type === "image/gif" && this.type === "classic") {
status = await this.processMessage(this.message);
status = await this.processMessage(this.message.channel ?? await this.client.rest.channels.get(this.message.channelID));
}
try {
@ -83,7 +83,7 @@ class ImageCommand extends Command {
}
this.success = true;
return {
file: buffer,
contents: buffer,
name: `${this.constructor.command}.${type}`
};
} catch (e) {
@ -92,14 +92,17 @@ class ImageCommand extends Command {
if (e === "No available servers") return "I can't seem to contact the image servers, they might be down or still trying to start up. Please wait a little bit.";
throw e;
} finally {
if (status && (status.channel.messages ? status.channel.messages.has(status.id) : await this.client.getMessage(status.channel.id, status.id).catch(() => undefined))) await status.delete();
const statusChannel = status.channel ?? await this.client.rest.channels.get(status.channelID);
if (status && (statusChannel.messages ? statusChannel.messages.has(status.id) : await this.client.getMessage(statusChannel.id, status.id).catch(() => undefined))) await status.delete();
runningCommands.delete(this.author.id);
}
}
processMessage(message) {
return this.client.createMessage(message.channel.id, `${random(emotes) || process.env.PROCESSING_EMOJI || "<a:processing:479351417102925854>"} Processing... This might take a while`);
processMessage(channel) {
return channel.createMessage({
content: `${random(emotes) || process.env.PROCESSING_EMOJI || "<a:processing:479351417102925854>"} Processing... This might take a while`
});
}
static init() {

View file

@ -4,9 +4,9 @@ import { players, queues } from "../utils/soundplayer.js";
class MusicCommand extends Command {
constructor(client, options) {
super(client, options);
if (this.channel.guild) {
this.connection = players.get(this.channel.guild.id);
this.queue = queues.get(this.channel.guild.id);
if (this.guild) {
this.connection = players.get(this.guild.id);
this.queue = queues.get(this.guild.id);
}
}

View file

@ -26,8 +26,8 @@ class AvatarCommand extends Command {
} else {
return self.dynamicAvatarURL(null, 512);
}
} else if (this.args.join(" ") !== "" && this.channel.guild) {
const searched = await this.channel.guild.searchMembers(this.args.join(" "));
} else if (this.args.join(" ") !== "" && this.guild) {
const searched = await this.guild.searchMembers(this.args.join(" "));
if (searched.length === 0) return self.dynamicAvatarURL(null, 512);
const user = await this.client.getRESTUser(searched[0].user.id);
return user ? user.dynamicAvatarURL(null, 512) : self.dynamicAvatarURL(null, 512);

View file

@ -26,8 +26,8 @@ class BannerCommand extends Command {
} else {
return "This user doesn't have a banner!";
}
} else if (this.args.join(" ") !== "" && this.channel.guild) {
const searched = await this.channel.guild.searchMembers(this.args.join(" "));
} else if (this.args.join(" ") !== "" && this.guild) {
const searched = await this.guild.searchMembers(this.args.join(" "));
if (searched.length === 0) return self.dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!";
const user = await this.client.getRESTUser(searched[0].user.id);
return user.dynamicBannerURL(null, 512) ?? (self.dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!");

View file

@ -4,20 +4,20 @@ import Command from "../../classes/command.js";
class ChannelCommand extends Command {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
const owners = process.env.OWNER.split(",");
if (!this.member.permissions.has("administrator") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
if (this.args.length === 0) return "You need to provide whether I should be enabled or disabled in this channel!";
if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!";
const guildDB = await db.getGuild(this.channel.guild.id);
const guildDB = await db.getGuild(this.guild.id);
if (this.args[0].toLowerCase() === "disable") {
let channel;
if (this.args[1]?.match(/^<?[@#]?[&!]?\d+>?$/) && this.args[1] >= 21154535154122752n) {
const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "");
if (guildDB.disabled.includes(id)) return "I'm already disabled in this channel!";
channel = this.channel.guild.channels.get(id);
channel = this.guild.channels.get(id);
} else {
if (guildDB.disabled.includes(this.channel.id)) return "I'm already disabled in this channel!";
channel = this.channel;
@ -31,7 +31,7 @@ class ChannelCommand extends Command {
if (this.args[1]?.match(/^<?[@#]?[&!]?\d+>?$/) && this.args[1] >= 21154535154122752n) {
const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "");
if (!guildDB.disabled.includes(id)) return "I'm not disabled in that channel!";
channel = this.channel.guild.channels.get(id);
channel = this.guild.channels.get(id);
} else {
if (!guildDB.disabled.includes(this.channel.id)) return "I'm not disabled in this channel!";
channel = this.channel;

View file

@ -5,7 +5,7 @@ import * as collections from "../../utils/collections.js";
class CommandCommand extends Command {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
const owners = process.env.OWNER.split(",");
if (!this.member.permissions.has("administrator") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
if (this.args.length === 0) return "You need to provide whether you want to enable/disable a command!";
@ -13,7 +13,7 @@ class CommandCommand extends Command {
if (!this.args[1]) return "You need to provide what command to enable/disable!";
if (!collections.commands.has(this.args[1].toLowerCase()) && !collections.aliases.has(this.args[1].toLowerCase())) return "That isn't a command!";
const guildDB = await db.getGuild(this.channel.guild.id);
const guildDB = await db.getGuild(this.guild.id);
const disabled = guildDB.disabled_commands ?? guildDB.disabledCommands;
const command = collections.aliases.get(this.args[1].toLowerCase()) ?? this.args[1].toLowerCase();
@ -21,13 +21,13 @@ class CommandCommand extends Command {
if (command === "command") return "You can't disable that command!";
if (disabled?.includes(command)) return "That command is already disabled!";
await db.disableCommand(this.channel.guild.id, command);
await db.disableCommand(this.guild.id, command);
this.success = true;
return `The command has been disabled. To re-enable it, just run \`${guildDB.prefix}command enable ${command}\`.`;
} else if (this.args[0].toLowerCase() === "enable") {
if (!disabled?.includes(command)) return "That command isn't disabled!";
await db.enableCommand(this.channel.guild.id, command);
await db.enableCommand(this.guild.id, command);
this.success = true;
return `The command \`${command}\` has been re-enabled.`;
}

View file

@ -4,7 +4,7 @@ import Command from "../../classes/command.js";
class CountCommand extends Command {
async run() {
if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) {
if (this.guild && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) {
this.success = false;
return "I don't have the `Embed Links` permission!";
}
@ -37,7 +37,7 @@ class CountCommand extends Command {
description: value.join("\n"),
author: {
name: this.author.username,
icon_url: this.author.avatarURL
iconURL: this.author.avatarURL()
}
}]
});

View file

@ -1,4 +1,4 @@
import { Constants } from "eris";
import { Constants } from "oceanic.js";
import database from "../../utils/database.js";
import * as collections from "../../utils/collections.js";
import { random } from "../../utils/misc.js";
@ -10,7 +10,7 @@ const argTypes = Object.keys(Constants.ApplicationCommandOptionTypes);
class HelpCommand extends Command {
async run() {
const { prefix } = this.channel.guild ? await database.getGuild(this.channel.guild.id) : "N/A";
const { prefix } = this.guild ? await database.getGuild(this.guild.id) : "N/A";
if (this.args.length !== 0 && (collections.commands.has(this.args[0].toLowerCase()) || collections.aliases.has(this.args[0].toLowerCase()))) {
const command = collections.aliases.get(this.args[0].toLowerCase()) ?? this.args[0].toLowerCase();
const info = collections.info.get(command);
@ -19,9 +19,9 @@ class HelpCommand extends Command {
embeds: [{
author: {
name: "esmBot Help",
icon_url: this.client.user.avatarURL
iconURL: this.client.user.avatarURL()
},
title: `${this.channel.guild ? prefix : ""}${command}`,
title: `${this.guild ? prefix : ""}${command}`,
url: "https://projectlounge.pw/esmBot/help.html",
description: command === "tags" ? "The main tags command. Check the help page for more info: https://projectlounge.pw/esmBot/help.html" : info.description,
color: 16711680,
@ -54,12 +54,12 @@ class HelpCommand extends Command {
}
return embed;
} else {
if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) {
if (this.guild && !this.channel.permissionsOf(this.client.user.id).has("EMBED_LINKS")) {
this.success = false;
return "I don't have the `Embed Links` permission!";
}
const pages = [];
if (help.categories === help.categoryTemplate && !help.generated) await help.generateList();
if (help.categories === help.categoryTemplate && !help.generated) help.generateList();
for (const category of Object.keys(help.categories)) {
const splitPages = help.categories[category].map((item, index) => {
return index % 15 === 0 ? help.categories[category].slice(index, index + 15) : null;
@ -83,7 +83,7 @@ class HelpCommand extends Command {
embeds: [{
author: {
name: "esmBot Help",
icon_url: this.client.user.avatarURL
iconURL: this.client.user.avatarURL()
},
title: value.title,
description: value.page.join("\n"),
@ -93,7 +93,7 @@ class HelpCommand extends Command {
},
fields: [{
name: "Prefix",
value: this.channel.guild ? prefix : "N/A"
value: this.guild ? prefix : "N/A"
}, {
name: "Tip",
value: random(tips)
@ -101,7 +101,7 @@ class HelpCommand extends Command {
}]
});
}
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, author: this.author }, embeds);
}
}

View file

@ -8,7 +8,7 @@ import Command from "../../classes/command.js";
class ImageSearchCommand extends Command {
async run() {
this.success = false;
if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!";
if (this.channel && !this.channel.permissionsOf(this.client.user.id).has("EMBED_LINKS")) return "I don't have the `Embed Links` permission!";
const query = this.options.query ?? this.args.join(" ");
if (!query || !query.trim()) return "You need to provide something to search for!";
await this.acknowledge();
@ -30,7 +30,7 @@ class ImageSearchCommand extends Command {
},
author: {
name: this.author.username,
icon_url: this.author.avatarURL
iconURL: this.author.avatarURL()
}
}]
});

View file

@ -8,7 +8,7 @@ class ImageStatsCommand extends Command {
embeds: [{
"author": {
"name": "esmBot Image Statistics",
"icon_url": this.client.user.avatarURL
"iconURL": this.client.user.avatarURL
},
"color": 16711680,
"description": `The bot is currently connected to ${connections.size} image server(s).`,

View file

@ -5,7 +5,8 @@ import { getServers } from "../../utils/misc.js";
class InfoCommand extends Command {
async run() {
const owner = await this.client.getRESTUser(process.env.OWNER.split(",")[0]);
let owner = this.client.users.get(process.env.OWNER.split(",")[0]);
if (!owner) owner = await this.client.getRESTUser(process.env.OWNER.split(",")[0]);
const servers = await getServers(this.client);
await this.acknowledge();
return {
@ -13,7 +14,7 @@ class InfoCommand extends Command {
color: 16711680,
author: {
name: "esmBot Info/Credits",
icon_url: this.client.user.avatarURL
iconURL: this.client.user.avatarURL()
},
description: `This instance is managed by **${owner.username}#${owner.discriminator}**.`,
fields: [{

View file

@ -3,14 +3,20 @@ import Command from "../../classes/command.js";
class PingCommand extends Command {
async run() {
if (this.type === "classic") {
const pingMessage = await this.client.createMessage(this.channel.id, Object.assign({
const pingMessage = await this.client.rest.channels.createMessage(this.message.channelID, Object.assign({
content: "🏓 Ping?"
}, this.reference));
await pingMessage.edit(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.channel.guild ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.channel.guild.id]).latency)}ms` : ""}\n\`\`\``);
await pingMessage.edit({
content: `🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.message.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.message.guildID]).latency)}ms` : ""}\n\`\`\``
});
} else {
await this.interaction.createMessage("🏓 Ping?");
const pingMessage = await this.interaction.getOriginalMessage();
await this.interaction.editOriginalMessage(`🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - Math.floor((this.interaction.id / 4194304) + 1420070400000)}ms${this.interaction.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.interaction.guildID]).latency)}ms` : ""}\n\`\`\``);
await this.interaction.createMessage({
content: "🏓 Ping?"
});
const pingMessage = await this.interaction.getOriginal();
await this.interaction.editOriginal({
content: `🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - Math.floor((this.interaction.id / 4194304) + 1420070400000)}ms${this.interaction.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.interaction.guildID]).latency)}ms` : ""}\n\`\`\``
});
}
}

View file

@ -3,15 +3,15 @@ import Command from "../../classes/command.js";
class PrefixCommand extends Command {
async run() {
if (!this.channel.guild) return `The current prefix is \`${process.env.PREFIX}\``;
const guild = await database.getGuild(this.channel.guild.id);
if (!this.guild) return `The current prefix is \`${process.env.PREFIX}\`.`;
const guild = await database.getGuild(this.guild.id);
if (this.args.length !== 0) {
const owners = process.env.OWNER.split(",");
if (!this.member.permissions.has("administrator") && !owners.includes(this.member.id)) {
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) {
this.success = false;
return "You need to be an administrator to change the bot prefix!";
}
await database.setPrefix(this.args[0], this.channel.guild);
await database.setPrefix(this.args[0], this.guild);
return `The prefix has been changed to ${this.args[0]}.`;
} else {
return `The current prefix is \`${guild.prefix}\`.`;

View file

@ -13,7 +13,7 @@ class QrCreateCommand extends Command {
qrcode.toFileStream(writable, this.content, { margin: 1 });
const file = await this.streamToBuf(writable);
return {
file: file,
contents: file,
name: "qr.png"
};
}

View file

@ -7,7 +7,7 @@ class RestartCommand extends Command {
this.success = false;
return "Only the bot owner can restart me!";
}
await this.client.createMessage(this.channel.id, Object.assign({
await this.message.channel.createMessage(Object.assign({
content: "esmBot is restarting."
}, this.reference));
process.exit(1);

View file

@ -1,58 +0,0 @@
import Command from "../../classes/command.js";
class ServerInfoCommand extends Command {
async run() {
if (!this.channel.guild) {
this.success = false;
return "This command only works in servers!";
}
const owner = await this.channel.guild.members.get(this.channel.guild.ownerID);
return {
embeds: [{
title: this.channel.guild.name,
thumbnail: {
url: this.channel.guild.iconURL
},
image: {
url: this.channel.guild.bannerURL
},
color: 16711680,
fields: [
{
name: "🔢 **ID:**",
value: this.channel.guild.id
},
{
name: "👤 **Owner:**",
value: owner ? `${owner.user.username}#${owner.user.discriminator}` : this.channel.guild.ownerID
},
{
name: "🗓 **Created on:**",
value: `<t:${Math.floor(this.channel.guild.createdAt / 1000)}:F>`
},
{
name: "👥 **Users:**",
value: this.channel.guild.memberCount,
inline: true
},
{
name: "💬 **Channels:**",
value: this.channel.guild.channels.size,
inline: true
},
{
name: "😃 **Emojis:**",
value: this.channel.guild.emojis.length,
inline: true
}
]
}]
};
}
static description = "Gets some info about the server";
static aliases = ["server"];
static directAllowed = false;
}
export default ServerInfoCommand;

View file

@ -2,7 +2,7 @@ import { readFileSync } from "fs";
const { version } = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url)));
import os from "os";
import Command from "../../classes/command.js";
import { VERSION } from "eris";
import { VERSION } from "oceanic.js";
import pm2 from "pm2";
import { getServers } from "../../utils/misc.js";
@ -10,14 +10,15 @@ class StatsCommand extends Command {
async run() {
const uptime = process.uptime() * 1000;
const connUptime = this.client.uptime;
const owner = await this.client.getRESTUser(process.env.OWNER.split(",")[0]);
let owner = this.client.users.get(process.env.OWNER.split(",")[0]);
if (!owner) owner = await this.client.getRESTUser(process.env.OWNER.split(",")[0]);
const servers = await getServers(this.client);
const processMem = `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`;
return {
embeds: [{
"author": {
"name": "esmBot Statistics",
"icon_url": this.client.user.avatarURL
"iconURL": this.client.user.avatarURL()
},
"description": `This instance is managed by **${owner.username}#${owner.discriminator}**.`,
"color": 16711680,
@ -50,7 +51,7 @@ class StatsCommand extends Command {
},
{
"name": "Library",
"value": `Eris ${VERSION}`,
"value": `Oceanic ${VERSION}`,
"inline": true
},
{
@ -60,7 +61,7 @@ class StatsCommand extends Command {
},
{
"name": "Shard",
"value": this.channel.guild ? this.client.guildShardMap[this.channel.guild.id] : "N/A",
"value": this.guild ? this.client.guildShardMap[this.guild.id] : "N/A",
"inline": true
},
{

View file

@ -1,64 +0,0 @@
import Command from "../../classes/command.js";
class UserInfoCommand extends Command {
async run() {
const getUser = this.message.mentions.length >= 1 ? this.message.mentions[0] : (this.args.length !== 0 ? this.client.users.get(this.args[0]) : this.author);
let user;
if (getUser) {
user = getUser;
} else if (this.args[0].match(/^<?[@#]?[&!]?\d+>?$/) && this.args[0] >= 21154535154122752n) {
try {
user = await this.client.getRESTUser(this.args[0]);
} catch {
user = this.author;
}
} else if (this.args.join(" ") !== "") {
const userRegex = new RegExp(this.args.join("|"), "i");
const member = this.client.users.find(element => {
return userRegex.test(element.username);
});
user = member ?? this.author;
} else {
user = this.author;
}
const member = this.channel.guild ? this.channel.guild.members.get(user.id) : undefined;
return {
embeds: [{
title: `${user.username}#${user.discriminator}`,
thumbnail: {
url: user.avatarURL
},
color: 16711680,
fields: [
{
name: "🔢 **ID:**",
value: user.id
},
{
name: "📛 **Nickname:**",
value: member ? (member.nick ?? "None") : "N/A"
},
{
name: "🤖 **Bot:**",
value: user.bot ? "Yes" : "No"
},
{
name: "🗓️ **Joined Discord on:**",
value: `<t:${Math.floor(user.createdAt / 1000)}:F>`
},
{
name: "💬 **Joined this server on:**",
value: member ? `<t:${Math.floor(member.joinedAt / 1000)}:F>` : "N/A"
}
]
}]
};
}
static description = "Gets info about a user";
static aliases = ["user"];
static arguments = ["[mention/id]"];
static slashAllowed = false;
}
export default UserInfoCommand;

View file

@ -4,9 +4,9 @@ import MusicCommand from "../../classes/musicCommand.js";
class HostCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (this.connection.host !== this.author.id && !process.env.OWNER.split(",").includes(this.connection.host)) return "Only the current voice session host can choose another host!";
const input = this.options.user ?? this.args.join(" ");
if (input?.trim()) {
@ -33,15 +33,15 @@ class HostCommand extends MusicCommand {
}
if (!user) return "I can't find that user!";
if (user.bot) return "This is illegal, you know.";
const member = this.channel.guild ? this.channel.guild.members.get(user.id) : undefined;
const member = this.guild ? this.guild.members.get(user.id) : undefined;
if (!member) return "That user isn't in this server!";
const object = this.connection;
object.host = member.id;
players.set(this.channel.guild.id, object);
players.set(this.guildID, object);
this.success = true;
return `🔊 ${member.mention} is the new voice channel host.`;
} else {
const member = this.channel.guild ? this.channel.guild.members.get(players.get(this.channel.guild.id).host) : undefined;
const member = this.guild ? this.guild.members.get(players.get(this.guild.id).host) : undefined;
this.success = true;
return `🔊 The current voice channel host is **${member?.username}#${member?.discriminator}**.`;
}

View file

@ -4,13 +4,13 @@ import MusicCommand from "../../classes/musicCommand.js";
class LoopCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (this.connection.host !== this.author.id && !this.member.permissions.has("manageChannels")) return "Only the current voice session host can loop the music!";
const object = this.connection;
object.loop = !object.loop;
players.set(this.channel.guild.id, object);
players.set(this.guild.id, object);
this.success = true;
return object.loop ? "🔊 The player is now looping." : "🔊 The player is no longer looping.";
}

View file

@ -4,9 +4,9 @@ import MusicCommand from "../../classes/musicCommand.js";
class NowPlayingCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
const player = this.connection.player;
if (!player) return "I'm not playing anything!";
const track = await player.node.rest.decode(player.track);
@ -17,7 +17,7 @@ class NowPlayingCommand extends MusicCommand {
color: 16711680,
author: {
name: "Now Playing",
icon_url: this.client.user.avatarURL
iconURL: this.client.user.avatarURL
},
fields: [{
name: " Title",
@ -29,7 +29,7 @@ class NowPlayingCommand extends MusicCommand {
},
{
name: "💬 Channel",
value: this.channel.guild.channels.get(this.member.voiceState.channelID).name
value: this.guild.channels.get(this.member.voiceState.channelID).name
},
{
name: "🌐 Node",

View file

@ -7,9 +7,9 @@ import MusicCommand from "../../classes/musicCommand.js";
class QueueCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!";
const player = this.connection;
const node = nodes.filter((val) => val.name === player.player.node.name)[0];
@ -30,7 +30,7 @@ class QueueCommand extends MusicCommand {
embeds: [{
author: {
name: "Queue",
icon_url: this.client.user.avatarURL
iconURL: this.client.user.avatarURL
},
color: 16711680,
footer: {

View file

@ -4,16 +4,16 @@ import MusicCommand from "../../classes/musicCommand.js";
class RemoveCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (this.connection.host !== this.author.id && !process.env.OWNER.split(",").includes(this.connection.host)) return "Only the current voice session host can remove songs from the queue!";
const pos = parseInt(this.options.position ?? this.args[0]);
if (isNaN(pos) || pos > this.queue.length || pos < 1) return "That's not a valid position!";
const removed = this.queue.splice(pos, 1);
if (removed.length === 0) return "That's not a valid position!";
const track = await this.connection.player.node.rest.decode(removed[0]);
queues.set(this.channel.guild.id, this.queue);
queues.set(this.guildID, this.queue);
this.success = true;
return `🔊 The song \`${track.title ? track.title : "(blank)"}\` has been removed from the queue.`;
}

View file

@ -3,9 +3,9 @@ import MusicCommand from "../../classes/musicCommand.js";
class SeekCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (this.connection.host !== this.author.id) return "Only the current voice session host can seek the music!";
const player = this.connection.player;
const track = await player.node.rest.decode(player.track);

View file

@ -4,13 +4,13 @@ import MusicCommand from "../../classes/musicCommand.js";
class ShuffleCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (this.connection.host !== this.author.id) return "Only the current voice session host can shuffle the music!";
const object = this.connection;
object.shuffle = !object.shuffle;
players.set(this.channel.guild.id, object);
players.set(this.guildID, object);
this.success = true;
return object.shuffle ? "🔊 The player is now shuffling." : "🔊 The player is no longer shuffling.";
}

View file

@ -4,12 +4,12 @@ import MusicCommand from "../../classes/musicCommand.js";
class SkipCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
const player = this.connection;
if (player.host !== this.author.id && !this.member.permissions.has("manageChannels")) {
const votes = skipVotes.get(this.channel.guild.id) ?? { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) };
const votes = skipVotes.get(this.guild.id) ?? { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) };
if (votes.ids.includes(this.author.id)) return "You've already voted to skip!";
const newObject = {
count: votes.count + 1,
@ -17,12 +17,12 @@ class SkipCommand extends MusicCommand {
max: votes.max
};
if (votes.count + 1 === votes.max) {
await player.player.stopTrack(this.channel.guild.id);
skipVotes.set(this.channel.guild.id, { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) });
await player.player.stopTrack(this.guild.id);
skipVotes.set(this.guild.id, { count: 0, ids: [], max: Math.min(3, player.voiceChannel.voiceMembers.filter((i) => i.id !== this.client.user.id && !i.bot).length) });
this.success = true;
if (this.type === "application") return "🔊 The current song has been skipped.";
} else {
skipVotes.set(this.channel.guild.id, newObject);
skipVotes.set(this.guild.id, newObject);
this.success = true;
return `🔊 Voted to skip song (${votes.count + 1}/${votes.max} people have voted).`;
}

View file

@ -4,19 +4,19 @@ import MusicCommand from "../../classes/musicCommand.js";
class StopCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.connection) {
await manager.getNode().leaveChannel(this.channel.guild.id);
await manager.getNode().leaveChannel(this.guild.id);
this.success = true;
return "🔊 The current voice channel session has ended.";
}
if (this.connection.host !== this.author.id && !this.member.permissions.has("manageChannels")) return "Only the current voice session host can stop the music!";
const connection = this.connection.player;
connection.node.leaveChannel(this.channel.guild.id);
players.delete(this.channel.guild.id);
queues.delete(this.channel.guild.id);
connection.node.leaveChannel(this.guild.id);
players.delete(this.guild.id);
queues.delete(this.guild.id);
this.success = true;
return `🔊 The voice channel session in \`${this.connection.voiceChannel.name}\` has ended.`;
}

View file

@ -3,9 +3,9 @@ import MusicCommand from "../../classes/musicCommand.js";
class ToggleCommand extends MusicCommand {
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
if (!this.member.voiceState.channelID) return "You need to be in a voice channel first!";
if (!this.channel.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (!this.guild.members.get(this.client.user.id).voiceState.channelID) return "I'm not in a voice channel!";
if (this.connection.host !== this.author.id && !this.member.permissions.has("manageChannels")) return "Only the current voice session host can pause/resume the music!";
const player = this.connection.player;
player.setPaused(!player.paused ? true : false);

View file

@ -8,7 +8,7 @@ class TagsCommand extends Command {
// todo: attempt to not make this file the worst thing that human eyes have ever seen
async run() {
this.success = false;
if (!this.channel.guild) return "This command only works in servers!";
if (!this.guild) return "This command only works in servers!";
const cmd = this.type === "classic" ? (this.args[0] ?? "").toLowerCase() : this.optionsArray[0].name;
if (!cmd || !cmd.trim()) return "You need to provide the name of the tag you want to view!";
const tagName = this.type === "classic" ? this.args.slice(1)[0] : (this.optionsArray[0].options[0] ?? {}).value;
@ -16,33 +16,33 @@ class TagsCommand extends Command {
if (cmd === "create" || cmd === "add") {
if (!tagName || !tagName.trim()) return "You need to provide the name of the tag you want to add!";
if (blacklist.includes(tagName)) return "You can't make a tag with that name!";
const getResult = await database.getTag(this.channel.guild.id, tagName);
const getResult = await database.getTag(this.guild.id, tagName);
if (getResult) return "This tag already exists!";
const result = await database.setTag(tagName, { content: this.type === "classic" ? this.args.slice(2).join(" ") : this.optionsArray[0].options[1].value, author: this.member.id }, this.channel.guild);
const result = await database.setTag(tagName, { content: this.type === "classic" ? this.args.slice(2).join(" ") : this.optionsArray[0].options[1].value, author: this.member.id }, this.guild);
this.success = true;
if (result) return result;
return `The tag \`${tagName}\` has been added!`;
} else if (cmd === "delete" || cmd === "remove") {
if (!tagName || !tagName.trim()) return "You need to provide the name of the tag you want to delete!";
const getResult = await database.getTag(this.channel.guild.id, tagName);
const getResult = await database.getTag(this.guild.id, tagName);
if (!getResult) return "This tag doesn't exist!";
const owners = process.env.OWNER.split(",");
if (getResult.author !== this.author.id && !this.member.permissions.has("manageMessages") && !owners.includes(this.author.id)) return "You don't own this tag!";
await database.removeTag(tagName, this.channel.guild);
await database.removeTag(tagName, this.guild);
this.success = true;
return `The tag \`${tagName}\` has been deleted!`;
} else if (cmd === "edit") {
if (!tagName || !tagName.trim()) return "You need to provide the name of the tag you want to edit!";
const getResult = await database.getTag(this.channel.guild.id, tagName);
const getResult = await database.getTag(this.guild.id, tagName);
if (!getResult) return "This tag doesn't exist!";
const owners = process.env.OWNER.split(",");
if (getResult.author !== this.author.id && !this.member.permissions.has("manageMessages") && !owners.includes(this.author.id)) return "You don't own this tag!";
await database.editTag(tagName, { content: this.type === "classic" ? this.args.slice(2).join(" ") : this.optionsArray[0].options[1].value, author: this.member.id }, this.channel.guild);
await database.editTag(tagName, { content: this.type === "classic" ? this.args.slice(2).join(" ") : this.optionsArray[0].options[1].value, author: this.member.id }, this.guild);
this.success = true;
return `The tag \`${tagName}\` has been edited!`;
} else if (cmd === "own" || cmd === "owner") {
if (!tagName || !tagName.trim()) return "You need to provide the name of the tag you want to check the owner of!";
const getResult = await database.getTag(this.channel.guild.id, tagName);
const getResult = await database.getTag(this.guild.id, tagName);
if (!getResult) return "This tag doesn't exist!";
const user = this.client.users.get(getResult.author);
this.success = true;
@ -58,7 +58,7 @@ class TagsCommand extends Command {
}
} else if (cmd === "list") {
if (!this.channel.permissionsOf(this.client.user.id).has("embedLinks")) return "I don't have the `Embed Links` permission!";
const tagList = await database.getTags(this.channel.guild.id);
const tagList = await database.getTags(this.guild.id);
const embeds = [];
const groups = Object.keys(tagList).map((item, index) => {
return index % 15 === 0 ? Object.keys(tagList).slice(index, index + 15) : null;
@ -76,7 +76,7 @@ class TagsCommand extends Command {
description: value.join("\n"),
author: {
name: this.author.username,
icon_url: this.author.avatarURL
iconURL: this.author.avatarURL
}
}]
});
@ -87,10 +87,10 @@ class TagsCommand extends Command {
} else {
let getResult;
if (cmd === "random") {
const tagList = await database.getTags(this.channel.guild.id);
const tagList = await database.getTags(this.guild.id);
getResult = tagList[random(Object.keys(tagList))];
} else {
getResult = await database.getTag(this.channel.guild.id, this.type === "classic" ? cmd : tagName);
getResult = await database.getTag(this.guild.id, this.type === "classic" ? cmd : tagName);
}
if (!getResult) return "This tag doesn't exist!";
this.success = true;

View file

@ -44,24 +44,25 @@ The default command name is the same as the filename that you save it as, exclud
The parameters available to your command consist of the following:
- `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.client`: An instance of an Oceanic [`Client`](https://docs.oceanic.ws/dev/classes/Client.Client.html), useful for getting info or performing lower-level communication with the Discord API.
- `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/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.
- `this.channel`: An Oceanic [`TextChannel`](https://docs.oceanic.ws/dev/classes/TextChannel.TextChannel.html) 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.guild`: An Oceanic [`Guild`](https://docs.oceanic.ws/dev/classes/Guild.Guild.html) object of the guild that the command was run in. This is undefined in DMs.
- `this.author`: An Oceanic [`User`](https://docs.oceanic.ws/dev/classes/User.User.html) object of the user who ran the command, or a [`Member`](https://docs.oceanic.ws/dev/classes/Member.Member.html) object identical to `this.member` if run in a server as a slash command.
- `this.member`: An Oceanic [`Member`](https://docs.oceanic.ws/dev/classes/Member.Member.html) 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://docs.oceanic.ws/dev/classes/User.User.html) 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.message`: An Oceanic [`Message`](https://docs.oceanic.ws/dev/classes/Message.Message.html) 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.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 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.interaction`: An Oceanic [`CommandInteraction`](https://docs.oceanic.ws/dev/classes/CommandInteraction.CommandInteraction.html) 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`.

View file

@ -25,7 +25,7 @@ export default async (client, interaction) => {
// eslint-disable-next-line no-unused-vars
const commandClass = new cmd(client, { type: "application", interaction });
const result = await commandClass.run();
const replyMethod = interaction.acknowledged ? "editOriginalMessage" : "createMessage";
const replyMethod = interaction.acknowledged ? "editOriginal" : "createMessage";
if (typeof result === "string") {
await interaction[replyMethod]({
content: result,
@ -35,9 +35,9 @@ export default async (client, interaction) => {
await interaction[replyMethod](Object.assign(result, {
flags: result.flags ?? (commandClass.success ? 0 : 64)
}));
} else if (typeof result === "object" && result.file) {
} else if (typeof result === "object" && result.contents) {
const fileSize = 8388119;
if (result.file.length > fileSize) {
if (result.contents.length > fileSize) {
if (process.env.TEMPDIR && process.env.TEMPDIR !== "") {
await upload(client, result, interaction, true);
} else {
@ -47,11 +47,11 @@ export default async (client, interaction) => {
});
}
} else {
await interaction[replyMethod](result.text ? result.text : {}, result);
await interaction[replyMethod](result.text ? result.text : { files: [result] });
}
}
} catch (error) {
const replyMethod = interaction.acknowledged ? "editOriginalMessage" : "createMessage";
const replyMethod = interaction.acknowledged ? "editOriginal" : "createMessage";
if (error.toString().includes("Request entity too large")) {
await interaction[replyMethod]({ content: "The resulting file was too large to upload. Try again with a smaller image if possible.", flags: 64 });
} else if (error.toString().includes("Job ended prematurely")) {
@ -63,11 +63,16 @@ export default async (client, interaction) => {
try {
let err = error;
if (error?.constructor?.name == "Promise") err = await error;
await interaction[replyMethod]("Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: <https://github.com/esmBot/esmBot/issues>", {
file: `Message: ${clean(err)}\n\nStack Trace: ${clean(err.stack)}`,
await interaction[replyMethod]({
content: "Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: <https://github.com/esmBot/esmBot/issues>",
files: [{
contents: `Message: ${clean(err)}\n\nStack Trace: ${clean(err.stack)}`,
name: "error.txt"
}]
});
} catch { /* silently ignore */ }
} catch (e) {
logger.error(`While attempting to send the previous error message, another error occurred: ${e.stack || e}`);
}
}
}
};

View file

@ -2,7 +2,7 @@ import database from "../utils/database.js";
import { log, error as _error } from "../utils/logger.js";
import { prefixCache, aliases, disabledCache, disabledCmdCache, commands } from "../utils/collections.js";
import parseCommand from "../utils/parseCommand.js";
import { clean } from "../utils/misc.js";
import { clean, cleanMessage } from "../utils/misc.js";
import { upload } from "../utils/tempimages.js";
// run when someone sends a message
@ -11,28 +11,28 @@ export default async (client, message) => {
if (message.author.bot) return;
// don't run command if bot can't send messages
if (message.channel.guild && !message.channel.permissionsOf(client.user.id).has("sendMessages")) return;
if (message.guildID && !message.channel.permissionsOf(client.user.id.toString()).has("SEND_MESSAGES")) return;
let prefixCandidate;
let guildDB;
if (message.channel.guild) {
const cachedPrefix = prefixCache.get(message.channel.guild.id);
if (message.guildID) {
const cachedPrefix = prefixCache.get(message.guildID);
if (cachedPrefix) {
prefixCandidate = cachedPrefix;
} else {
guildDB = await database.getGuild(message.channel.guild.id);
guildDB = await database.getGuild(message.guildID);
if (!guildDB) {
guildDB = await database.fixGuild(message.channel.guild);
guildDB = await database.fixGuild(message.guildID);
}
prefixCandidate = guildDB.prefix;
prefixCache.set(message.channel.guild.id, guildDB.prefix);
prefixCache.set(message.guildID, guildDB.prefix);
}
}
let prefix;
let isMention = false;
if (message.channel.guild) {
const user = message.channel.guild.members.get(client.user.id);
if (message.guildID) {
const user = message.guild.members.get(client.user.id);
if (message.content.startsWith(user.mention)) {
prefix = `${user.mention} `;
isMention = true;
@ -50,8 +50,8 @@ export default async (client, message) => {
if (!message.content.startsWith(prefix)) return;
// separate commands and args
const replace = isMention ? `@${(message.channel.guild ? message.channel.guild.members.get(client.user.id).nick : client.user.username) ?? client.user.username} ` : prefix;
const content = message.cleanContent.substring(replace.length).trim();
const replace = isMention ? `@${(message.guild ? message.guild.members.get(client.user.id).nick : client.user.username) ?? client.user.username} ` : prefix;
const content = cleanMessage(message).substring(replace.length).trim();
const rawContent = message.content.substring(prefix.length).trim();
const preArgs = content.split(/\s+/g);
preArgs.shift();
@ -60,22 +60,22 @@ export default async (client, message) => {
const aliased = aliases.get(command);
// don't run if message is in a disabled channel
if (message.channel.guild) {
const disabled = disabledCache.get(message.channel.guild.id);
if (message.guildID) {
const disabled = disabledCache.get(message.guildID);
if (disabled) {
if (disabled.includes(message.channel.id) && command != "channel") return;
if (disabled.includes(message.channelID) && command != "channel") return;
} else {
guildDB = await database.getGuild(message.channel.guild.id);
disabledCache.set(message.channel.guild.id, guildDB.disabled);
if (guildDB.disabled.includes(message.channel.id) && command !== "channel") return;
guildDB = await database.getGuild(message.guildID);
disabledCache.set(message.guildID, guildDB.disabled);
if (guildDB.disabled.includes(message.channelID) && command !== "channel") return;
}
const disabledCmds = disabledCmdCache.get(message.channel.guild.id);
const disabledCmds = disabledCmdCache.get(message.guildID);
if (disabledCmds) {
if (disabledCmds.includes(aliased ?? command)) return;
} else {
guildDB = await database.getGuild(message.channel.guild.id);
disabledCmdCache.set(message.channel.guild.id, guildDB.disabled_commands ?? guildDB.disabledCommands);
guildDB = await database.getGuild(message.guildID);
disabledCmdCache.set(message.guildID, guildDB.disabled_commands ?? guildDB.disabledCommands);
if ((guildDB.disabled_commands ?? guildDB.disabledCommands).includes(aliased ?? command)) return;
}
}
@ -85,15 +85,15 @@ export default async (client, message) => {
if (!cmd) return;
// block certain commands from running in DMs
if (!cmd.directAllowed && !message.channel.guild) return;
if (!cmd.directAllowed && !message.guildID) return;
// actually run the command
log("log", `${message.author.username} (${message.author.id}) ran classic command ${command}`);
const reference = {
messageReference: {
channelID: message.channel.id,
channelID: message.channelID,
messageID: message.id,
guildID: message.channel.guild ? message.channel.guild.id : undefined,
guildID: message.guildID ?? undefined,
failIfNotExists: false
},
allowedMentions: {
@ -110,15 +110,15 @@ export default async (client, message) => {
if ((endTime - startTime) >= 180000) reference.allowedMentions.repliedUser = true;
if (typeof result === "string") {
reference.allowedMentions.repliedUser = true;
await client.createMessage(message.channel.id, Object.assign({
await client.rest.channels.createMessage(message.channelID, Object.assign({
content: result
}, reference));
} else if (typeof result === "object" && result.embeds) {
await client.createMessage(message.channel.id, Object.assign(result, reference));
} else if (typeof result === "object" && result.file) {
await client.rest.channels.createMessage(message.channelID, Object.assign(result, reference));
} else if (typeof result === "object" && result.contents) {
let fileSize = 8388119;
if (message.channel.guild) {
switch (message.channel.guild.premiumTier) {
if (message.guildID) {
switch (message.guild.premiumTier) {
case 2:
fileSize = 52428308;
break;
@ -127,42 +127,44 @@ export default async (client, message) => {
break;
}
}
if (result.file.length > fileSize) {
if (result.contents.length > fileSize) {
if (process.env.TEMPDIR && process.env.TEMPDIR !== "") {
await upload(client, result, message);
} else {
await client.createMessage(message.channel.id, "The resulting image was more than 8MB in size, so I can't upload it.");
await client.rest.channels.createMessage(message.channelID, "The resulting image was more than 8MB in size, so I can't upload it.");
}
} else {
await client.createMessage(message.channel.id, Object.assign({
content: result.text ? result.text : undefined
}, reference), result);
await client.rest.channels.createMessage(message.channelID, Object.assign({
content: result.text ? result.text : undefined,
files: [result]
}, reference));
}
}
} catch (error) {
if (error.toString().includes("Request entity too large")) {
await client.createMessage(message.channel.id, Object.assign({
await client.rest.channels.createMessage(message.channelID, Object.assign({
content: "The resulting file was too large to upload. Try again with a smaller image if possible."
}, reference));
} else if (error.toString().includes("Job ended prematurely")) {
await client.createMessage(message.channel.id, Object.assign({
await client.rest.channels.createMessage(message.channelID, Object.assign({
content: "Something happened to the image servers before I could receive the image. Try running your command again."
}, reference));
} else if (error.toString().includes("Timed out")) {
await client.createMessage(message.channel.id, Object.assign({
await client.rest.channels.createMessage(message.channelID, Object.assign({
content: "The request timed out before I could download that image. Try uploading your image somewhere else or reducing its size."
}, reference));
} else {
_error(`Error occurred with command message ${message.cleanContent}: ${error.stack || error}`);
_error(`Error occurred with command message ${message.content}: ${error.stack || error}`);
try {
let err = error;
if (error?.constructor?.name == "Promise") err = await error;
await client.createMessage(message.channel.id, Object.assign({
content: "Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: <https://github.com/esmBot/esmBot/issues>"
}, reference), [{
file: `Message: ${clean(err)}\n\nStack Trace: ${clean(err.stack)}`,
await client.rest.channels.createMessage(message.channelID, Object.assign({
content: "Uh oh! I ran into an error while running this command. Please report the content of the attached file at the following link or on the esmBot Support server: <https://github.com/esmBot/esmBot/issues>",
files: [{
contents: `Message: ${clean(err)}\n\nStack Trace: ${clean(err.stack)}`,
name: "error.txt"
}]);
}]
}, reference));
} catch (e) {
_error(`While attempting to send the previous error message, another error occurred: ${e.stack || e}`);
}

View file

@ -6,21 +6,25 @@ const isWaiting = new Map();
export default async (client, member, oldChannel) => {
if (!oldChannel) return;
const connection = players.get(oldChannel.guild.id);
const connection = players.get(oldChannel.guildID);
if (oldChannel.id === connection?.voiceChannel.id) {
if (oldChannel.voiceMembers.filter((i) => i.id !== client.user.id && !i.bot).length === 0) {
if (isWaiting.has(oldChannel.id)) return;
isWaiting.set(oldChannel.id, true);
connection.player.setPaused(true);
const waitMessage = await client.createMessage(connection.originalChannel.id, "🔊 Waiting 10 seconds for someone to return...");
const waitMessage = await client.rest.channels.createMessage(connection.originalChannel.id, {
content: "🔊 Waiting 10 seconds for someone to return..."
});
const awaitRejoin = new AwaitRejoin(oldChannel, true, member.id);
awaitRejoin.on("end", async (rejoined, newMember) => {
isWaiting.delete(oldChannel.id);
if (rejoined) {
connection.player.setPaused(false);
if (member.id !== newMember.id) {
players.set(connection.voiceChannel.guild.id, { player: connection.player, type: connection.type, host: newMember.id, voiceChannel: connection.voiceChannel, originalChannel: connection.originalChannel, loop: connection.loop, shuffle: connection.shuffle, playMessage: connection.playMessage });
waitMessage.edit(`🔊 ${newMember.mention} is the new voice channel host.`);
players.set(connection.voiceChannel.guildID, { player: connection.player, type: connection.type, host: newMember.id, voiceChannel: connection.voiceChannel, originalChannel: connection.originalChannel, loop: connection.loop, shuffle: connection.shuffle, playMessage: connection.playMessage });
waitMessage.edit({
content: `🔊 ${newMember.mention} is the new voice channel host.`
});
} else {
try {
await waitMessage.delete();
@ -35,20 +39,24 @@ export default async (client, member, oldChannel) => {
// no-op
}
try {
connection.player.node.leaveChannel(connection.originalChannel.guild.id);
connection.player.node.leaveChannel(connection.originalChannel.guildID);
} catch {
// no-op
}
players.delete(connection.originalChannel.guild.id);
queues.delete(connection.originalChannel.guild.id);
skipVotes.delete(connection.originalChannel.guild.id);
client.createMessage(connection.originalChannel.id, `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`);
players.delete(connection.originalChannel.guildID);
queues.delete(connection.originalChannel.guildID);
skipVotes.delete(connection.originalChannel.guildID);
client.rest.channels.createMessage(connection.originalChannel.id, {
content: `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`
});
}
});
} else if (member.id === connection.host) {
if (isWaiting.has(oldChannel.id)) return;
isWaiting.set(oldChannel.id, true);
const waitMessage = await client.createMessage(connection.originalChannel.id, "🔊 Waiting 10 seconds for the host to return...");
const waitMessage = await client.rest.channels.createMessage(connection.originalChannel.id, {
content: "🔊 Waiting 10 seconds for the host to return..."
});
const awaitRejoin = new AwaitRejoin(oldChannel, false, member.id);
awaitRejoin.on("end", async (rejoined) => {
isWaiting.delete(oldChannel.id);
@ -67,32 +75,38 @@ export default async (client, member, oldChannel) => {
// no-op
}
try {
connection.player.node.leaveChannel(connection.originalChannel.guild.id);
connection.player.node.leaveChannel(connection.originalChannel.guildID);
} catch {
// no-op
}
players.delete(connection.originalChannel.guild.id);
queues.delete(connection.originalChannel.guild.id);
skipVotes.delete(connection.originalChannel.guild.id);
client.createMessage(connection.originalChannel.id, `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`);
players.delete(connection.originalChannel.guildID);
queues.delete(connection.originalChannel.guildID);
skipVotes.delete(connection.originalChannel.guildID);
client.rest.channels.createMessage(connection.originalChannel.id, {
content: `🔊 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 });
waitMessage.edit(`🔊 ${randomMember.mention} is the new voice channel host.`);
players.set(connection.voiceChannel.guildID, { player: connection.player, type: connection.type, host: randomMember.id, voiceChannel: connection.voiceChannel, originalChannel: connection.originalChannel, loop: connection.loop, shuffle: connection.shuffle, playMessage: connection.playMessage });
waitMessage.edit({
content: `🔊 ${randomMember.mention} is the new voice channel host.`
});
}
}
});
} else if (member.id === client.user.id) {
isWaiting.delete(oldChannel.id);
try {
connection.player.node.leaveChannel(connection.originalChannel.guild.id);
connection.player.node.leaveChannel(connection.originalChannel.guildID);
} catch {
// no-op
}
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 voice channel session in \`${connection.originalChannel.name}\` has ended.`);
players.delete(connection.originalChannel.guildID);
queues.delete(connection.originalChannel.guildID);
skipVotes.delete(connection.originalChannel.guildID);
await client.rest.channels.createMessage(connection.originalChannel.id, {
content: `🔊 The voice channel session in \`${connection.originalChannel.name}\` has ended.`
});
}
}
};

View file

@ -31,12 +31,12 @@
"cmake-js": "^6.3.2",
"dotenv": "^16.0.2",
"emoji-regex": "^10.1.0",
"eris": "github:esmBot/eris#dev",
"file-type": "^17.1.6",
"format-duration": "^2.0.0",
"jsqr": "^1.4.0",
"node-addon-api": "^5.0.0",
"node-emoji": "^1.11.0",
"oceanic.js": "1.1.1-dev.51a0d21",
"qrcode": "^1.5.1",
"sharp": "^0.30.7",
"shoukaku": "github:Deivu/shoukaku",
@ -45,9 +45,9 @@
"winston-daily-rotate-file": "^4.7.1"
},
"devDependencies": {
"@babel/core": "^7.19.0",
"@babel/eslint-parser": "^7.18.9",
"@babel/eslint-plugin": "^7.18.10",
"@babel/core": "^7.19.1",
"@babel/eslint-parser": "^7.19.1",
"@babel/eslint-plugin": "^7.19.1",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"eslint": "^8.23.1",
"eslint-plugin-unicorn": "^42.0.0"
@ -59,7 +59,7 @@
"pm2": "^5.2.0",
"postgres": "^3.2.4",
"uuid": "^8.3.2",
"ws": "^8.8.1",
"ws": "^8.9.0",
"zlib-sync": "^0.1.7"
}
}

View file

@ -1,9 +1,9 @@
lockfileVersion: 5.4
specifiers:
'@babel/core': ^7.19.0
'@babel/eslint-parser': ^7.18.9
'@babel/eslint-plugin': ^7.18.10
'@babel/core': ^7.19.1
'@babel/eslint-parser': ^7.19.1
'@babel/eslint-plugin': ^7.19.1
'@babel/plugin-proposal-class-properties': ^7.18.6
'@top-gg/sdk': ^3.1.3
better-sqlite3: ^7.6.2
@ -11,7 +11,6 @@ specifiers:
cmake-js: ^6.3.2
dotenv: ^16.0.2
emoji-regex: ^10.1.0
eris: github:esmBot/eris#dev
erlpack: github:abalabahaha/erlpack
eslint: ^8.23.1
eslint-plugin-unicorn: ^42.0.0
@ -20,6 +19,7 @@ specifiers:
jsqr: ^1.4.0
node-addon-api: ^5.0.0
node-emoji: ^1.11.0
oceanic.js: 1.1.1-dev.51a0d21
pm2: ^5.2.0
postgres: ^3.2.4
qrcode: ^1.5.1
@ -29,7 +29,7 @@ specifiers:
uuid: ^8.3.2
winston: ^3.8.2
winston-daily-rotate-file: ^4.7.1
ws: ^8.8.1
ws: ^8.9.0
zlib-sync: ^0.1.7
dependencies:
@ -37,12 +37,12 @@ dependencies:
cmake-js: 6.3.2
dotenv: 16.0.2
emoji-regex: 10.1.0
eris: github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0_bufferutil@4.0.6
file-type: 17.1.6
format-duration: 2.0.0
jsqr: 1.4.0
node-addon-api: 5.0.0
node-emoji: 1.11.0
oceanic.js: 1.1.1-dev.51a0d21_bufferutil@4.0.6
qrcode: 1.5.1
sharp: 0.30.7
shoukaku: github.com/Deivu/shoukaku/7822080092a13ea4cc71ab7d9f891f5cb933683b_bufferutil@4.0.6
@ -57,14 +57,14 @@ optionalDependencies:
pm2: 5.2.0_bufferutil@4.0.6
postgres: 3.2.4
uuid: 8.3.2
ws: 8.8.1_bufferutil@4.0.6
ws: 8.9.0_bufferutil@4.0.6
zlib-sync: 0.1.7
devDependencies:
'@babel/core': 7.19.0
'@babel/eslint-parser': 7.18.9_eia2ig3m3pjccex4igkch6uffy
'@babel/eslint-plugin': 7.18.10_lp3qrsx55lt6xwfnux3kkcfwiu
'@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.19.0
'@babel/core': 7.19.1
'@babel/eslint-parser': 7.19.1_zdglor7vg7osicr5spasq6cc5a
'@babel/eslint-plugin': 7.19.1_64ks3oho3y6ldxyvuvb3gc4oge
'@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.19.1
eslint: 8.23.1
eslint-plugin-unicorn: 42.0.0_eslint@8.23.1
@ -85,24 +85,24 @@ packages:
'@babel/highlight': 7.18.6
dev: true
/@babel/compat-data/7.19.0:
resolution: {integrity: sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==}
/@babel/compat-data/7.19.1:
resolution: {integrity: sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/core/7.19.0:
resolution: {integrity: sha512-reM4+U7B9ss148rh2n1Qs9ASS+w94irYXga7c2jaQv9RVzpS7Mv1a9rnYYwuDa45G+DkORt9g6An2k/V4d9LbQ==}
/@babel/core/7.19.1:
resolution: {integrity: sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==}
engines: {node: '>=6.9.0'}
dependencies:
'@ampproject/remapping': 2.2.0
'@babel/code-frame': 7.18.6
'@babel/generator': 7.19.0
'@babel/helper-compilation-targets': 7.19.0_@babel+core@7.19.0
'@babel/helper-compilation-targets': 7.19.1_@babel+core@7.19.1
'@babel/helper-module-transforms': 7.19.0
'@babel/helpers': 7.19.0
'@babel/parser': 7.19.0
'@babel/parser': 7.19.1
'@babel/template': 7.18.10
'@babel/traverse': 7.19.0
'@babel/traverse': 7.19.1
'@babel/types': 7.19.0
convert-source-map: 1.8.0
debug: 4.3.4
@ -113,28 +113,28 @@ packages:
- supports-color
dev: true
/@babel/eslint-parser/7.18.9_eia2ig3m3pjccex4igkch6uffy:
resolution: {integrity: sha512-KzSGpMBggz4fKbRbWLNyPVTuQr6cmCcBhOyXTw/fieOVaw5oYAwcAj4a7UKcDYCPxQq+CG1NCDZH9e2JTXquiQ==}
/@babel/eslint-parser/7.19.1_zdglor7vg7osicr5spasq6cc5a:
resolution: {integrity: sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
'@babel/core': '>=7.11.0'
eslint: ^7.5.0 || ^8.0.0
dependencies:
'@babel/core': 7.19.0
'@babel/core': 7.19.1
'@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
eslint: 8.23.1
eslint-scope: 5.1.1
eslint-visitor-keys: 2.1.0
semver: 6.3.0
dev: true
/@babel/eslint-plugin/7.18.10_lp3qrsx55lt6xwfnux3kkcfwiu:
resolution: {integrity: sha512-iV1OZj/7eg4wZIcsVEkXS3MUWdhmpLsu2h+9Zr2ppywKWdCRs6VfjxbRzmHHYeurTizrrnaJ9ZkbO8KOv4lauQ==}
/@babel/eslint-plugin/7.19.1_64ks3oho3y6ldxyvuvb3gc4oge:
resolution: {integrity: sha512-ElGPkQPapKMa3zVqXHkZYzuL7I5LbRw9UWBUArgWsdWDDb9XcACqOpBib5tRPA9XvbVZYrFUkoQPbiJ4BFvu4w==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
'@babel/eslint-parser': '>=7.11.0'
eslint: '>=7.5.0'
dependencies:
'@babel/eslint-parser': 7.18.9_eia2ig3m3pjccex4igkch6uffy
'@babel/eslint-parser': 7.19.1_zdglor7vg7osicr5spasq6cc5a
eslint: 8.23.1
eslint-rule-composer: 0.3.0
dev: true
@ -155,32 +155,32 @@ packages:
'@babel/types': 7.19.0
dev: true
/@babel/helper-compilation-targets/7.19.0_@babel+core@7.19.0:
resolution: {integrity: sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==}
/@babel/helper-compilation-targets/7.19.1_@babel+core@7.19.1:
resolution: {integrity: sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/compat-data': 7.19.0
'@babel/core': 7.19.0
'@babel/compat-data': 7.19.1
'@babel/core': 7.19.1
'@babel/helper-validator-option': 7.18.6
browserslist: 4.21.3
browserslist: 4.21.4
semver: 6.3.0
dev: true
/@babel/helper-create-class-features-plugin/7.19.0_@babel+core@7.19.0:
/@babel/helper-create-class-features-plugin/7.19.0_@babel+core@7.19.1:
resolution: {integrity: sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
dependencies:
'@babel/core': 7.19.0
'@babel/core': 7.19.1
'@babel/helper-annotate-as-pure': 7.18.6
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-function-name': 7.19.0
'@babel/helper-member-expression-to-functions': 7.18.9
'@babel/helper-optimise-call-expression': 7.18.6
'@babel/helper-replace-supers': 7.18.9
'@babel/helper-replace-supers': 7.19.1
'@babel/helper-split-export-declaration': 7.18.6
transitivePeerDependencies:
- supports-color
@ -228,9 +228,9 @@ packages:
'@babel/helper-module-imports': 7.18.6
'@babel/helper-simple-access': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/helper-validator-identifier': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
'@babel/template': 7.18.10
'@babel/traverse': 7.19.0
'@babel/traverse': 7.19.1
'@babel/types': 7.19.0
transitivePeerDependencies:
- supports-color
@ -248,14 +248,14 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-replace-supers/7.18.9:
resolution: {integrity: sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==}
/@babel/helper-replace-supers/7.19.1:
resolution: {integrity: sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-environment-visitor': 7.18.9
'@babel/helper-member-expression-to-functions': 7.18.9
'@babel/helper-optimise-call-expression': 7.18.6
'@babel/traverse': 7.19.0
'@babel/traverse': 7.19.1
'@babel/types': 7.19.0
transitivePeerDependencies:
- supports-color
@ -280,8 +280,8 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-identifier/7.18.6:
resolution: {integrity: sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==}
/@babel/helper-validator-identifier/7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
dev: true
@ -295,7 +295,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.18.10
'@babel/traverse': 7.19.0
'@babel/traverse': 7.19.1
'@babel/types': 7.19.0
transitivePeerDependencies:
- supports-color
@ -305,27 +305,27 @@ packages:
resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-validator-identifier': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
chalk: 2.4.2
js-tokens: 4.0.0
dev: true
/@babel/parser/7.19.0:
resolution: {integrity: sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==}
/@babel/parser/7.19.1:
resolution: {integrity: sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.19.0
dev: true
/@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.19.0:
/@babel/plugin-proposal-class-properties/7.18.6_@babel+core@7.19.1:
resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
'@babel/core': 7.19.0
'@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.19.0
'@babel/core': 7.19.1
'@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.19.1
'@babel/helper-plugin-utils': 7.19.0
transitivePeerDependencies:
- supports-color
@ -336,12 +336,12 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
'@babel/parser': 7.19.0
'@babel/parser': 7.19.1
'@babel/types': 7.19.0
dev: true
/@babel/traverse/7.19.0:
resolution: {integrity: sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==}
/@babel/traverse/7.19.1:
resolution: {integrity: sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.18.6
@ -350,7 +350,7 @@ packages:
'@babel/helper-function-name': 7.19.0
'@babel/helper-hoist-variables': 7.18.6
'@babel/helper-split-export-declaration': 7.18.6
'@babel/parser': 7.19.0
'@babel/parser': 7.19.1
'@babel/types': 7.19.0
debug: 4.3.4
globals: 11.12.0
@ -363,7 +363,7 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.18.10
'@babel/helper-validator-identifier': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
dev: true
@ -380,6 +380,26 @@ packages:
kuler: 2.0.0
dev: false
/@discordjs/voice/0.11.0_bufferutil@4.0.6:
resolution: {integrity: sha512-6+9cj1dxzBJm7WJ9qyG2XZZQ8rcLl6x2caW0C0OxuTtMLAaEDntpb6lqMTFiBg/rDc4Rd59g1w0gJmib33CuHw==}
engines: {node: '>=16.9.0'}
requiresBuild: true
dependencies:
'@types/ws': 8.5.3
discord-api-types: 0.36.3
prism-media: 1.3.4
tslib: 2.4.0
ws: 8.9.0_bufferutil@4.0.6
transitivePeerDependencies:
- '@discordjs/opus'
- bufferutil
- ffmpeg-static
- node-opus
- opusscript
- utf-8-validate
dev: false
optional: true
/@eslint/eslintrc/1.3.2:
resolution: {integrity: sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -397,8 +417,8 @@ packages:
- supports-color
dev: true
/@humanwhocodes/config-array/0.10.4:
resolution: {integrity: sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==}
/@humanwhocodes/config-array/0.10.5:
resolution: {integrity: sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==}
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 1.2.1
@ -459,6 +479,12 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@nicolo-ribaudo/eslint-scope-5-internals/5.1.1-v1:
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
dependencies:
eslint-scope: 5.1.1
dev: true
/@nodelib/fs.scandir/2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -563,7 +589,7 @@ packages:
axios: 0.21.4_debug@4.3.4
debug: 4.3.4
eventemitter2: 6.4.9
ws: 7.4.6_bufferutil@4.0.6
ws: 7.5.9_bufferutil@4.0.6
transitivePeerDependencies:
- bufferutil
- supports-color
@ -599,10 +625,22 @@ packages:
- encoding
dev: false
/@types/node/18.7.19:
resolution: {integrity: sha512-Sq1itGUKUX1ap7GgZlrzdBydjbsJL/NSQt/4wkAxUJ7/OS5c2WkoN6WSpWc2Yc5wtKMZOUA0VCs/j2XJadN3HA==}
dev: false
optional: true
/@types/normalize-package-data/2.4.1:
resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
dev: true
/@types/ws/8.5.3:
resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==}
dependencies:
'@types/node': 18.7.19
dev: false
optional: true
/acorn-jsx/5.3.2_acorn@8.8.0:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@ -748,7 +786,7 @@ packages:
/axios/0.21.4_debug@4.3.4:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies:
follow-redirects: 1.15.1_debug@4.3.4
follow-redirects: 1.15.2_debug@4.3.4
transitivePeerDependencies:
- debug
dev: false
@ -834,15 +872,15 @@ packages:
dependencies:
fill-range: 7.0.1
/browserslist/4.21.3:
resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==}
/browserslist/4.21.4:
resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
dependencies:
caniuse-lite: 1.0.30001397
electron-to-chromium: 1.4.247
caniuse-lite: 1.0.30001410
electron-to-chromium: 1.4.259
node-releases: 2.0.6
update-browserslist-db: 1.0.8_browserslist@4.21.3
update-browserslist-db: 1.0.9_browserslist@4.21.4
dev: true
/buffer-from/1.1.2:
@ -904,8 +942,8 @@ packages:
engines: {node: '>=6'}
dev: false
/caniuse-lite/1.0.30001397:
resolution: {integrity: sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==}
/caniuse-lite/1.0.30001410:
resolution: {integrity: sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==}
dev: true
/chainsaw/0.1.0:
@ -1211,6 +1249,11 @@ packages:
path-type: 4.0.0
dev: true
/discord-api-types/0.36.3:
resolution: {integrity: sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==}
dev: false
optional: true
/doctrine/3.0.0:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
@ -1226,11 +1269,11 @@ packages:
/duplexer2/0.1.4:
resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
dependencies:
readable-stream: 2.3.7
readable-stream: 2.1.5
dev: false
/electron-to-chromium/1.4.247:
resolution: {integrity: sha512-FLs6R4FQE+1JHM0hh3sfdxnYjKvJpHZyhQDjc2qFq/xFvmmRt/TATNToZhrcGUFzpF2XjeiuozrA8lI0PZmYYw==}
/electron-to-chromium/1.4.259:
resolution: {integrity: sha512-dzbPZG0PKsqiSxfZovjBZn5InX4OoSITSsJ5S/U0QRrFaKsQtHx352zbyqtiDpNpX5lnPCAeBS6D+QpWipqDRw==}
dev: true
/emitter-listener/1.1.2:
@ -1310,7 +1353,7 @@ packages:
peerDependencies:
eslint: '>=8.8.0'
dependencies:
'@babel/helper-validator-identifier': 7.18.6
'@babel/helper-validator-identifier': 7.19.1
ci-info: 3.4.0
clean-regexp: 1.0.0
eslint: 8.23.1
@ -1374,7 +1417,7 @@ packages:
hasBin: true
dependencies:
'@eslint/eslintrc': 1.3.2
'@humanwhocodes/config-array': 0.10.4
'@humanwhocodes/config-array': 0.10.5
'@humanwhocodes/gitignore-to-minimatch': 1.0.2
'@humanwhocodes/module-importer': 1.0.1
ajv: 6.12.6
@ -1591,8 +1634,8 @@ packages:
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
dev: false
/follow-redirects/1.15.1_debug@4.3.4:
resolution: {integrity: sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==}
/follow-redirects/1.15.2_debug@4.3.4:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
@ -2122,7 +2165,7 @@ packages:
'@colors/colors': 1.5.0
fecha: 4.2.3
ms: 2.1.3
safe-stable-stringify: 2.3.1
safe-stable-stringify: 2.4.0
triple-beam: 1.3.0
dev: false
@ -2341,6 +2384,23 @@ packages:
engines: {node: '>= 6'}
dev: false
/oceanic.js/1.1.1-dev.51a0d21_bufferutil@4.0.6:
resolution: {integrity: sha512-Ks/SuYCY9inHSU0iHAWBUwy73den1T4DDs8HbyYMXDE36o3RYjQx5w54u5gZ5pjoEBg7/Ik1hQbJmfC0kH8F9g==}
engines: {node: '>=16.16.0'}
dependencies:
undici: 5.10.0
ws: 8.9.0_bufferutil@4.0.6
optionalDependencies:
'@discordjs/voice': 0.11.0_bufferutil@4.0.6
transitivePeerDependencies:
- '@discordjs/opus'
- bufferutil
- ffmpeg-static
- node-opus
- opusscript
- utf-8-validate
dev: false
/once/1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
@ -2657,6 +2717,25 @@ packages:
engines: {node: '>= 0.8.0'}
dev: true
/prism-media/1.3.4:
resolution: {integrity: sha512-eW7LXORkTCQznZs+eqe9VjGOrLBxcBPXgNyHXMTSRVhphvd/RrxgIR7WaWt4fkLuhshcdT5KHL88LAfcvS3f5g==}
peerDependencies:
'@discordjs/opus': ^0.8.0
ffmpeg-static: ^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0
node-opus: ^0.3.3
opusscript: ^0.0.8
peerDependenciesMeta:
'@discordjs/opus':
optional: true
ffmpeg-static:
optional: true
node-opus:
optional: true
opusscript:
optional: true
dev: false
optional: true
/process-nextick-args/1.0.7:
resolution: {integrity: sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw==}
dev: false
@ -2922,8 +3001,8 @@ packages:
regexp-tree: 0.1.24
dev: true
/safe-stable-stringify/2.3.1:
resolution: {integrity: sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==}
/safe-stable-stringify/2.4.0:
resolution: {integrity: sha512-eehKHKpab6E741ud7ZIMcXhKcP6TSIezPkNZhy5U8xC6+VvrRdUA2tMgxGxaGl4cz7c2Ew5+mg5+wNB16KQqrA==}
engines: {node: '>=10'}
dev: false
@ -3378,13 +3457,13 @@ packages:
setimmediate: 1.0.5
dev: false
/update-browserslist-db/1.0.8_browserslist@4.21.3:
resolution: {integrity: sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA==}
/update-browserslist-db/1.0.9_browserslist@4.21.4:
resolution: {integrity: sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==}
hasBin: true
peerDependencies:
browserslist: '>= 4.21.0'
dependencies:
browserslist: 4.21.3
browserslist: 4.21.4
escalade: 3.1.1
picocolors: 1.0.0
dev: true
@ -3514,7 +3593,7 @@ packages:
logform: 2.4.2
one-time: 1.0.0
readable-stream: 3.6.0
safe-stable-stringify: 2.3.1
safe-stable-stringify: 2.4.0
stack-trace: 0.0.10
triple-beam: 1.3.0
winston-transport: 4.5.0
@ -3560,8 +3639,24 @@ packages:
dev: false
optional: true
/ws/8.8.1_bufferutil@4.0.6:
resolution: {integrity: sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==}
/ws/7.5.9_bufferutil@4.0.6:
resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==}
engines: {node: '>=8.3.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ^5.0.2
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dependencies:
bufferutil: 4.0.6
dev: false
optional: true
/ws/8.9.0_bufferutil@4.0.6:
resolution: {integrity: sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@ -3664,7 +3759,7 @@ packages:
requiresBuild: true
dependencies:
undici: 5.10.0
ws: 8.8.1_bufferutil@4.0.6
ws: 8.9.0_bufferutil@4.0.6
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@ -3680,16 +3775,3 @@ packages:
nan: 2.16.0
dev: false
optional: true
github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0_bufferutil@4.0.6:
resolution: {tarball: https://codeload.github.com/esmBot/eris/tar.gz/fbc637e7f92963d7f9e57c86223e995269e70de0}
id: github.com/esmBot/eris/fbc637e7f92963d7f9e57c86223e995269e70de0
name: eris
version: 0.17.2-dev
engines: {node: '>=10.4.0'}
dependencies:
ws: 8.8.1_bufferutil@4.0.6
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: false

View file

@ -118,16 +118,16 @@ export async function enableCommand(guild, command) {
}
export async function disableChannel(channel) {
const guildDB = await this.getGuild(channel.guild.id);
await sql`UPDATE guilds SET disabled_commands = ${[...guildDB.disabled, channel.id]} WHERE guild_id = ${channel.guild.id}`;
disabledCache.set(channel.guild.id, [...guildDB.disabled, channel.id]);
const guildDB = await this.getGuild(channel.guildID);
await sql`UPDATE guilds SET disabled_commands = ${[...guildDB.disabled, channel.id]} WHERE guild_id = ${channel.guildID}`;
disabledCache.set(channel.guildID, [...guildDB.disabled, channel.id]);
}
export async function enableChannel(channel) {
const guildDB = await this.getGuild(channel.guild.id);
const guildDB = await this.getGuild(channel.guildID);
const newDisabled = guildDB.disabled.filter(item => item !== channel.id);
await sql`UPDATE guilds SET disabled_commands = ${newDisabled} WHERE guild_id = ${channel.guild.id}`;
disabledCache.set(channel.guild.id, newDisabled);
await sql`UPDATE guilds SET disabled_commands = ${newDisabled} WHERE guild_id = ${channel.guildID}`;
disabledCache.set(channel.guildID, newDisabled);
}
export async function getCounts() {

View file

@ -106,16 +106,16 @@ export async function enableCommand(guild, command) {
}
export async function disableChannel(channel) {
const guildDB = await this.getGuild(channel.guild.id);
connection.prepare("UPDATE guilds SET disabled = ? WHERE guild_id = ?").run(JSON.stringify([...JSON.parse(guildDB.disabled), channel.id]), channel.guild.id);
collections.disabledCache.set(channel.guild.id, [...JSON.parse(guildDB.disabled), channel.id]);
const guildDB = await this.getGuild(channel.guildID);
connection.prepare("UPDATE guilds SET disabled = ? WHERE guild_id = ?").run(JSON.stringify([...JSON.parse(guildDB.disabled), channel.id]), channel.guildID);
collections.disabledCache.set(channel.guildID, [...JSON.parse(guildDB.disabled), channel.id]);
}
export async function enableChannel(channel) {
const guildDB = await this.getGuild(channel.guild.id);
const guildDB = await this.getGuild(channel.guildID);
const newDisabled = JSON.parse(guildDB.disabled).filter(item => item !== channel.id);
connection.prepare("UPDATE guilds SET disabled = ? WHERE guild_id = ?").run(JSON.stringify(newDisabled), channel.guild.id);
collections.disabledCache.set(channel.guild.id, newDisabled);
connection.prepare("UPDATE guilds SET disabled = ? WHERE guild_id = ?").run(JSON.stringify(newDisabled), channel.guildID);
collections.disabledCache.set(channel.guildID, newDisabled);
}
export async function getTag(guild, tag) {

View file

@ -122,9 +122,9 @@ export async function send(bot) {
log("info", "Sending application command data to Discord...");
let cmdArray = commandArray.main;
if (process.env.ADMIN_SERVER && process.env.ADMIN_SERVER !== "") {
await bot.bulkEditGuildCommands(process.env.ADMIN_SERVER, commandArray.private);
await bot.application.bulkEditGuildCommands(process.env.ADMIN_SERVER, commandArray.private);
} else {
cmdArray = [...commandArray.main, ...commandArray.private];
}
await bot.bulkEditCommands(cmdArray);
await bot.application.bulkEditGlobalCommands(cmdArray);
}

View file

@ -131,8 +131,8 @@ const checkImages = async (message, extraReturnTypes, video, sticker) => {
}
}
// then check the attachments
} else if (message.attachments.length !== 0 && message.attachments[0].width) {
type = await getImage(message.attachments[0].proxy_url, message.attachments[0].url, video);
} else if (message.attachments.size !== 0 && message.attachments.first().width) {
type = await getImage(message.attachments.first().proxyURL, message.attachments.first().url, video);
}
}
// if the return value exists then return it
@ -147,7 +147,7 @@ export default async (client, cmdMessage, interaction, options, extraReturnTypes
if (options) {
if (options.image) {
const attachment = interaction.data.resolved.attachments.get(options.image);
const result = await getImage(attachment.proxyUrl, attachment.url, video, attachment.contentType);
const result = await getImage(attachment.proxyURL, attachment.url, video, attachment.contentType);
if (result !== false) return result;
} else if (options.link) {
const result = await getImage(options.link, options.link, video, extraReturnTypes, false, null, true);
@ -171,7 +171,8 @@ export default async (client, cmdMessage, interaction, options, extraReturnTypes
if (!singleMessage) {
// if there aren't any replies or interaction attachments then iterate over the last few messages in the channel
try {
const messages = await client.getMessages((interaction ? interaction : cmdMessage).channel.id);
const channel = (interaction ? interaction : cmdMessage).channel ?? await client.rest.channels.get((interaction ? interaction : cmdMessage).channelID);
const messages = await channel.getMessages();
// iterate over each message
for (const message of messages) {
const result = await checkImages(message, extraReturnTypes, video, sticker);

View file

@ -52,11 +52,12 @@ export function textEncode(string) {
}
// set activity (a.k.a. the gamer code)
export function activityChanger(bot) {
export async function activityChanger(bot) {
if (!broadcast) {
bot.editStatus("dnd", {
await bot.editStatus("dnd", [{
type: "GAME",
name: random(messages) + (types.classic ? ` | @${bot.user.username} help` : ""),
});
}]);
}
setTimeout(() => activityChanger(bot), 900000);
}
@ -68,16 +69,18 @@ export function checkBroadcast(bot) {
}
export function startBroadcast(bot, message) {
bot.editStatus("dnd", {
bot.editStatus("dnd", [{
type: "GAME",
name: message + (types.classic ? ` | @${bot.user.username} help` : ""),
});
}]);
broadcast = true;
}
export function endBroadcast(bot) {
bot.editStatus("dnd", {
bot.editStatus("dnd", [{
type: "GAME",
name: random(messages) + (types.classic ? ` | @${bot.user.username} help` : ""),
});
}]);
broadcast = false;
}
@ -108,3 +111,46 @@ export function getServers(bot) {
}
});
}
// copied from eris
export function cleanMessage(message) {
let cleanContent = message.content && message.content.replace(/<a?(:\w+:)[0-9]+>/g, "$1") || "";
let authorName = message.author.username;
if (message.guildID) {
const member = message.guild.members.get(message.author.id);
if (member && member.nick) {
authorName = member.nick;
}
}
cleanContent = cleanContent.replace(new RegExp(`<@!?${message.author.id}>`, "g"), `@\u200b${authorName}`);
if (message.mentions) {
for (const mention of message.mentions.members) {
if (message.guildID) {
const member = message.guild.members.get(mention.id);
if (member && member.nick) {
cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), `@\u200b${member.nick}`);
}
}
cleanContent = cleanContent.replace(new RegExp(`<@!?${mention.id}>`, "g"), `@\u200b${mention.username}`);
}
if (message.guildID && message.mentions.roles) {
for (const roleID of message.mentions.roles) {
const role = message.guild.roles.get(roleID);
const roleName = role ? role.name : "deleted-role";
cleanContent = cleanContent.replace(new RegExp(`<@&${roleID}>`, "g"), `@\u200b${roleName}`);
}
}
for (const id of message.mentions.channels) {
const channel = message.client.getChannel(id);
if (channel && channel.name && channel.mention) {
cleanContent = cleanContent.replace(channel.mention, `#${channel.name}`);
}
}
}
return cleanContent.replace(/@everyone/g, "@\u200beveryone").replace(/@here/g, "@\u200bhere");
}

View file

@ -1,4 +1,4 @@
// eris doesn't come with a method to wait for interactions by default, so we make our own
// oceanic doesn't come with a method to wait for interactions by default, so we make our own
import { EventEmitter } from "events";
class InteractionCollector extends EventEmitter {

View file

@ -1,12 +1,12 @@
import InteractionCollector from "./awaitinteractions.js";
import { ComponentInteraction } from "eris";
import { ComponentInteraction } from "oceanic.js";
export default async (client, info, pages, timeout = 120000) => {
const options = info.type === "classic" ? {
messageReference: {
channelID: info.channel.id,
channelID: info.message.channelID,
messageID: info.message.id,
guildID: info.channel.guild ? info.channel.guild.id : undefined,
guildID: info.message.guildID,
failIfNotExists: false
},
allowedMentions: {
@ -26,7 +26,7 @@ export default async (client, info, pages, timeout = 120000) => {
name: "◀"
},
style: 1,
custom_id: "back"
customID: "back"
},
{
type: 2,
@ -36,7 +36,7 @@ export default async (client, info, pages, timeout = 120000) => {
name: "▶"
},
style: 1,
custom_id: "forward"
customID: "forward"
},
{
type: 2,
@ -46,7 +46,7 @@ export default async (client, info, pages, timeout = 120000) => {
name: "🔢"
},
style: 1,
custom_id: "jump"
customID: "jump"
},
{
type: 2,
@ -56,24 +56,24 @@ export default async (client, info, pages, timeout = 120000) => {
name: "🗑"
},
style: 4,
custom_id: "delete"
customID: "delete"
}
]
}]
};
let currentPage;
if (info.type === "classic") {
currentPage = await client.createMessage(info.message.channel.id, Object.assign(pages[page], options, pages.length > 1 ? components : {}));
currentPage = await client.rest.channels.createMessage(info.message.channelID, Object.assign(pages[page], options, pages.length > 1 ? components : {}));
} else {
await info.interaction[info.interaction.acknowledged ? "editOriginalMessage" : "createMessage"](Object.assign(pages[page], pages.length > 1 ? components : {}));
currentPage = await info.interaction.getOriginalMessage();
await info.interaction[info.interaction.acknowledged ? "editOriginal" : "createMessage"](Object.assign(pages[page], pages.length > 1 ? components : {}));
currentPage = await info.interaction.getOriginal();
}
if (pages.length > 1) {
const interactionCollector = new InteractionCollector(client, currentPage, ComponentInteraction, timeout);
interactionCollector.on("interaction", async (interaction) => {
if ((interaction.member ?? interaction.user).id === info.author.id) {
switch (interaction.data.custom_id) {
switch (interaction.data.customID) {
case "back":
await interaction.deferUpdate();
page = page > 0 ? --page : pages.length - 1;
@ -99,7 +99,7 @@ export default async (client, info, pages, timeout = 120000) => {
type: 1,
components: [{
type: 3,
custom_id: "seekDropdown",
customID: "seekDropdown",
placeholder: "Page Number",
options: []
}]
@ -114,11 +114,11 @@ export default async (client, info, pages, timeout = 120000) => {
}
var promise;
if (info.type === "classic") {
promise = client.createMessage(info.message.channel.id, Object.assign({ content: "What page do you want to jump to?" }, {
promise = client.rest.channels.createMessage(info.message.channelID, Object.assign({ content: "What page do you want to jump to?" }, {
messageReference: {
channelID: currentPage.channel.id,
channelID: currentPage.channelID,
messageID: currentPage.id,
guildID: currentPage.channel.guild ? currentPage.channel.guild.id : undefined,
guildID: currentPage.guildID,
failIfNotExists: false
},
allowedMentions: {
@ -132,9 +132,9 @@ export default async (client, info, pages, timeout = 120000) => {
const dropdownCollector = new InteractionCollector(client, askMessage, ComponentInteraction, timeout);
let ended = false;
dropdownCollector.on("interaction", async (response) => {
if (response.data.custom_id !== "seekDropdown") return;
if (response.data.customID !== "seekDropdown") return;
try {
if (askMessage.channel.messages ? askMessage.channel.messages.has(askMessage.id) : await client.getMessage(askMessage.channel.id, askMessage.id).catch(() => undefined)) await askMessage.delete();
await askMessage.delete();
} catch {
// no-op
}
@ -146,7 +146,7 @@ export default async (client, info, pages, timeout = 120000) => {
dropdownCollector.once("end", async () => {
if (ended) return;
try {
if (askMessage.channel.messages ? askMessage.channel.messages.has(askMessage.id) : await client.getMessage(askMessage.channel.id, askMessage.id).catch(() => undefined)) await askMessage.delete();
await askMessage.delete();
} catch {
// no-op
}
@ -159,7 +159,11 @@ export default async (client, info, pages, timeout = 120000) => {
case "delete":
await interaction.deferUpdate();
interactionCollector.emit("end", true);
if (currentPage.channel.messages ? currentPage.channel.messages.has(currentPage.id) : await client.getMessage(currentPage.channel.id, currentPage.id).catch(() => undefined)) await currentPage.delete();
try {
await currentPage.delete();
} catch {
// no-op
}
return;
default:
break;
@ -172,8 +176,10 @@ export default async (client, info, pages, timeout = 120000) => {
for (const index of components.components[0].components.keys()) {
components.components[0].components[index].disabled = true;
}
if (currentPage.channel.messages ? currentPage.channel.messages.has(currentPage.id) : await client.getMessage(currentPage.channel.id, currentPage.id).catch(() => undefined)) {
try {
await currentPage.edit(components);
} catch {
// no-op
}
}
});

View file

@ -32,7 +32,7 @@ export async function checkStatus() {
}
export function connect(client) {
manager = new Shoukaku(new Connectors.Eris(client), nodes, { moveOnDisconnect: true, resume: true, reconnectInterval: 500, reconnectTries: 1 });
manager = new Shoukaku(new Connectors.OceanicJS(client), nodes, { moveOnDisconnect: true, resume: true, reconnectInterval: 500, reconnectTries: 1 });
client.emit("ready"); // workaround
manager.on("error", (node, error) => {
logger.error(`An error occurred on Lavalink node ${node}: ${error}`);
@ -66,7 +66,7 @@ export async function play(client, sound, options, music = false) {
if (!options.channel.guild.permissionsOf(client.user.id).has("voiceConnect")) return { content: "I can't join this voice channel!", flags: 64 };
const voiceChannel = options.channel.guild.channels.get(options.member.voiceState.channelID);
if (!voiceChannel.permissionsOf(client.user.id).has("voiceConnect")) return { content: "I don't have permission to join this voice channel!", flags: 64 };
if (!music && manager.players.has(options.channel.guild.id)) return { content: "I can't play a sound effect while other audio is playing!", flags: 64 };
if (!music && manager.players.has(options.channel.guildID)) return { content: "I can't play a sound effect while other audio is playing!", flags: 64 };
let node = manager.getNode();
if (!node) {
const status = await checkStatus();
@ -87,17 +87,17 @@ export async function play(client, sound, options, music = false) {
logger.error(e);
return { content: "🔊 Hmmm, seems that all of the audio servers are down. Try again in a bit.", flags: 64 };
}
const oldQueue = queues.get(voiceChannel.guild.id);
const oldQueue = queues.get(voiceChannel.guildID);
if (!response.tracks || response.tracks.length === 0) return { content: "I couldn't find that song!", flags: 64 };
if (music) {
const sortedTracks = response.tracks.map((val) => { return val.track; });
const playlistTracks = response.playlistInfo.selectedTrack ? sortedTracks : [sortedTracks[0]];
queues.set(voiceChannel.guild.id, oldQueue ? [...oldQueue, ...playlistTracks] : playlistTracks);
queues.set(voiceChannel.guildID, oldQueue ? [...oldQueue, ...playlistTracks] : playlistTracks);
}
const playerMeta = players.get(options.channel.guild.id);
const playerMeta = players.get(options.channel.guildID);
let player;
if (node.players.has(voiceChannel.guild.id)) {
player = node.players.get(voiceChannel.guild.id);
if (node.players.has(voiceChannel.guildID)) {
player = node.players.get(voiceChannel.guildID);
} else if (playerMeta?.player) {
const storedState = playerMeta?.player?.connection.state;
if (storedState && storedState === 1) {
@ -105,7 +105,7 @@ export async function play(client, sound, options, music = false) {
}
}
const connection = player ?? await node.joinChannel({
guildId: voiceChannel.guild.id,
guildId: voiceChannel.guildID,
channelId: voiceChannel.id,
shardId: voiceChannel.guild.shard.id,
deaf: true
@ -120,19 +120,19 @@ export async function play(client, sound, options, music = false) {
}
export async function nextSong(client, options, connection, track, info, music, voiceChannel, host, loop = false, shuffle = false, lastTrack = null) {
skipVotes.delete(voiceChannel.guild.id);
skipVotes.delete(voiceChannel.guildID);
const parts = Math.floor((0 / info.length) * 10);
let playingMessage;
if (music && lastTrack === track && players.has(voiceChannel.guild.id)) {
playingMessage = players.get(voiceChannel.guild.id).playMessage;
if (music && lastTrack === track && players.has(voiceChannel.guildID)) {
playingMessage = players.get(voiceChannel.guildID).playMessage;
} else {
try {
const content = !music ? "🔊 Playing sound..." : {
const content = !music ? { content: "🔊 Playing sound..." } : {
embeds: [{
color: 16711680,
author: {
name: "Now Playing",
icon_url: client.user.avatarURL
iconURL: client.user.avatarURL
},
fields: [{
name: " Title",
@ -157,7 +157,7 @@ export async function nextSong(client, options, connection, track, info, music,
}]
};
if (options.type === "classic") {
playingMessage = await client.createMessage(options.channel.id, content);
playingMessage = await client.rest.channels.createMessage(options.channel.id, content);
} else {
await options.interaction[options.interaction.acknowledged ? "editOriginalMessage" : "createMessage"](content);
playingMessage = await options.interaction.getOriginalMessage();
@ -171,30 +171,30 @@ export async function nextSong(client, options, connection, track, info, music,
connection.removeAllListeners("end");
connection.setVolume(0.70);
connection.playTrack({ track });
players.set(voiceChannel.guild.id, { player: connection, type: music ? "music" : "sound", host: host, voiceChannel: voiceChannel, originalChannel: options.channel, loop, shuffle, playMessage: playingMessage });
players.set(voiceChannel.guildID, { player: connection, type: music ? "music" : "sound", host: host, voiceChannel: voiceChannel, originalChannel: options.channel, loop, shuffle, playMessage: playingMessage });
connection.once("exception", async (exception) => {
try {
if (playingMessage.channel.messages.has(playingMessage.id)) await playingMessage.delete();
const playMessage = players.get(voiceChannel.guild.id).playMessage;
const playMessage = players.get(voiceChannel.guildID).playMessage;
if (playMessage.channel.messages.has(playMessage.id)) await playMessage.delete();
} catch {
// no-op
}
try {
connection.node.leaveChannel(voiceChannel.guild.id);
connection.node.leaveChannel(voiceChannel.guildID);
} catch {
// no-op
}
connection.removeAllListeners("stuck");
connection.removeAllListeners("end");
players.delete(voiceChannel.guild.id);
queues.delete(voiceChannel.guild.id);
players.delete(voiceChannel.guildID);
queues.delete(voiceChannel.guildID);
logger.error(exception.error);
const content = `🔊 Looks like there was an error regarding sound playback:\n\`\`\`${exception.type}: ${exception.error}\`\`\``;
if (options.type === "classic") {
await client.createMessage(options.channel.id, content);
await client.rest.channels.createMessage(options.channel.id, { content });
} else {
await options.interaction.createMessage(content);
await options.interaction.createMessage({ content });
}
});
connection.on("stuck", () => {
@ -204,11 +204,11 @@ export async function nextSong(client, options, connection, track, info, music,
});
connection.on("end", async (data) => {
if (data.reason === "REPLACED") return;
let queue = queues.get(voiceChannel.guild.id);
const player = players.get(voiceChannel.guild.id);
let queue = queues.get(voiceChannel.guildID);
const player = players.get(voiceChannel.guildID);
if (player && process.env.STAYVC === "true") {
player.type = "idle";
players.set(voiceChannel.guild.id, player);
players.set(voiceChannel.guildID, player);
}
let newQueue;
if (player?.shuffle) {
@ -225,7 +225,7 @@ export async function nextSong(client, options, connection, track, info, music,
} else {
newQueue = queue ? queue.slice(1) : [];
}
queues.set(voiceChannel.guild.id, newQueue);
queues.set(voiceChannel.guildID, newQueue);
if (newQueue.length !== 0) {
const newTrack = await connection.node.rest.decode(newQueue[0]);
nextSong(client, options, connection, newQueue[0], newTrack, music, voiceChannel, host, player.loop, player.shuffle, track);
@ -239,15 +239,15 @@ export async function nextSong(client, options, connection, track, info, music,
}
} else if (process.env.STAYVC !== "true") {
await setTimeout(400);
connection.node.leaveChannel(voiceChannel.guild.id);
players.delete(voiceChannel.guild.id);
queues.delete(voiceChannel.guild.id);
skipVotes.delete(voiceChannel.guild.id);
connection.node.leaveChannel(voiceChannel.guildID);
players.delete(voiceChannel.guildID);
queues.delete(voiceChannel.guildID);
skipVotes.delete(voiceChannel.guildID);
const content = `🔊 The voice channel session in \`${voiceChannel.name}\` has ended.`;
if (options.type === "classic") {
await client.createMessage(options.channel.id, content);
await client.rest.channels.createMessage(options.channel.id, { content });
} else {
await options.interaction.createMessage(content);
await options.interaction.createMessage({ content });
}
}
if (options.type === "classic") {

View file

@ -23,11 +23,11 @@ export async function upload(client, result, context, interaction = false) {
if (interaction) {
await context[context.acknowledged ? "editOriginalMessage" : "createMessage"](payload);
} else {
await client.createMessage(context.channel.id, Object.assign(payload, {
await client.rest.channels.createMessage(context.channel.id, Object.assign(payload, {
messageReference: {
channelID: context.channel.id,
messageID: context.id,
guildID: context.channel.guild ? context.channel.guild.id : undefined,
guildID: context.channel.guildID ?? undefined,
failIfNotExists: false
},
allowedMentions: {