More slash command work

This commit is contained in:
Essem 2022-03-31 14:53:22 -05:00
parent 77913618d6
commit c821d91254
No known key found for this signature in database
GPG Key ID: 7D497397CC3A2A8C
26 changed files with 188 additions and 62 deletions

View File

@ -2,13 +2,21 @@ import Command from "../../classes/command.js";
class DiceCommand extends Command {
async run() {
if (this.args.length === 0 || !this.args[0].match(/^\d+$/)) {
const max = this.type === "classic" ? parseInt(this.args[0]) : this.options.max;
if (!max) {
return `🎲 The dice landed on ${Math.floor(Math.random() * 6) + 1}.`;
} else {
return `🎲 The dice landed on ${Math.floor(Math.random() * parseInt(this.args[0])) + 1}.`;
return `🎲 The dice landed on ${Math.floor(Math.random() * max) + 1}.`;
}
}
static flags = [{
name: "max",
type: 4,
description: "The maximum dice value",
min_value: 1
}];
static description = "Rolls the dice";
static aliases = ["roll", "die", "rng", "random"];
static arguments = ["{number}"];

View File

@ -28,6 +28,7 @@ class AvatarCommand extends Command {
static description = "Gets a user's avatar";
static aliases = ["pfp", "ava"];
static arguments = ["{mention/id}"];
static slashAllowed = false;
}
export default AvatarCommand;

View File

@ -28,6 +28,7 @@ class BannerCommand extends Command {
static description = "Gets a user's banner";
static aliases = ["userbanner"];
static arguments = ["{mention/id}"];
static slashAllowed = false;
}
export default BannerCommand;

View File

@ -6,8 +6,9 @@ class BroadcastCommand extends Command {
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 (this.args.length !== 0) {
this.ipc.broadcast("playbroadcast", this.args.join(" "));
const message = this.type === "classic" ? this.args.join(" ") : this.options.message;
if (message && message.trim()) {
this.ipc.broadcast("playbroadcast", message);
this.ipc.register("broadcastSuccess", () => {
this.ipc.unregister("broadcastSuccess");
resolve("Successfully broadcasted message.");
@ -22,6 +23,12 @@ class BroadcastCommand extends Command {
});
}
static flags = [{
name: "message",
type: 3,
description: "The message to broadcast"
}];
static description = "Broadcasts a playing message until the command is run again or the bot restarts";
}

View File

@ -39,7 +39,7 @@ class CountCommand extends Command {
}]
});
}
return paginator(this.client, this.message, embeds);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}
static description = "Gets how many times every command was used";

View File

@ -5,7 +5,8 @@ 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!";
const code = this.args.join(" ");
await this.acknowledge();
const code = this.type === "classic" ? this.args.join(" ") : this.options.code;
try {
const evaled = eval(code);
const cleaned = await clean(evaled);
@ -24,6 +25,13 @@ class EvalCommand extends Command {
}
}
static flags = [{
name: "code",
type: 3,
description: "The code to execute",
required: true
}];
static description = "Executes JavaScript code";
static aliases = ["run"];
static arguments = ["[code]"];

View File

@ -25,6 +25,7 @@ class EvalRawCommand extends Command {
static description = "Executes JavaScript code (with raw output)";
static aliases = ["run"];
static arguments = ["[code]"];
static slashAllowed = false;
}
export default EvalRawCommand;

View File

@ -8,7 +8,8 @@ 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!";
const code = this.args.join(" ");
await this.acknowledge();
const code = this.type === "classic" ? this.args.join(" ") : this.options.cmd;
try {
const execed = await exec(code);
if (execed.stderr) return `\`ERROR\` \`\`\`xl\n${await clean(execed.stderr)}\n\`\`\``;
@ -28,6 +29,13 @@ class ExecCommand extends Command {
}
}
static flags = [{
name: "cmd",
type: 3,
description: "The command to execute",
required: true
}];
static description = "Executes a shell command";
static aliases = ["runcmd"];
static arguments = ["[command]"];

View File

@ -93,7 +93,7 @@ class HelpCommand extends Command {
}]
});
}
return paginator(this.client, this.message, embeds);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}
}

View File

@ -8,10 +8,11 @@ import Command from "../../classes/command.js";
class ImageSearchCommand 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.args.length === 0) return "You need to provide something to search for!";
const query = this.type === "classic" ? this.args.join(" ") : this.options.query;
if (!query || !query.trim()) return "You need to provide something to search for!";
this.acknowledge();
const embeds = [];
const rawImages = await fetch(`${random(searx)}/search?format=json&safesearch=2&categories=images&q=!goi%20!ddi%20${encodeURIComponent(this.args.join(" "))}`).then(res => res.json());
const rawImages = await fetch(`${random(searx)}/search?format=json&safesearch=2&categories=images&q=!goi%20!ddi%20${encodeURIComponent(query)}`).then(res => res.json());
if (rawImages.results.length === 0) return "I couldn't find any results!";
const images = rawImages.results.filter((val) => !val.img_src.startsWith("data:"));
for (const [i, value] of images.entries()) {
@ -33,9 +34,16 @@ class ImageSearchCommand extends Command {
}]
});
}
return paginator(this.client, this.message, embeds);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}
static flags = [{
name: "query",
type: 3,
description: "The query you want to search for",
required: true
}];
static description = "Searches for images across the web";
static aliases = ["im", "photo", "img"];
static arguments = ["[query]"];

View File

@ -6,12 +6,14 @@ class ReloadCommand extends Command {
return new Promise((resolve) => {
const owners = process.env.OWNER.split(",");
if (!owners.includes(this.author.id)) return resolve("Only the bot owner can reload commands!");
if (this.args.length === 0) return resolve("You need to provide a command to reload!");
this.ipc.broadcast("reload", this.args[0]);
const commandName = this.type === "classic" ? this.args.join(" ") : this.options.cmd;
if (!commandName || !commandName.trim()) return resolve("You need to provide a command to reload!");
this.acknowledge();
this.ipc.broadcast("reload", commandName);
this.ipc.register("reloadSuccess", () => {
this.ipc.unregister("reloadSuccess");
this.ipc.unregister("reloadFail");
resolve(`The command \`${this.args[0]}\` has been reloaded.`);
resolve(`The command \`${commandName}\` has been reloaded.`);
});
this.ipc.register("reloadFail", (message) => {
this.ipc.unregister("reloadSuccess");
@ -21,6 +23,13 @@ class ReloadCommand extends Command {
});
}
static flags = [{
name: "cmd",
type: 3,
description: "The command to reload",
required: true
}];
static description = "Reloads a command";
static arguments = ["[command]"];
}

View File

@ -58,6 +58,7 @@ class UserInfoCommand extends Command {
static description = "Gets info about a user";
static aliases = ["user"];
static arguments = ["[mention/id]"];
static slashAllowed = false;
}
export default UserInfoCommand;

View File

@ -7,17 +7,25 @@ import Command from "../../classes/command.js";
class YouTubeCommand extends Command {
async run() {
if (this.args.length === 0) return "You need to provide something to search for!";
const query = this.type === "classic" ? this.args.join(" ") : this.options.query;
if (!query || !query.trim()) return "You need to provide something to search for!";
this.acknowledge();
const messages = [];
const videos = await fetch(`${random(searx)}/search?format=json&safesearch=1&categories=videos&q=!youtube%20${encodeURIComponent(this.args.join(" "))}`).then(res => res.json());
const videos = await fetch(`${random(searx)}/search?format=json&safesearch=1&categories=videos&q=!youtube%20${encodeURIComponent(query)}`).then(res => res.json());
if (videos.results.length === 0) return "I couldn't find any results!";
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}` });
}
return paginator(this.client, this.message, messages);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, messages);
}
static flags = [{
name: "query",
type: 3,
description: "The query you want to search for",
required: true
}];
static description = "Searches YouTube";
static aliases = ["yt", "video", "ytsearch"];
static arguments = ["[query]"];

View File

@ -7,16 +7,17 @@ class FlagCommand extends ImageCommand {
flagPath = "";
async criteria() {
if (!this.args[0].match(emojiRegex)) return false;
const flag = emoji.unemojify(this.args[0]).replaceAll(":", "").replace("flag-", "");
const text = this.type === "classic" ? this.args[0] : this.options.text;
if (!text.match(emojiRegex)) return false;
const flag = emoji.unemojify(text).replaceAll(":", "").replace("flag-", "");
let path = `./assets/images/region-flags/png/${flag.toUpperCase()}.png`;
if (flag === "pirate_flag") path = "./assets/images/pirateflag.png";
if (flag === "rainbow-flag") path = "./assets/images/rainbowflag.png";
if (flag === "checkered_flag") path = "./assets/images/checkeredflag.png";
if (flag === "transgender_flag") path = "./assets/images/transflag.png";
if (this.args[0] === "🏴󠁧󠁢󠁳󠁣󠁴󠁿") path = "./assets/images/region-flags/png/GB-SCT.png";
if (this.args[0] === "🏴󠁧󠁢󠁷󠁬󠁳󠁿") path = "./assets/images/region-flags/png/GB-WLS.png";
if (this.args[0] === "🏴󠁧󠁢󠁥󠁮󠁧󠁿") path = "./assets/images/region-flags/png/GB-ENG.png";
if (text === "🏴󠁧󠁢󠁳󠁣󠁴󠁿") path = "./assets/images/region-flags/png/GB-SCT.png";
if (text === "🏴󠁧󠁢󠁷󠁬󠁳󠁿") path = "./assets/images/region-flags/png/GB-WLS.png";
if (text === "🏴󠁧󠁢󠁥󠁮󠁧󠁿") path = "./assets/images/region-flags/png/GB-ENG.png";
try {
await fs.promises.access(path);
this.flagPath = path;

View File

@ -1,8 +1,18 @@
import ImageCommand from "../../classes/imageCommand.js";
class FreezeCommand extends ImageCommand {
constructor(client, cluster, worker, ipc, options) {
super(client, cluster, worker, ipc, options);
this.flags.push({
name: "endframe",
type: 4,
description: "Set the end frame (default: last frame)",
min_value: 0
});
}
params() {
const frameCount = parseInt(this.args[0]);
const frameCount = parseInt(this.type === "classic" ? this.args[0] : this.options.endframe);
return {
loop: false,
frame: isNaN(frameCount) ? -1 : frameCount

View File

@ -1,8 +1,19 @@
import ImageCommand from "../../classes/imageCommand.js";
class JPEGCommand extends ImageCommand {
constructor(client, cluster, worker, ipc, options) {
super(client, cluster, worker, ipc, options);
this.flags.push({
name: "quality",
type: 4,
description: "Set the JPEG quality (default: 1)",
min_value: 1,
max_value: 100
});
}
params() {
const quality = parseInt(this.args[0]);
const quality = parseInt(this.type === "classic" ? this.args[0] : this.options.quality);
return {
quality: isNaN(quality) ? 1 : Math.max(1, Math.min(quality, 100))
};

View File

@ -19,8 +19,8 @@ class MotivateCommand extends ImageCommand {
}
params(url) {
const newArgs = this.args.filter(item => !item.includes(url));
const [topText, bottomText] = newArgs.join(" ").split(/(?<!\\),/).map(elem => elem.trim());
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
const [topText, bottomText] = newArgs.split(/(?<!\\),/).map(elem => elem.trim());
return {
top: topText.replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%"),
bottom: bottomText ? bottomText.replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%") : "",

View File

@ -3,9 +3,10 @@ import { random } from "../../utils/misc.js";
const names = ["esmBot", "me_irl", "dankmemes", "hmmm", "gaming", "wholesome", "chonkers", "memes", "funny", "pcmasterrace", "thomastheplankengine"];
class RedditCommand extends ImageCommand {
params() {
params(url) {
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
return {
caption: this.args.length === 0 ? random(names) : this.args.join(" ").replaceAll("\n", "").replaceAll(" ", "")
caption: newArgs && newArgs.trim() ? newArgs.replaceAll("\n", "").replaceAll(" ", "") : random(names)
};
}

View File

@ -1,8 +1,18 @@
import ImageCommand from "../../classes/imageCommand.js";
class SlowCommand extends ImageCommand {
constructor(client, cluster, worker, ipc, options) {
super(client, cluster, worker, ipc, options);
this.flags.push({
name: "multiplier",
type: 4,
description: "Set the speed multiplier (default: 2)",
min_value: 1
});
}
params() {
const speed = parseInt(this.args[0]);
const speed = parseInt(this.type === "classic" ? this.args[0] : this.options.multiplier);
return {
slow: true,
speed: isNaN(speed) ? 2 : speed

View File

@ -13,10 +13,10 @@ class SnapchatCommand extends ImageCommand {
}
params(url) {
const newArgs = this.args.filter(item => !item.includes(url));
const newArgs = this.type === "classic" ? this.args.filter(item => !item.includes(url)).join(" ") : this.options.text;
const position = parseFloat(this.specialArgs.position);
return {
caption: newArgs.join(" ").replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%"),
caption: newArgs.replaceAll("&", "\\&amp;").replaceAll(">", "\\&gt;").replaceAll("<", "\\&lt;").replaceAll("\"", "\\&quot;").replaceAll("'", "\\&apos;").replaceAll("%", "\\%"),
pos: isNaN(position) ? 0.5 : position
};
}

View File

@ -49,7 +49,7 @@ class QueueCommand extends MusicCommand {
});
}
if (embeds.length === 0) return "There's nothing in the queue!";
return paginator(this.client, this.message, embeds);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
}
static description = "Shows the current queue";

View File

@ -75,7 +75,7 @@ class TagsCommand extends Command {
});
}
if (embeds.length === 0) return "I couldn't find any tags!";
return paginator(this.client, this.message, embeds);
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
} else if (this.args[0].toLowerCase() === "random") {
const tagList = await database.getTags(this.channel.guild.id);
return tagList[random(Object.keys(tagList))].content;

5
events/debug.js Normal file
View File

@ -0,0 +1,5 @@
import { debug } from "../utils/logger.js";
export default async (client, cluster, worker, ipc, message) => {
debug(message);
};

View File

@ -80,8 +80,8 @@ class Shard extends BaseClusterWorker {
this.ipc.register("reload", async (message) => {
const path = paths.get(message);
if (!path) return this.ipc.broadcast("reloadFail", { result: "I couldn't find that command!" });
const result = await load(this.bot, this.clusterID, this.workerID, this.ipc, path, await checkStatus());
if (result) return this.ipc.broadcast("reloadFail", { result });
const result = await load(this.bot, this.clusterID, this.workerID, this.ipc, path, await checkStatus(), true);
if (result !== message) return this.ipc.broadcast("reloadFail", { result });
return this.ipc.broadcast("reloadSuccess");
});

View File

@ -4,7 +4,7 @@ import { log } from "./logger.js";
let queryValue = 0;
// load command into memory
export async function load(client, cluster, worker, ipc, command, soundStatus) {
export async function load(client, cluster, worker, ipc, command, soundStatus, slashReload = false) {
const { default: props } = await import(`${command}?v=${queryValue}`);
queryValue++;
if (props.requires.includes("sound") && soundStatus) {
@ -27,6 +27,19 @@ export async function load(client, cluster, worker, ipc, command, soundStatus) {
flags: propsInstance.flags ?? props.flags,
slashAllowed: props.slashAllowed
});
if (slashReload && props.slashAllowed) {
const commandList = await client.getCommands();
const oldCommand = commandList.filter((item) => {
return item.name === commandName;
})[0];
await client.editCommand(oldCommand.id, {
name: commandName,
type: 1,
description: props.description,
options: propsInstance.flags ?? props.flags
});
}
if (props.aliases) {
for (const alias of props.aliases) {

View File

@ -1,18 +1,18 @@
import InteractionCollector from "./awaitinteractions.js";
import { ComponentInteraction } from "eris";
export default async (client, message, pages, timeout = 120000) => {
const options = {
export default async (client, info, pages, timeout = 120000) => {
const options = info.type === "classic" ? {
messageReference: {
channelID: message.channel.id,
messageID: message.id,
guildID: message.channel.guild ? message.channel.guild.id : undefined,
channelID: info.channel.id,
messageID: info.message.id,
guildID: info.channel.guild ? info.channel.guild.id : undefined,
failIfNotExists: false
},
allowedMentions: {
repliedUser: false
}
};
} : {};
let page = 0;
const components = {
components: [{
@ -61,11 +61,18 @@ export default async (client, message, pages, timeout = 120000) => {
]
}]
};
let currentPage = await client.createMessage(message.channel.id, Object.assign(pages[page], options, pages.length > 1 ? components : {}));
let currentPage;
if (info.type === "classic") {
currentPage = await client.createMessage(info.message.channel.id, 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();
}
if (pages.length > 1) {
const interactionCollector = new InteractionCollector(client, currentPage, ComponentInteraction, timeout);
interactionCollector.on("interaction", async (interaction) => {
if ((interaction.member ?? interaction.user).id === message.author.id) {
if ((interaction.member ?? interaction.user).id === info.author.id) {
switch (interaction.data.custom_id) {
case "back":
await interaction.deferUpdate();
@ -105,17 +112,23 @@ export default async (client, message, pages, timeout = 120000) => {
};
jumpComponents.components[0].components[0].options[i] = payload;
}
client.createMessage(message.channel.id, Object.assign({ content: "What page do you want to jump to?" }, {
messageReference: {
channelID: currentPage.channel.id,
messageID: currentPage.id,
guildID: currentPage.channel.guild ? currentPage.channel.guild.id : undefined,
failIfNotExists: false
},
allowedMentions: {
repliedUser: false
}
}, jumpComponents)).then(askMessage => {
var promise;
if (info.type === "classic") {
promise = client.createMessage(info.message.channel.id, Object.assign({ content: "What page do you want to jump to?" }, {
messageReference: {
channelID: currentPage.channel.id,
messageID: currentPage.id,
guildID: currentPage.channel.guild ? currentPage.channel.guild.id : undefined,
failIfNotExists: false
},
allowedMentions: {
repliedUser: false
}
}, jumpComponents));
} else {
promise = info.interaction.createFollowup(Object.assign({ content: "What page do you want to jump to?" }, jumpComponents));
}
promise.then(askMessage => {
const dropdownCollector = new InteractionCollector(client, askMessage, ComponentInteraction, timeout);
let ended = false;
dropdownCollector.on("interaction", async (response) => {
@ -145,7 +158,7 @@ export default async (client, message, pages, timeout = 120000) => {
break;
case "delete":
await interaction.deferUpdate();
interactionCollector.emit("end");
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();
return;
default:
@ -153,13 +166,15 @@ export default async (client, message, pages, timeout = 120000) => {
}
}
});
interactionCollector.once("end", async () => {
interactionCollector.once("end", async (deleted = false) => {
interactionCollector.removeAllListeners("interaction");
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)) {
await currentPage.edit(components);
if (!deleted) {
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)) {
await currentPage.edit(components);
}
}
});
}