Added (hacky) message command support, lots of work to prepare for message content intent enforcement, improve broadcast, remove evalraw, update packages

This commit is contained in:
Essem 2022-08-31 20:00:34 -05:00
parent 3392c3c89e
commit d33a7804d7
No known key found for this signature in database
GPG key ID: 7D497397CC3A2A8C
56 changed files with 443 additions and 315 deletions

View file

@ -8,11 +8,15 @@ class AvatarCommand extends Command {
if (this.type === "classic" && this.message.mentions[0]) {
return this.message.mentions[0].dynamicAvatarURL(null, 512);
} else if (await this.ipc.fetchUser(member)) {
const user = await this.ipc.fetchUser(member);
return user.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 512) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // hacky "solution"
let user = await this.ipc.fetchUser(member);
if (!user) user = await this.client.getRESTUser(member);
return user?.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 512) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // hacky "solution"
} else if (mentionRegex.test(member)) {
const id = member.match(mentionRegex)[1];
if (id < 21154535154122752n) return "That's not a valid mention!";
if (id < 21154535154122752n) {
this.success = false;
return "That's not a valid mention!";
}
try {
const user = await this.client.getRESTUser(id);
return user.avatar ? this.client._formatImage(`/avatars/${user.id}/${user.avatar}`, null, 512) : `https://cdn.discordapp.com/embed/avatars/${user.discriminator % 5}.png`; // repeat of hacky "solution" from above

View file

@ -12,7 +12,10 @@ class BannerCommand extends Command {
return user.dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!";
} else if (mentionRegex.test(member)) {
const id = member.match(mentionRegex)[1];
if (id < 21154535154122752n) return "That's not a valid mention!";
if (id < 21154535154122752n) {
this.success = false;
return "That's not a valid mention!";
}
try {
const user = await this.client.getRESTUser(id);
return user.dynamicBannerURL(null, 512) ?? "This user doesn't have a banner!";

View file

@ -3,11 +3,13 @@ import { clean } from "../../utils/misc.js";
class Base64Command extends Command {
async run() {
this.success = false;
if (this.type === "classic" && this.args.length === 0) return "You need to provide whether you want to encode or decode the text!";
const command = this.type === "classic" ? this.args[0].toLowerCase() : this.optionsArray[0].name.toLowerCase();
if (command !== "decode" && command !== "encode") return "You need to provide whether you want to encode or decode the text!";
const string = this.options.text ?? this.args.slice(1).join(" ");
if (!string || !string.trim()) return `You need to provide a string to ${command}!`;
this.success = true;
if (command === "decode") {
const b64Decoded = Buffer.from(string, "base64").toString("utf8");
return `\`\`\`\n${await clean(b64Decoded)}\`\`\``;

View file

@ -5,15 +5,20 @@ class BroadcastCommand extends Command {
run() {
return new Promise((resolve) => {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can broadcast messages!";
if (!owners.includes(this.author.id)) {
this.success = false;
resolve("Only the bot owner can broadcast messages!");
}
const message = this.options.message ?? this.args.join(" ");
if (message?.trim()) {
this.ipc.centralStore.set("broadcast", message);
this.ipc.broadcast("playbroadcast", message);
this.ipc.register("broadcastSuccess", () => {
this.ipc.unregister("broadcastSuccess");
resolve("Successfully broadcasted message.");
});
} else {
this.ipc.centralStore.delete("broadcast");
this.ipc.broadcast("broadcastend");
this.ipc.register("broadcastEnd", () => {
this.ipc.unregister("broadcastEnd");

View file

@ -3,6 +3,7 @@ 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!";
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!";
@ -23,6 +24,7 @@ class ChannelCommand extends Command {
}
await db.disableChannel(channel);
this.success = true;
return `I have been disabled in this channel. To re-enable me, just run \`${guildDB.prefix}channel enable\`.`;
} else if (this.args[0].toLowerCase() === "enable") {
let channel;
@ -36,11 +38,12 @@ class ChannelCommand extends Command {
}
await db.enableChannel(channel);
this.success = true;
return "I have been re-enabled in this channel.";
}
}
static description = "Enables/disables me in a channel (does not work with slash commands)";
static description = "Enables/disables classic commands in a channel (use server settings for slash commands)";
static arguments = ["[enable/disable]", "{id}"];
static slashAllowed = false;
static directAllowed = false;

View file

@ -4,6 +4,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!";
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!";
@ -21,16 +22,18 @@ class CommandCommand extends Command {
if (disabled?.includes(command)) return "That command is already disabled!";
await db.disableCommand(this.channel.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);
this.success = true;
return `The command \`${command}\` has been re-enabled.`;
}
}
static description = "Enables/disables a command for a server";
static description = "Enables/disables a classic command for a server (use server settings for slash commands)";
static aliases = ["cmd"];
static arguments = ["[enable/disable]", "[command]"];
static slashAllowed = false;

View file

@ -4,7 +4,10 @@ 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")) return "I don't have the `Embed Links` permission!";
if (this.channel.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) {
this.success = false;
return "I don't have the `Embed Links` permission!";
}
const counts = await database.getCounts();
const countArray = [];
for (const entry of Object.entries(counts)) {

View file

@ -4,8 +4,7 @@ import Command from "../../classes/command.js";
class EmoteCommand extends Command {
async run() {
const emoji = this.options.emoji ?? this.content;
if (!emoji || !emoji.trim()) return "You need to provide an emoji!";
if (emoji.split(" ")[0].match(/^<a?:.+:\d+>$/)) {
if (emoji && emoji.trim() && emoji.split(" ")[0].match(/^<a?:.+:\d+>$/)) {
return `https://cdn.discordapp.com/emojis/${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$2")}.${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$1") === "a" ? "gif" : "png"}`;
} else if (emoji.match(emojiRegex)) {
const codePoints = [];
@ -14,6 +13,7 @@ class EmoteCommand extends Command {
}
return `https://twemoji.maxcdn.com/v/latest/72x72/${codePoints.join("-").replace("-fe0f", "")}.png`;
} else {
this.success = false;
return "You need to provide a valid emoji to get an image!";
}
}

View file

@ -4,7 +4,10 @@ import Command from "../../classes/command.js";
class EvalCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can use eval!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can use eval!";
}
await this.acknowledge();
const code = this.options.code ?? this.args.join(" ");
try {

View file

@ -1,31 +0,0 @@
import { clean } from "../../utils/misc.js";
import Command from "../../classes/command.js";
class EvalRawCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can use evalraw!";
const code = this.args.join(" ");
try {
const evaled = eval(code);
if (evaled.length >= 2000) {
return {
text: "The result was too large, so here it is as a file:",
file: evaled,
name: "result.txt"
};
} else {
return evaled;
}
} catch (err) {
return `\`ERROR\` \`\`\`xl\n${await clean(err)}\n\`\`\``;
}
}
static description = "Executes JavaScript code (with raw output)";
static aliases = ["run"];
static arguments = ["[code]"];
static slashAllowed = false;
}
export default EvalRawCommand;

View file

@ -7,7 +7,10 @@ import Command from "../../classes/command.js";
class ExecCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can use exec!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can use exec!";
}
await this.acknowledge();
const code = this.options.cmd ?? this.args.join(" ");
try {

View file

@ -54,7 +54,10 @@ class HelpCommand extends Command {
}
return embed;
} else {
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.guild && !this.channel.permissionsOf(this.client.user.id).has("embedLinks")) {
this.success = false;
return "I don't have the `Embed Links` permission!";
}
const pages = [];
if (help.categories === help.categoryTemplate && !help.generated) await help.generateList();
for (const category of Object.keys(help.categories)) {

View file

@ -7,6 +7,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!";
const query = this.options.query ?? this.args.join(" ");
if (!query || !query.trim()) return "You need to provide something to search for!";
@ -34,6 +35,7 @@ class ImageSearchCommand extends Command {
}]
});
}
this.success = true;
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}

View file

@ -3,7 +3,10 @@ import Command from "../../classes/command.js";
class ImageReloadCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can reload the image servers!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can reload the image servers!";
}
const amount = await this.ipc.serviceCommand("image", { type: "reload" }, true);
if (amount > 0) {
return `Successfully connected to ${amount} image servers.`;

View file

@ -6,9 +6,11 @@ class LengthenCommand extends Command {
async run() {
await this.acknowledge();
const input = this.options.url ?? this.args.join(" ");
this.success = false;
if (!input || !input.trim() || !urlCheck(input)) return "You need to provide a short URL to lengthen!";
if (urlCheck(input)) {
const url = await request(encodeURI(input), { method: "HEAD" });
this.success = true;
return url.headers.location || input;
} else {
return "That isn't a URL!";

View file

@ -7,7 +7,10 @@ class PrefixCommand extends Command {
const guild = await database.getGuild(this.channel.guild.id);
if (this.args.length !== 0) {
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 change the bot prefix!";
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);
return `The prefix has been changed to ${this.args[0]}.`;
} else {

View file

@ -4,7 +4,10 @@ import Command from "../../classes/command.js";
class QrCreateCommand extends Command {
async run() {
if (this.args.length === 0) return "You need to provide some text to generate a QR code!";
if (this.args.length === 0) {
this.success = false;
return "You need to provide some text to generate a QR code!";
}
await this.acknowledge();
const writable = new PassThrough();
qrcode.toFileStream(writable, this.content, { margin: 1 });

View file

@ -8,12 +8,14 @@ import imageDetect from "../../utils/imagedetect.js";
class QrReadCommand extends Command {
async run() {
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
this.success = false;
if (image === undefined) return "You need to provide an image/GIF with a QR code to read!";
await this.acknowledge();
const data = Buffer.from(await (await request(image.path)).body.arrayBuffer());
const rawData = await sharp(data).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
const qrBuffer = jsqr(rawData.data, rawData.info.width, rawData.info.height);
if (!qrBuffer) return "I couldn't find a QR code!";
this.success = true;
return `\`\`\`\n${await clean(qrBuffer.data)}\n\`\`\``;
}

View file

@ -5,7 +5,10 @@ class RawCommand extends Command {
async run() {
await this.acknowledge();
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
if (image === undefined) return "You need to provide an image/GIF to get a raw URL!";
if (image === undefined) {
this.success = false;
return "You need to provide an image/GIF to get a raw URL!";
}
return image.path;
}

View file

@ -3,7 +3,10 @@ import Command from "../../classes/command.js";
class RestartCommand extends Command {
async run() {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can restart me!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can restart me!";
}
await this.client.createMessage(this.channel.id, Object.assign({
content: "esmBot is restarting."
}, this.reference));

View file

@ -2,7 +2,10 @@ import Command from "../../classes/command.js";
class ServerInfoCommand extends Command {
async run() {
if (!this.channel.guild) return "This command only works in servers!";
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: [{

View file

@ -2,8 +2,10 @@ import Command from "../../classes/command.js";
class SnowflakeCommand extends Command {
async run() {
this.success = false;
if (!this.args[0]) return "You need to provide a snowflake ID!";
if (!this.args[0].match(/^<?[@#]?[&!]?\d+>?$/) && this.args[0] < 21154535154122752n) return "That's not a valid snowflake!";
this.success = true;
return `<t:${Math.floor(((this.args[0].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "") / 4194304) + 1420070400000) / 1000)}:F>`;
}

View file

@ -5,7 +5,10 @@ class SoundReloadCommand extends Command {
run() {
return new Promise((resolve) => {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return "Only the bot owner can reload Lavalink!";
if (!owners.includes(this.author.id)) {
this.success = false;
return "Only the bot owner can reload Lavalink!";
}
this.acknowledge().then(() => {
this.ipc.broadcast("soundreload");
this.ipc.register("soundReloadSuccess", (msg) => {

View file

@ -4,10 +4,13 @@ import imagedetect from "../../utils/imagedetect.js";
class StickerCommand extends Command {
async run() {
const result = await imagedetect(this.client, this.message, this.interaction, this.options, false, false, true);
this.success = false;
if (!result) return "You need to provide a sticker!";
if (result.format_type === 1) { // PNG
this.success = true;
return `https://cdn.discordapp.com/stickers/${result.id}.png`;
} else if (result.format_type === 2) { // APNG
this.success = true;
return {
embeds: [{
color: 16711680,
@ -18,6 +21,7 @@ class StickerCommand extends Command {
}]
};
} else if (result.format_type === 3) { // Lottie
this.success = true;
return `I can't display this sticker because it uses the Lottie animation format; however, I can give you the raw JSON link to it: https://cdn.discordapp.com/stickers/${result.id}.json`;
} else {
return "I don't recognize that sticker format!";

View file

@ -8,6 +8,7 @@ import Command from "../../classes/command.js";
class YouTubeCommand extends Command {
async run() {
const query = this.options.query ?? this.args.join(" ");
this.success = false;
if (!query || !query.trim()) return "You need to provide something to search for!";
await this.acknowledge();
const messages = [];
@ -16,6 +17,7 @@ class YouTubeCommand extends Command {
for (const [i, value] of videos.results.entries()) {
messages.push({ content: `Page ${i + 1} of ${videos.results.length}\n<:youtube:637020823005167626> **${value.title.replaceAll("*", "\\*")}**\nUploaded by **${value.author.replaceAll("*", "\\*")}**\n${value.url}` });
}
this.success = true;
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, messages);
}