mirror of
https://github.com/keanuplayz/TravBot-v3.git
synced 2024-08-15 02:33:12 +00:00
Separated command handler from utility modules and fixed lingering errors in commands
This commit is contained in:
parent
6ed4c0988f
commit
5c3896c2db
38 changed files with 344 additions and 407 deletions
|
@ -1,5 +1,5 @@
|
|||
import Command from "../../core/command";
|
||||
import {random} from "../../core/lib";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {random} from "../../lib";
|
||||
|
||||
const responses = [
|
||||
"Most likely,",
|
||||
|
@ -24,16 +24,16 @@ const responses = [
|
|||
"Very doubtful,"
|
||||
];
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Answers your question in an 8-ball manner.",
|
||||
endpoint: false,
|
||||
usage: "<question>",
|
||||
run: "Please provide a question.",
|
||||
any: new Command({
|
||||
description: "Question to ask the 8-ball.",
|
||||
async run($) {
|
||||
const sender = $.message.author;
|
||||
$.channel.send(`${random(responses)} <@${sender.id}>`);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const sender = message.author;
|
||||
channel.send(`${random(responses)} <@${sender.id}>`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {User} from "discord.js";
|
||||
import Command from "../../core/command";
|
||||
import {random} from "../../core/lib";
|
||||
import {parseVars} from "../../core/lib";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {random, parseVars} from "../../lib";
|
||||
|
||||
const cookies = [
|
||||
`has given %target% a chocolate chip cookie!`,
|
||||
|
@ -26,29 +25,29 @@ const cookies = [
|
|||
`bakes %target% fresh cookies, it smells amazing.`
|
||||
];
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Gives specified user a cookie.",
|
||||
usage: "['all'/@user]",
|
||||
run: ":cookie: Here's a cookie!",
|
||||
subcommands: {
|
||||
all: new Command({
|
||||
async run($) {
|
||||
$.channel.send(`${$.author} gave everybody a cookie!`);
|
||||
all: new NamedCommand({
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(`${author} gave everybody a cookie!`);
|
||||
}
|
||||
})
|
||||
},
|
||||
user: new Command({
|
||||
description: "User to give cookie to.",
|
||||
async run($) {
|
||||
const sender = $.author;
|
||||
const mention: User = $.args[0];
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const sender = author;
|
||||
const mention: User = args[0];
|
||||
|
||||
if (mention.id == sender.id) {
|
||||
$.channel.send("You can't give yourself cookies!");
|
||||
channel.send("You can't give yourself cookies!");
|
||||
return;
|
||||
}
|
||||
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
`:cookie: <@${sender.id}> ${parseVars(random(cookies), {
|
||||
target: mention.toString()
|
||||
})}`
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand, callMemberByUsername} from "../../core";
|
||||
import {isAuthorized, getMoneyEmbed} from "./modules/eco-utils";
|
||||
import {DailyCommand, PayCommand, GuildCommand, LeaderboardCommand} from "./modules/eco-core";
|
||||
import {BuyCommand, ShopCommand} from "./modules/eco-shop";
|
||||
import {MondayCommand} from "./modules/eco-extras";
|
||||
import {callMemberByUsername} from "../../core/libd";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Economy command for Monika.",
|
||||
async run({guild, channel, author}) {
|
||||
if (isAuthorized(guild, channel)) channel.send(getMoneyEmbed(author));
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import Command from "../../../core/command";
|
||||
import {prompt} from "../../../core/libd";
|
||||
import {pluralise} from "../../../core/lib";
|
||||
import {Storage} from "../../../core/structures";
|
||||
import {Command, NamedCommand, prompt} from "../../../core";
|
||||
import {pluralise} from "../../../lib";
|
||||
import {Storage} from "../../../structures";
|
||||
import {isAuthorized, getMoneyEmbed, getSendEmbed, ECO_EMBED_COLOR} from "./eco-utils";
|
||||
|
||||
export const DailyCommand = new Command({
|
||||
export const DailyCommand = new NamedCommand({
|
||||
description: "Pick up your daily Mons. The cooldown is per user and every 22 hours to allow for some leeway.",
|
||||
aliases: ["get"],
|
||||
async run({author, channel, guild}) {
|
||||
|
@ -38,7 +37,7 @@ export const DailyCommand = new Command({
|
|||
}
|
||||
});
|
||||
|
||||
export const GuildCommand = new Command({
|
||||
export const GuildCommand = new NamedCommand({
|
||||
description: "Get info on the guild's economy as a whole.",
|
||||
async run({guild, channel}) {
|
||||
if (isAuthorized(guild, channel)) {
|
||||
|
@ -75,7 +74,7 @@ export const GuildCommand = new Command({
|
|||
}
|
||||
});
|
||||
|
||||
export const LeaderboardCommand = new Command({
|
||||
export const LeaderboardCommand = new NamedCommand({
|
||||
description: "See the richest players.",
|
||||
aliases: ["top"],
|
||||
async run({guild, channel, client}) {
|
||||
|
@ -109,7 +108,7 @@ export const LeaderboardCommand = new Command({
|
|||
}
|
||||
});
|
||||
|
||||
export const PayCommand = new Command({
|
||||
export const PayCommand = new NamedCommand({
|
||||
description: "Send money to someone.",
|
||||
usage: "<user> <amount>",
|
||||
run: "Who are you sending this money to?",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Command from "../../../core/command";
|
||||
import {Storage} from "../../../core/structures";
|
||||
import {Command, NamedCommand} from "../../../core/command";
|
||||
import {Storage} from "../../../structures";
|
||||
import {isAuthorized, getMoneyEmbed} from "./eco-utils";
|
||||
|
||||
const WEEKDAY = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
||||
|
||||
export const MondayCommand = new Command({
|
||||
export const MondayCommand = new NamedCommand({
|
||||
description: "Use this on a UTC Monday to get an extra Mon. Does not affect your 22 hour timer for `eco daily`.",
|
||||
async run({guild, channel, author}) {
|
||||
if (isAuthorized(guild, channel)) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Message} from "discord.js";
|
||||
import {random} from "../../../core/lib";
|
||||
import {random} from "../../../lib";
|
||||
|
||||
export interface ShopItem {
|
||||
cost: number;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import Command from "../../../core/command";
|
||||
import {pluralise, split} from "../../../core/lib";
|
||||
import {paginate} from "../../../core/libd";
|
||||
import {Storage, getPrefix} from "../../../core/structures";
|
||||
import {Command, NamedCommand, paginate} from "../../../core";
|
||||
import {pluralise, split} from "../../../lib";
|
||||
import {Storage, getPrefix} from "../../../structures";
|
||||
import {isAuthorized, ECO_EMBED_COLOR} from "./eco-utils";
|
||||
import {ShopItems, ShopItem} from "./eco-shop-items";
|
||||
import {EmbedField} from "discord.js";
|
||||
|
||||
export const ShopCommand = new Command({
|
||||
export const ShopCommand = new NamedCommand({
|
||||
description: "Displays the list of items you can buy in the shop.",
|
||||
async run({guild, channel, author}) {
|
||||
if (isAuthorized(guild, channel)) {
|
||||
|
@ -45,7 +44,7 @@ export const ShopCommand = new Command({
|
|||
}
|
||||
});
|
||||
|
||||
export const BuyCommand = new Command({
|
||||
export const BuyCommand = new NamedCommand({
|
||||
description: "Buys an item from the shop.",
|
||||
usage: "<item>",
|
||||
async run({guild, channel, args, message, author}) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {pluralise} from "../../../core/lib";
|
||||
import {Storage} from "../../../core/structures";
|
||||
import {pluralise} from "../../../lib";
|
||||
import {Storage} from "../../../structures";
|
||||
import {User, Guild, TextChannel, DMChannel, NewsChannel} from "discord.js";
|
||||
|
||||
export const ECO_EMBED_COLOR = 0xf1c40f;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {URL} from "url";
|
||||
import Command from "../../core/command";
|
||||
import {getContent} from "../../core/lib";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {getContent} from "../../lib";
|
||||
|
||||
const endpoints: {sfw: {[key: string]: string}} = {
|
||||
sfw: {
|
||||
|
@ -34,26 +34,26 @@ const endpoints: {sfw: {[key: string]: string}} = {
|
|||
}
|
||||
};
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Provides you with a random image with the selected argument.",
|
||||
async run($) {
|
||||
$.channel.send(
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(
|
||||
`Please provide an image type. Available arguments:\n\`[${Object.keys(endpoints.sfw).join(", ")}]\`.`
|
||||
);
|
||||
},
|
||||
any: new Command({
|
||||
description: "Image type to send.",
|
||||
async run($) {
|
||||
const arg = $.args[0];
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const arg = args[0];
|
||||
|
||||
if (!(arg in endpoints.sfw)) {
|
||||
$.channel.send("Couldn't find that endpoint!");
|
||||
channel.send("Couldn't find that endpoint!");
|
||||
return;
|
||||
}
|
||||
|
||||
let url = new URL(`https://nekos.life/api/v2${endpoints.sfw[arg]}`);
|
||||
const content = await getContent(url.toString());
|
||||
$.channel.send(content.url);
|
||||
channel.send(content.url);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Command from "../../core/command";
|
||||
import {random} from "../../core/lib";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {random} from "../../lib";
|
||||
|
||||
const responses = [
|
||||
"boomer",
|
||||
|
@ -59,9 +59,9 @@ const responses = [
|
|||
"large man"
|
||||
];
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Sends random ok message.",
|
||||
async run($) {
|
||||
$.channel.send(`ok ${random(responses)}`);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(`ok ${random(responses)}`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import Command from "../../core/command";
|
||||
import {getContent} from "../../core/lib";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {getContent} from "../../lib";
|
||||
import {URL} from "url";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "OwO-ifies the input.",
|
||||
async run($) {
|
||||
let url = new URL(`https://nekos.life/api/v2/owoify?text=${$.args.join(" ")}`);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
let url = new URL(`https://nekos.life/api/v2/owoify?text=${args.join(" ")}`);
|
||||
const content = (await getContent(url.toString())) as any; // Apparently, the object in question is {owo: string}.
|
||||
$.channel.send(content.owo);
|
||||
channel.send(content.owo);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
import {MessageEmbed} from "discord.js";
|
||||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Create a poll.",
|
||||
usage: "<question>",
|
||||
run: "Please provide a question.",
|
||||
any: new Command({
|
||||
description: "Question for the poll.",
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const embed = new MessageEmbed()
|
||||
.setAuthor(
|
||||
`Poll created by ${$.message.author.username}`,
|
||||
$.message.guild?.iconURL({dynamic: true}) ?? undefined
|
||||
`Poll created by ${message.author.username}`,
|
||||
message.guild?.iconURL({dynamic: true}) ?? undefined
|
||||
)
|
||||
.setColor(0xffffff)
|
||||
.setFooter("React to vote.")
|
||||
.setDescription($.args.join(" "));
|
||||
const msg = await $.channel.send(embed);
|
||||
.setDescription(args.join(" "));
|
||||
const msg = await channel.send(embed);
|
||||
await msg.react("✅");
|
||||
await msg.react("⛔");
|
||||
$.message.delete({
|
||||
message.delete({
|
||||
timeout: 1000
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import Command, {handler} from "../../core/command";
|
||||
import {clean} from "../../core/lib";
|
||||
import {botHasPermission} from "../../core/libd";
|
||||
import {Config, Storage} from "../../core/structures";
|
||||
import {getPermissionLevel, getPermissionName} from "../../core/permissions";
|
||||
import {Command, NamedCommand, botHasPermission, getPermissionLevel, getPermissionName} from "../../core";
|
||||
import {clean} from "../../lib";
|
||||
import {Config, Storage} from "../../structures";
|
||||
import {Permissions} from "discord.js";
|
||||
import {logs} from "../../modules/globals";
|
||||
|
||||
|
@ -20,59 +18,55 @@ function getLogBuffer(type: string) {
|
|||
const activities = ["playing", "listening", "streaming", "watching"];
|
||||
const statuses = ["online", "idle", "dnd", "invisible"];
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description:
|
||||
"An all-in-one command to do admin stuff. You need to be either an admin of the server or one of the bot's mechanics to use this command.",
|
||||
async run($) {
|
||||
if (!$.member) {
|
||||
$.channel.send("Couldn't find a member object for you! Did you make sure you used this in a server?");
|
||||
return;
|
||||
}
|
||||
const permLevel = getPermissionLevel($.member);
|
||||
$.channel.send(
|
||||
`${$.author.toString()}, your permission level is \`${getPermissionName(permLevel)}\` (${permLevel}).`
|
||||
);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
if (!member)
|
||||
return channel.send("Couldn't find a member object for you! Did you make sure you used this in a server?");
|
||||
const permLevel = getPermissionLevel(author, member);
|
||||
return channel.send(`${author}, your permission level is \`${getPermissionName(permLevel)}\` (${permLevel}).`);
|
||||
},
|
||||
subcommands: {
|
||||
set: new Command({
|
||||
set: new NamedCommand({
|
||||
description: "Set different per-guild settings for the bot.",
|
||||
run: "You have to specify the option you want to set.",
|
||||
permission: PERMISSIONS.ADMIN,
|
||||
subcommands: {
|
||||
prefix: new Command({
|
||||
prefix: new NamedCommand({
|
||||
description: "Set a custom prefix for your guild. Removes your custom prefix if none is provided.",
|
||||
usage: "(<prefix>)",
|
||||
async run($) {
|
||||
Storage.getGuild($.guild?.id || "N/A").prefix = null;
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
Storage.getGuild(guild?.id || "N/A").prefix = null;
|
||||
Storage.save();
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
`The custom prefix for this guild has been removed. My prefix is now back to \`${Config.prefix}\`.`
|
||||
);
|
||||
},
|
||||
any: new Command({
|
||||
async run($) {
|
||||
Storage.getGuild($.guild?.id || "N/A").prefix = $.args[0];
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
Storage.getGuild(guild?.id || "N/A").prefix = args[0];
|
||||
Storage.save();
|
||||
$.channel.send(`The custom prefix for this guild is now \`${$.args[0]}\`.`);
|
||||
channel.send(`The custom prefix for this guild is now \`${args[0]}\`.`);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}),
|
||||
diag: new Command({
|
||||
diag: new NamedCommand({
|
||||
description: 'Requests a debug log with the "info" verbosity level.',
|
||||
permission: PERMISSIONS.BOT_SUPPORT,
|
||||
async run($) {
|
||||
$.channel.send(getLogBuffer("info"));
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(getLogBuffer("info"));
|
||||
},
|
||||
any: new Command({
|
||||
description: `Select a verbosity to listen to. Available levels: \`[${Object.keys(logs).join(", ")}]\``,
|
||||
async run($) {
|
||||
const type = $.args[0];
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const type = args[0];
|
||||
|
||||
if (type in logs) $.channel.send(getLogBuffer(type));
|
||||
if (type in logs) channel.send(getLogBuffer(type));
|
||||
else
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
`Couldn't find a verbosity level named \`${type}\`! The available types are \`[${Object.keys(
|
||||
logs
|
||||
).join(", ")}]\`.`
|
||||
|
@ -80,75 +74,72 @@ export default new Command({
|
|||
}
|
||||
})
|
||||
}),
|
||||
status: new Command({
|
||||
status: new NamedCommand({
|
||||
description: "Changes the bot's status.",
|
||||
permission: PERMISSIONS.BOT_SUPPORT,
|
||||
async run($) {
|
||||
$.channel.send("Setting status to `online`...");
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send("Setting status to `online`...");
|
||||
},
|
||||
any: new Command({
|
||||
description: `Select a status to set to. Available statuses: \`[${statuses.join(", ")}]\`.`,
|
||||
async run($) {
|
||||
if (!statuses.includes($.args[0])) {
|
||||
$.channel.send("That status doesn't exist!");
|
||||
return;
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
if (!statuses.includes(args[0])) {
|
||||
return channel.send("That status doesn't exist!");
|
||||
} else {
|
||||
$.client.user?.setStatus($.args[0]);
|
||||
$.channel.send(`Setting status to \`${$.args[0]}\`...`);
|
||||
client.user?.setStatus(args[0]);
|
||||
return channel.send(`Setting status to \`${args[0]}\`...`);
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
purge: new Command({
|
||||
purge: new NamedCommand({
|
||||
description: "Purges the bot's own messages.",
|
||||
permission: PERMISSIONS.BOT_SUPPORT,
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// It's probably better to go through the bot's own messages instead of calling bulkDelete which requires MANAGE_MESSAGES.
|
||||
if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES) && $.channel.type !== "dm") {
|
||||
$.message.delete();
|
||||
const msgs = await $.channel.messages.fetch({
|
||||
if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES) && channel.type !== "dm") {
|
||||
message.delete();
|
||||
const msgs = await channel.messages.fetch({
|
||||
limit: 100
|
||||
});
|
||||
const travMessages = msgs.filter((m) => m.author.id === $.client.user?.id);
|
||||
const travMessages = msgs.filter((m) => m.author.id === client.user?.id);
|
||||
|
||||
await $.channel.send(`Found ${travMessages.size} messages to delete.`).then((m) =>
|
||||
await channel.send(`Found ${travMessages.size} messages to delete.`).then((m) =>
|
||||
m.delete({
|
||||
timeout: 5000
|
||||
})
|
||||
);
|
||||
await $.channel.bulkDelete(travMessages);
|
||||
await channel.bulkDelete(travMessages);
|
||||
} else {
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
"This command must be executed in a guild where I have the `MANAGE_MESSAGES` permission."
|
||||
);
|
||||
}
|
||||
}
|
||||
}),
|
||||
clear: new Command({
|
||||
clear: new NamedCommand({
|
||||
description: "Clears a given amount of messages.",
|
||||
usage: "<amount>",
|
||||
run: "A number was not provided.",
|
||||
number: new Command({
|
||||
description: "Amount of messages to delete.",
|
||||
async run($) {
|
||||
if ($.channel.type === "dm") {
|
||||
await $.channel.send("Can't clear messages in the DMs!");
|
||||
return;
|
||||
}
|
||||
$.message.delete();
|
||||
const fetched = await $.channel.messages.fetch({
|
||||
limit: $.args[0]
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
if (channel.type === "dm") return channel.send("Can't clear messages in the DMs!");
|
||||
message.delete();
|
||||
const fetched = await channel.messages.fetch({
|
||||
limit: args[0]
|
||||
});
|
||||
await $.channel.bulkDelete(fetched);
|
||||
await channel.bulkDelete(fetched);
|
||||
return;
|
||||
}
|
||||
})
|
||||
}),
|
||||
eval: new Command({
|
||||
eval: new NamedCommand({
|
||||
description: "Evaluate code.",
|
||||
usage: "<code>",
|
||||
permission: PERMISSIONS.BOT_OWNER,
|
||||
// You have to bring everything into scope to use them. AFAIK, there isn't a more maintainable way to do this, but at least TS will let you know if anything gets removed.
|
||||
async run({args, author, channel, client, guild, member, message}) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
try {
|
||||
const code = args.join(" ");
|
||||
let evaled = eval(code);
|
||||
|
@ -160,49 +151,46 @@ export default new Command({
|
|||
}
|
||||
}
|
||||
}),
|
||||
nick: new Command({
|
||||
nick: new NamedCommand({
|
||||
description: "Change the bot's nickname.",
|
||||
permission: PERMISSIONS.BOT_SUPPORT,
|
||||
async run($) {
|
||||
const nickName = $.args.join(" ");
|
||||
await $.guild?.me?.setNickname(nickName);
|
||||
if (botHasPermission($.guild, Permissions.FLAGS.MANAGE_MESSAGES))
|
||||
$.message.delete({timeout: 5000}).catch(handler.bind($));
|
||||
$.channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000}));
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const nickName = args.join(" ");
|
||||
await guild?.me?.setNickname(nickName);
|
||||
if (botHasPermission(guild, Permissions.FLAGS.MANAGE_MESSAGES)) message.delete({timeout: 5000});
|
||||
channel.send(`Nickname set to \`${nickName}\``).then((m) => m.delete({timeout: 5000}));
|
||||
}
|
||||
}),
|
||||
guilds: new Command({
|
||||
guilds: new NamedCommand({
|
||||
description: "Shows a list of all guilds the bot is a member of.",
|
||||
permission: PERMISSIONS.BOT_SUPPORT,
|
||||
async run($) {
|
||||
const guildList = $.client.guilds.cache.array().map((e) => e.name);
|
||||
$.channel.send(guildList, {split: true});
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const guildList = client.guilds.cache.array().map((e) => e.name);
|
||||
channel.send(guildList, {split: true});
|
||||
}
|
||||
}),
|
||||
activity: new Command({
|
||||
activity: new NamedCommand({
|
||||
description: "Set the activity of the bot.",
|
||||
permission: PERMISSIONS.BOT_SUPPORT,
|
||||
usage: "<type> <string>",
|
||||
async run($) {
|
||||
$.client.user?.setActivity(".help", {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
client.user?.setActivity(".help", {
|
||||
type: "LISTENING"
|
||||
});
|
||||
$.channel.send("Activity set to default.");
|
||||
channel.send("Activity set to default.");
|
||||
},
|
||||
any: new Command({
|
||||
description: `Select an activity type to set. Available levels: \`[${activities.join(", ")}]\``,
|
||||
async run($) {
|
||||
const type = $.args[0];
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const type = args[0];
|
||||
|
||||
if (activities.includes(type)) {
|
||||
$.client.user?.setActivity($.args.slice(1).join(" "), {
|
||||
type: $.args[0].toUpperCase()
|
||||
client.user?.setActivity(args.slice(1).join(" "), {
|
||||
type: args[0].toUpperCase()
|
||||
});
|
||||
$.channel.send(
|
||||
`Set activity to \`${$.args[0].toUpperCase()}\` \`${$.args.slice(1).join(" ")}\`.`
|
||||
);
|
||||
channel.send(`Set activity to \`${args[0].toUpperCase()}\` \`${args.slice(1).join(" ")}\`.`);
|
||||
} else
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
`Couldn't find an activity type named \`${type}\`! The available types are \`[${activities.join(
|
||||
", "
|
||||
)}]\`.`
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import {Command, NamedCommand} from "../../core/command";
|
||||
import {toTitleCase} from "../../core/lib";
|
||||
import {loadableCommands, categories} from "../../core/loader";
|
||||
import {getPermissionName} from "../../core/permissions";
|
||||
import {Command, NamedCommand, loadableCommands, categories, getPermissionName} from "../../core";
|
||||
import {toTitleCase} from "../../lib";
|
||||
|
||||
export default new NamedCommand({
|
||||
description: "Lists all commands. If a command is specified, their arguments are listed as well.",
|
||||
usage: "([command, [subcommand/type], ...])",
|
||||
aliases: ["h"],
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const commands = await loadableCommands;
|
||||
let output = `Legend: \`<type>\`, \`[list/of/stuff]\`, \`(optional)\`, \`(<optional type>)\`, \`([optional/list/...])\``;
|
||||
|
||||
|
@ -22,17 +20,17 @@ export default new NamedCommand({
|
|||
}
|
||||
}
|
||||
|
||||
$.channel.send(output, {split: true});
|
||||
channel.send(output, {split: true});
|
||||
},
|
||||
any: new Command({
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// Setup the root command
|
||||
const commands = await loadableCommands;
|
||||
let header = $.args.shift() as string;
|
||||
let header = args.shift() as string;
|
||||
let command = commands.get(header);
|
||||
if (!command || header === "test") return $.channel.send(`No command found by the name \`${header}\`.`);
|
||||
if (!command || header === "test") return channel.send(`No command found by the name \`${header}\`.`);
|
||||
if (!(command instanceof NamedCommand))
|
||||
return $.channel.send(`Command is not a proper instance of NamedCommand.`);
|
||||
return channel.send(`Command is not a proper instance of NamedCommand.`);
|
||||
if (command.name) header = command.name;
|
||||
|
||||
// Search categories
|
||||
|
@ -45,9 +43,9 @@ export default new NamedCommand({
|
|||
}
|
||||
|
||||
// Gather info
|
||||
const result = await command.resolveInfo($.args);
|
||||
const result = await command.resolveInfo(args);
|
||||
|
||||
if (result.type === "error") return $.channel.send(result.message);
|
||||
if (result.type === "error") return channel.send(result.message);
|
||||
|
||||
let append = "";
|
||||
command = result.command;
|
||||
|
@ -79,7 +77,7 @@ export default new NamedCommand({
|
|||
aliases = formattedAliases.join(", ") || "None";
|
||||
}
|
||||
|
||||
return $.channel.send(
|
||||
return channel.send(
|
||||
`Command: \`${header}\`\nAliases: ${aliases}\nCategory: \`${category}\`\nPermission Required: \`${getPermissionName(
|
||||
result.permission
|
||||
)}\` (${result.permission})\nDescription: ${command.description}\n${append}`,
|
||||
|
|
|
@ -1,56 +1,7 @@
|
|||
import Command from "../core/command";
|
||||
import {Command, NamedCommand} from "../core";
|
||||
|
||||
export default new Command({
|
||||
description:
|
||||
'This is a template/testing command providing common functionality. Remove what you don\'t need, and rename/delete this file to generate a fresh command file here. This command should be automatically excluded from the help command. The "usage" parameter (string) overrides the default usage for the help command. The "endpoint" parameter (boolean) prevents further arguments from being passed. Also, as long as you keep the run function async, it\'ll return a promise allowing the program to automatically catch any synchronous errors. However, you\'ll have to do manual error handling if you go the then and catch route.',
|
||||
endpoint: false,
|
||||
usage: "",
|
||||
permission: -1,
|
||||
aliases: [],
|
||||
async run($) {
|
||||
// code
|
||||
},
|
||||
subcommands: {
|
||||
layer: new Command({
|
||||
description:
|
||||
'This is a named subcommand, meaning that the key name is what determines the keyword to use. With default settings for example, "$test layer".',
|
||||
endpoint: false,
|
||||
usage: "",
|
||||
permission: -1,
|
||||
aliases: [],
|
||||
async run($) {
|
||||
export default new NamedCommand({
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// code
|
||||
}
|
||||
})
|
||||
},
|
||||
user: new Command({
|
||||
description:
|
||||
'This is the subcommand for getting users by pinging them or copying their ID. With default settings for example, "$test 237359961842253835". The argument will be a user object and won\'t run if no user is found by that ID.',
|
||||
endpoint: false,
|
||||
usage: "",
|
||||
permission: -1,
|
||||
async run($) {
|
||||
// code
|
||||
}
|
||||
}),
|
||||
number: new Command({
|
||||
description:
|
||||
'This is a numeric subcommand, meaning that any type of number (excluding Infinity/NaN) will route to this command if present. With default settings for example, "$test -5.2". The argument with the number is already parsed so you can just use it without converting it.',
|
||||
endpoint: false,
|
||||
usage: "",
|
||||
permission: -1,
|
||||
async run($) {
|
||||
// code
|
||||
}
|
||||
}),
|
||||
any: new Command({
|
||||
description:
|
||||
"This is a generic subcommand, meaning that if there isn't a more specific subcommand that's called, it falls to this. With default settings for example, \"$test reeee\".",
|
||||
endpoint: false,
|
||||
usage: "",
|
||||
permission: -1,
|
||||
async run($) {
|
||||
// code
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Renames current voice channel.",
|
||||
usage: "<name>",
|
||||
async run($) {
|
||||
const voiceChannel = $.message.member?.voice.channel;
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const voiceChannel = message.member?.voice.channel;
|
||||
|
||||
if (!voiceChannel) {
|
||||
$.channel.send("You are not in a voice channel.");
|
||||
channel.send("You are not in a voice channel.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!voiceChannel.guild.me?.hasPermission("MANAGE_CHANNELS")) {
|
||||
$.channel.send("I am lacking the required permissions to perform this action.");
|
||||
channel.send("I am lacking the required permissions to perform this action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($.args.length === 0) {
|
||||
$.channel.send("Please provide a new voice channel name.");
|
||||
if (args.length === 0) {
|
||||
channel.send("Please provide a new voice channel name.");
|
||||
return;
|
||||
}
|
||||
|
||||
const prevName = voiceChannel.name;
|
||||
const newName = $.args.join(" ");
|
||||
const newName = args.join(" ");
|
||||
await voiceChannel.setName(newName);
|
||||
await $.channel.send(`Changed channel name from "${prevName}" to "${newName}".`);
|
||||
await channel.send(`Changed channel name from "${prevName}" to "${newName}".`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {queryClosestEmoteByName} from "./modules/emote-utils";
|
||||
import {botHasPermission} from "../../core/libd";
|
||||
import {Permissions} from "discord.js";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Send the specified emote.",
|
||||
run: "Please provide a command name.",
|
||||
any: new Command({
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
import {MessageEmbed, version as djsversion} from "discord.js";
|
||||
import {MessageEmbed, version as djsversion, Guild} from "discord.js";
|
||||
import ms from "ms";
|
||||
import os from "os";
|
||||
import Command from "../../core/command";
|
||||
import {formatBytes, trimArray} from "../../core/lib";
|
||||
import {getMemberByUsername} from "../../core/libd";
|
||||
import {Command, NamedCommand, getMemberByUsername} from "../../core";
|
||||
import {formatBytes, trimArray} from "../../lib";
|
||||
import {verificationLevels, filterLevels, regions} from "../../defs/info";
|
||||
import moment from "moment";
|
||||
import utc from "moment";
|
||||
import {Guild} from "discord.js";
|
||||
import moment, {utc} from "moment";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Command to provide all sorts of info about the current server, a user, etc.",
|
||||
run: "Please provide an argument.\nFor help, run `%prefix%help info`.",
|
||||
subcommands: {
|
||||
avatar: new Command({
|
||||
avatar: new NamedCommand({
|
||||
description: "Shows your own, or another user's avatar.",
|
||||
usage: "(<user>)",
|
||||
async run($) {
|
||||
$.channel.send($.author.displayAvatarURL({dynamic: true, size: 2048}));
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(author.displayAvatarURL({dynamic: true, size: 2048}));
|
||||
},
|
||||
user: new Command({
|
||||
description: "Shows your own, or another user's avatar.",
|
||||
async run($) {
|
||||
$.channel.send(
|
||||
$.args[0].displayAvatarURL({
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(
|
||||
args[0].displayAvatarURL({
|
||||
dynamic: true,
|
||||
size: 2048
|
||||
})
|
||||
|
@ -32,39 +29,39 @@ export default new Command({
|
|||
}),
|
||||
any: new Command({
|
||||
description: "Shows another user's avatar by searching their name",
|
||||
async run($) {
|
||||
if ($.guild) {
|
||||
const name = $.args.join(" ");
|
||||
const member = await getMemberByUsername($.guild, name);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
if (guild) {
|
||||
const name = args.join(" ");
|
||||
const member = await getMemberByUsername(guild, name);
|
||||
|
||||
if (member) {
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
member.user.displayAvatarURL({
|
||||
dynamic: true,
|
||||
size: 2048
|
||||
})
|
||||
);
|
||||
} else {
|
||||
$.channel.send(`No user found by the name \`${name}\`!`);
|
||||
channel.send(`No user found by the name \`${name}\`!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
bot: new Command({
|
||||
bot: new NamedCommand({
|
||||
description: "Displays info about the bot.",
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
const core = os.cpus()[0];
|
||||
const embed = new MessageEmbed()
|
||||
.setColor($.guild?.me?.displayHexColor || "BLUE")
|
||||
.setColor(guild?.me?.displayHexColor || "BLUE")
|
||||
.addField("General", [
|
||||
`**❯ Client:** ${$.client.user?.tag} (${$.client.user?.id})`,
|
||||
`**❯ Servers:** ${$.client.guilds.cache.size.toLocaleString()}`,
|
||||
`**❯ Users:** ${$.client.guilds.cache
|
||||
`**❯ Client:** ${client.user?.tag} (${client.user?.id})`,
|
||||
`**❯ Servers:** ${client.guilds.cache.size.toLocaleString()}`,
|
||||
`**❯ Users:** ${client.guilds.cache
|
||||
.reduce((a: any, b: {memberCount: any}) => a + b.memberCount, 0)
|
||||
.toLocaleString()}`,
|
||||
`**❯ Channels:** ${$.client.channels.cache.size.toLocaleString()}`,
|
||||
`**❯ Creation Date:** ${utc($.client.user?.createdTimestamp).format("Do MMMM YYYY HH:mm:ss")}`,
|
||||
`**❯ Channels:** ${client.channels.cache.size.toLocaleString()}`,
|
||||
`**❯ Creation Date:** ${utc(client.user?.createdTimestamp).format("Do MMMM YYYY HH:mm:ss")}`,
|
||||
`**❯ Node.JS:** ${process.version}`,
|
||||
`**❯ Version:** v${process.env.npm_package_version}`,
|
||||
`**❯ Discord.JS:** ${djsversion}`,
|
||||
|
@ -84,45 +81,45 @@ export default new Command({
|
|||
`\u3000 • Used: ${formatBytes(process.memoryUsage().heapUsed)}`
|
||||
])
|
||||
.setTimestamp();
|
||||
const avatarURL = $.client.user?.displayAvatarURL({
|
||||
const avatarURL = client.user?.displayAvatarURL({
|
||||
dynamic: true,
|
||||
size: 2048
|
||||
});
|
||||
if (avatarURL) embed.setThumbnail(avatarURL);
|
||||
$.channel.send(embed);
|
||||
channel.send(embed);
|
||||
}
|
||||
}),
|
||||
guild: new Command({
|
||||
guild: new NamedCommand({
|
||||
description: "Displays info about the current guild or another guild.",
|
||||
usage: "(<guild name>/<guild ID>)",
|
||||
async run($) {
|
||||
if ($.guild) {
|
||||
$.channel.send(await getGuildInfo($.guild, $.guild));
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
if (guild) {
|
||||
channel.send(await getGuildInfo(guild, guild));
|
||||
} else {
|
||||
$.channel.send("Please execute this command in a guild.");
|
||||
channel.send("Please execute this command in a guild.");
|
||||
}
|
||||
},
|
||||
any: new Command({
|
||||
description: "Display info about a guild by finding its name or ID.",
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// If a guild ID is provided (avoid the "number" subcommand because of inaccuracies), search for that guild
|
||||
if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) {
|
||||
const id = $.args[0];
|
||||
const guild = $.client.guilds.cache.get(id);
|
||||
if (args.length === 1 && /^\d{17,19}$/.test(args[0])) {
|
||||
const id = args[0];
|
||||
const guild = client.guilds.cache.get(id);
|
||||
|
||||
if (guild) {
|
||||
$.channel.send(await getGuildInfo(guild, $.guild));
|
||||
channel.send(await getGuildInfo(guild, guild));
|
||||
} else {
|
||||
$.channel.send(`None of the servers I'm in matches the guild ID \`${id}\`!`);
|
||||
channel.send(`None of the servers I'm in matches the guild ID \`${id}\`!`);
|
||||
}
|
||||
} else {
|
||||
const query: string = $.args.join(" ").toLowerCase();
|
||||
const guild = $.client.guilds.cache.find((guild) => guild.name.toLowerCase().includes(query));
|
||||
const query: string = args.join(" ").toLowerCase();
|
||||
const guild = client.guilds.cache.find((guild) => guild.name.toLowerCase().includes(query));
|
||||
|
||||
if (guild) {
|
||||
$.channel.send(await getGuildInfo(guild, $.guild));
|
||||
channel.send(await getGuildInfo(guild, guild));
|
||||
} else {
|
||||
$.channel.send(`None of the servers I'm in matches the query \`${query}\`!`);
|
||||
channel.send(`None of the servers I'm in matches the query \`${query}\`!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,12 +128,12 @@ export default new Command({
|
|||
},
|
||||
user: new Command({
|
||||
description: "Displays info about mentioned user.",
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, client, args}) {
|
||||
// Transforms the User object into a GuildMember object of the current guild.
|
||||
const member = await $.guild?.members.fetch($.args[0]);
|
||||
const member = await guild?.members.fetch(args[0]);
|
||||
|
||||
if (!member) {
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
"No member object was found by that user! Are you sure you used this command in a server?"
|
||||
);
|
||||
return;
|
||||
|
@ -166,16 +163,14 @@ export default new Command({
|
|||
`**❯ Game:** ${member.user.presence.activities || "Not playing a game."}`
|
||||
])
|
||||
.addField("Member", [
|
||||
`**❯ Highest Role:** ${
|
||||
member.roles.highest.id === $.guild?.id ? "None" : member.roles.highest.name
|
||||
}`,
|
||||
`**❯ Highest Role:** ${member.roles.highest.id === guild?.id ? "None" : member.roles.highest.name}`,
|
||||
`**❯ Server Join Date:** ${moment(member.joinedAt).format("LL LTS")}`,
|
||||
`**❯ Hoist Role:** ${member.roles.hoist ? member.roles.hoist.name : "None"}`,
|
||||
`**❯ Roles:** [${roles.length}]: ${
|
||||
roles.length == 0 ? "None" : roles.length <= 10 ? roles.join(", ") : trimArray(roles).join(", ")
|
||||
}`
|
||||
]);
|
||||
$.channel.send(embed);
|
||||
channel.send(embed);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,44 +1,38 @@
|
|||
import {GuildEmoji} from "discord.js";
|
||||
import {MessageEmbed} from "discord.js";
|
||||
import Command from "../../core/command";
|
||||
import {split} from "../../core/lib";
|
||||
import {paginate} from "../../core/libd";
|
||||
import {GuildEmoji, MessageEmbed, TextChannel, DMChannel, NewsChannel, User} from "discord.js";
|
||||
import {Command, NamedCommand, paginate} from "../../core";
|
||||
import {split} from "../../lib";
|
||||
import vm from "vm";
|
||||
import {TextChannel} from "discord.js";
|
||||
import {DMChannel} from "discord.js";
|
||||
import {NewsChannel} from "discord.js";
|
||||
import {User} from "discord.js";
|
||||
|
||||
const REGEX_TIMEOUT_MS = 1000;
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Lists all emotes the bot has in it's registry,",
|
||||
usage: "<regex pattern> (-flags)",
|
||||
async run($) {
|
||||
displayEmoteList($.client.emojis.cache.array(), $.channel, $.author);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
displayEmoteList(client.emojis.cache.array(), channel, author);
|
||||
},
|
||||
any: new Command({
|
||||
description:
|
||||
"Filters emotes by via a regular expression. Flags can be added by adding a dash at the end. For example, to do a case-insensitive search, do %prefix%lsemotes somepattern -i",
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
// If a guild ID is provided, filter all emotes by that guild (but only if there aren't any arguments afterward)
|
||||
if ($.args.length === 1 && /^\d{17,19}$/.test($.args[0])) {
|
||||
const guildID: string = $.args[0];
|
||||
if (args.length === 1 && /^\d{17,19}$/.test(args[0])) {
|
||||
const guildID: string = args[0];
|
||||
|
||||
displayEmoteList(
|
||||
$.client.emojis.cache.filter((emote) => emote.guild.id === guildID).array(),
|
||||
$.channel,
|
||||
$.author
|
||||
client.emojis.cache.filter((emote) => emote.guild.id === guildID).array(),
|
||||
channel,
|
||||
author
|
||||
);
|
||||
} else {
|
||||
// Otherwise, search via a regex pattern
|
||||
let flags: string | undefined = undefined;
|
||||
|
||||
if (/^-[dgimsuy]{1,7}$/.test($.args[$.args.length - 1])) {
|
||||
flags = $.args.pop().substring(1);
|
||||
if (/^-[dgimsuy]{1,7}$/.test(args[args.length - 1])) {
|
||||
flags = args.pop().substring(1);
|
||||
}
|
||||
|
||||
let emoteCollection = $.client.emojis.cache.array();
|
||||
let emoteCollection = client.emojis.cache.array();
|
||||
// Creates a sandbox to stop a regular expression if it takes too much time to search.
|
||||
// To avoid passing in a giant data structure, I'll just pass in the structure {[id: string]: [name: string]}.
|
||||
//let emotes: {[id: string]: string} = {};
|
||||
|
@ -50,7 +44,7 @@ export default new Command({
|
|||
|
||||
// The result will be sandbox.emotes because it'll be modified in-place.
|
||||
const sandbox = {
|
||||
regex: new RegExp($.args.join(" "), flags),
|
||||
regex: new RegExp(args.join(" "), flags),
|
||||
emotes
|
||||
};
|
||||
const context = vm.createContext(sandbox);
|
||||
|
@ -64,10 +58,10 @@ export default new Command({
|
|||
script.runInContext(context, {timeout: REGEX_TIMEOUT_MS});
|
||||
emotes = sandbox.emotes;
|
||||
emoteCollection = emoteCollection.filter((emote) => emotes.has(emote.id)); // Only allow emotes that haven't been deleted.
|
||||
displayEmoteList(emoteCollection, $.channel, $.author);
|
||||
displayEmoteList(emoteCollection, channel, author);
|
||||
} catch (error) {
|
||||
if (error.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
`The regular expression you entered exceeded the time limit of ${REGEX_TIMEOUT_MS} milliseconds.`
|
||||
);
|
||||
} else {
|
||||
|
@ -75,7 +69,7 @@ export default new Command({
|
|||
}
|
||||
}
|
||||
} else {
|
||||
$.channel.send("Failed to initialize sandbox.");
|
||||
channel.send("Failed to initialize sandbox.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {Message, Channel, TextChannel} from "discord.js";
|
||||
import {queryClosestEmoteByName} from "./modules/emote-utils";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description:
|
||||
"Reacts to the a previous message in your place. You have to react with the same emote before the bot removes that reaction.",
|
||||
usage: 'react <emotes...> (<distance / message ID / "Copy ID" / "Copy Message Link">)',
|
||||
async run($) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
let target: Message | undefined;
|
||||
let distance = 1;
|
||||
|
||||
if ($.args.length >= 2) {
|
||||
const last = $.args[$.args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator.
|
||||
if (args.length >= 2) {
|
||||
const last = args[args.length - 1]; // Because this is optional, do not .pop() unless you're sure it's a message link indicator.
|
||||
const URLPattern = /^(?:https:\/\/discord.com\/channels\/(\d{17,19})\/(\d{17,19})\/(\d{17,19}))$/;
|
||||
const copyIDPattern = /^(?:(\d{17,19})-(\d{17,19}))$/;
|
||||
|
||||
|
@ -21,66 +21,65 @@ export default new Command({
|
|||
const guildID = match[1];
|
||||
const channelID = match[2];
|
||||
const messageID = match[3];
|
||||
let guild = $.guild;
|
||||
let channel: Channel | undefined = $.channel;
|
||||
let tmpChannel: Channel | undefined = channel;
|
||||
|
||||
if (guild?.id !== guildID) {
|
||||
try {
|
||||
guild = await $.client.guilds.fetch(guildID);
|
||||
guild = await client.guilds.fetch(guildID);
|
||||
} catch {
|
||||
return $.channel.send(`\`${guildID}\` is an invalid guild ID!`);
|
||||
return channel.send(`\`${guildID}\` is an invalid guild ID!`);
|
||||
}
|
||||
}
|
||||
|
||||
if (channel.id !== channelID) channel = guild.channels.cache.get(channelID);
|
||||
if (!channel) return $.channel.send(`\`${channelID}\` is an invalid channel ID!`);
|
||||
if (tmpChannel.id !== channelID) tmpChannel = guild.channels.cache.get(channelID);
|
||||
if (!tmpChannel) return channel.send(`\`${channelID}\` is an invalid channel ID!`);
|
||||
|
||||
if ($.message.id !== messageID) {
|
||||
if (message.id !== messageID) {
|
||||
try {
|
||||
target = await (channel as TextChannel).messages.fetch(messageID);
|
||||
target = await (tmpChannel as TextChannel).messages.fetch(messageID);
|
||||
} catch {
|
||||
return $.channel.send(`\`${messageID}\` is an invalid message ID!`);
|
||||
return channel.send(`\`${messageID}\` is an invalid message ID!`);
|
||||
}
|
||||
}
|
||||
|
||||
$.args.pop();
|
||||
args.pop();
|
||||
}
|
||||
// <Channel ID>-<Message ID> ("Copy ID" Button)
|
||||
else if (copyIDPattern.test(last)) {
|
||||
const match = copyIDPattern.exec(last)!;
|
||||
const channelID = match[1];
|
||||
const messageID = match[2];
|
||||
let channel: Channel | undefined = $.channel;
|
||||
let tmpChannel: Channel | undefined = channel;
|
||||
|
||||
if (channel.id !== channelID) channel = $.guild?.channels.cache.get(channelID);
|
||||
if (!channel) return $.channel.send(`\`${channelID}\` is an invalid channel ID!`);
|
||||
if (tmpChannel.id !== channelID) tmpChannel = guild?.channels.cache.get(channelID);
|
||||
if (!tmpChannel) return channel.send(`\`${channelID}\` is an invalid channel ID!`);
|
||||
|
||||
if ($.message.id !== messageID) {
|
||||
if (message.id !== messageID) {
|
||||
try {
|
||||
target = await (channel as TextChannel).messages.fetch(messageID);
|
||||
target = await (tmpChannel as TextChannel).messages.fetch(messageID);
|
||||
} catch {
|
||||
return $.channel.send(`\`${messageID}\` is an invalid message ID!`);
|
||||
return channel.send(`\`${messageID}\` is an invalid message ID!`);
|
||||
}
|
||||
}
|
||||
|
||||
$.args.pop();
|
||||
args.pop();
|
||||
}
|
||||
// <Message ID>
|
||||
else if (/^\d{17,19}$/.test(last)) {
|
||||
try {
|
||||
target = await $.channel.messages.fetch(last);
|
||||
target = await channel.messages.fetch(last);
|
||||
} catch {
|
||||
return $.channel.send(`No valid message found by the ID \`${last}\`!`);
|
||||
return channel.send(`No valid message found by the ID \`${last}\`!`);
|
||||
}
|
||||
|
||||
$.args.pop();
|
||||
args.pop();
|
||||
}
|
||||
// The entire string has to be a number for this to match. Prevents leaCheeseAmerican1 from triggering this.
|
||||
else if (/^\d+$/.test(last)) {
|
||||
distance = parseInt(last);
|
||||
|
||||
if (distance >= 0 && distance <= 99) $.args.pop();
|
||||
else return $.channel.send("Your distance must be between 0 and 99!");
|
||||
if (distance >= 0 && distance <= 99) args.pop();
|
||||
else return channel.send("Your distance must be between 0 and 99!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,13 +87,13 @@ export default new Command({
|
|||
// Messages are ordered from latest to earliest.
|
||||
// You also have to add 1 as well because fetchMessages includes your own message.
|
||||
target = (
|
||||
await $.message.channel.messages.fetch({
|
||||
await message.channel.messages.fetch({
|
||||
limit: distance + 1
|
||||
})
|
||||
).last();
|
||||
}
|
||||
|
||||
for (const search of $.args) {
|
||||
for (const search of args) {
|
||||
// Even though the bot will always grab *some* emote, the user can choose not to keep that emote there if it isn't what they want
|
||||
const emote = queryClosestEmoteByName(search);
|
||||
const reaction = await target!.react(emote);
|
||||
|
@ -102,7 +101,7 @@ export default new Command({
|
|||
// This part is called with a promise because you don't want to wait 5 seconds between each reaction.
|
||||
setTimeout(() => {
|
||||
// This reason for this null assertion is that by the time you use this command, the client is going to be loaded.
|
||||
reaction.users.remove($.client.user!);
|
||||
reaction.users.remove(client.user!);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Repeats your message.",
|
||||
usage: "<message>",
|
||||
run: "Please provide a message for me to say!",
|
||||
any: new Command({
|
||||
description: "Message to repeat.",
|
||||
async run($) {
|
||||
$.channel.send(`*${$.author} says:*\n${$.args.join(" ")}`);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
channel.send(`*${author} says:*\n${args.join(" ")}`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
import Command, {handler} from "../../core/command";
|
||||
import {pluralise} from "../../core/lib";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import {pluralise} from "../../lib";
|
||||
import moment from "moment";
|
||||
import {Collection, TextChannel} from "discord.js";
|
||||
|
||||
const lastUsedTimestamps: {[id: string]: number} = {};
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description:
|
||||
"Scans all text channels in the current guild and returns the number of times each emoji specific to the guild has been used. Has a cooldown of 24 hours per guild.",
|
||||
async run($) {
|
||||
if (!$.guild) {
|
||||
$.channel.send(`You must use this command on a server!`);
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
if (!guild) {
|
||||
channel.send(`You must use this command on a server!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test if the command is on cooldown. This isn't the strictest cooldown possible, because in the event that the bot crashes, the cooldown will be reset. But for all intends and purposes, it's a good enough cooldown. It's a per-server cooldown.
|
||||
const startTime = Date.now();
|
||||
const cooldown = 86400000; // 24 hours
|
||||
const lastUsedTimestamp = lastUsedTimestamps[$.guild.id] ?? 0;
|
||||
const lastUsedTimestamp = lastUsedTimestamps[guild.id] ?? 0;
|
||||
const difference = startTime - lastUsedTimestamp;
|
||||
const howLong = moment(startTime).to(lastUsedTimestamp + cooldown);
|
||||
|
||||
// If it's been less than an hour since the command was last used, prevent it from executing.
|
||||
if (difference < cooldown) {
|
||||
$.channel.send(
|
||||
channel.send(
|
||||
`This command requires a day to cooldown. You'll be able to activate this command ${howLong}.`
|
||||
);
|
||||
return;
|
||||
} else lastUsedTimestamps[$.guild.id] = startTime;
|
||||
} else lastUsedTimestamps[guild.id] = startTime;
|
||||
|
||||
const stats: {
|
||||
[id: string]: {
|
||||
|
@ -39,20 +39,20 @@ export default new Command({
|
|||
} = {};
|
||||
let totalUserEmoteUsage = 0;
|
||||
// IMPORTANT: You MUST check if the bot actually has access to the channel in the first place. It will get the list of all channels, but that doesn't mean it has access to every channel. Without this, it'll require admin access and throw an annoying unhelpful DiscordAPIError: Missing Access otherwise.
|
||||
const allTextChannelsInCurrentGuild = $.guild.channels.cache.filter(
|
||||
const allTextChannelsInCurrentGuild = guild.channels.cache.filter(
|
||||
(channel) => channel.type === "text" && channel.viewable
|
||||
) as Collection<string, TextChannel>;
|
||||
let messagesSearched = 0;
|
||||
let channelsSearched = 0;
|
||||
let currentChannelName = "";
|
||||
const totalChannels = allTextChannelsInCurrentGuild.size;
|
||||
const statusMessage = await $.channel.send("Gathering emotes...");
|
||||
const statusMessage = await channel.send("Gathering emotes...");
|
||||
let warnings = 0;
|
||||
$.channel.startTyping();
|
||||
channel.startTyping();
|
||||
|
||||
// Initialize the emote stats object with every emote in the current guild.
|
||||
// The goal here is to cut the need to access guild.emojis.get() which'll make it faster and easier to work with.
|
||||
for (let emote of $.guild.emojis.cache.values()) {
|
||||
for (let emote of guild.emojis.cache.values()) {
|
||||
// If you don't include the "a" for animated emotes, it'll not only not show up, but also cause all other emotes in the same message to not show up. The emote name is self-correcting but it's better to keep the right value since it'll be used to calculate message lengths that fit.
|
||||
stats[emote.id] = {
|
||||
name: emote.name,
|
||||
|
@ -70,7 +70,7 @@ export default new Command({
|
|||
|
||||
for (const channel of allTextChannelsInCurrentGuild.values()) {
|
||||
currentChannelName = channel.name;
|
||||
let selected = channel.lastMessageID ?? $.message.id;
|
||||
let selected = channel.lastMessageID ?? message.id;
|
||||
let continueLoop = true;
|
||||
|
||||
while (continueLoop) {
|
||||
|
@ -167,7 +167,7 @@ export default new Command({
|
|||
)}.`
|
||||
);
|
||||
console.log(`Finished operation in ${finishTime - startTime} ms.`);
|
||||
$.channel.stopTyping();
|
||||
channel.stopTyping();
|
||||
|
||||
// Display stats on emote usage.
|
||||
// This can work outside the loop now that it's synchronous, and now it's clearer what code is meant to execute at the end.
|
||||
|
@ -186,6 +186,6 @@ export default new Command({
|
|||
);
|
||||
}
|
||||
|
||||
$.channel.send(lines, {split: true}).catch(handler.bind($));
|
||||
await channel.send(lines, {split: true});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import Command from "../../core/command";
|
||||
import {Command, NamedCommand} from "../../core";
|
||||
import * as https from "https";
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Shortens a given URL.",
|
||||
run: "Please provide a URL.",
|
||||
any: new Command({
|
||||
async run($) {
|
||||
https.get("https://is.gd/create.php?format=simple&url=" + encodeURIComponent($.args[0]), function (res) {
|
||||
async run({message, channel, guild, author, member, client, args}) {
|
||||
https.get("https://is.gd/create.php?format=simple&url=" + encodeURIComponent(args[0]), function (res) {
|
||||
var body = "";
|
||||
res.on("data", function (chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
res.on("end", function () {
|
||||
$.channel.send(`<${body}>`);
|
||||
channel.send(`<${body}>`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Command from "../../core/command";
|
||||
import {ask, askYesOrNo, askMultipleChoice, prompt, callMemberByUsername} from "../../core/libd";
|
||||
import {Storage} from "../../core/structures";
|
||||
import {Command, NamedCommand, ask, askYesOrNo, askMultipleChoice, prompt, callMemberByUsername} from "../../core";
|
||||
import {Storage} from "../../structures";
|
||||
import {User} from "discord.js";
|
||||
import moment from "moment";
|
||||
|
||||
|
@ -167,7 +166,7 @@ function getTimeEmbed(user: User) {
|
|||
return embed;
|
||||
}
|
||||
|
||||
export default new Command({
|
||||
export default new NamedCommand({
|
||||
description: "Show others what time it is for you.",
|
||||
aliases: ["tz"],
|
||||
async run({channel, author}) {
|
||||
|
@ -175,7 +174,7 @@ export default new Command({
|
|||
},
|
||||
subcommands: {
|
||||
// Welcome to callback hell. We hope you enjoy your stay here!
|
||||
setup: new Command({
|
||||
setup: new NamedCommand({
|
||||
description: "Registers your timezone information for the bot.",
|
||||
async run({author, channel}) {
|
||||
const profile = Storage.getUser(author.id);
|
||||
|
@ -327,7 +326,7 @@ export default new Command({
|
|||
);
|
||||
}
|
||||
}),
|
||||
delete: new Command({
|
||||
delete: new NamedCommand({
|
||||
description: "Delete your timezone information.",
|
||||
async run({channel, author}) {
|
||||
prompt(
|
||||
|
@ -344,7 +343,7 @@ export default new Command({
|
|||
);
|
||||
}
|
||||
}),
|
||||
utc: new Command({
|
||||
utc: new NamedCommand({
|
||||
description: "Displays UTC time.",
|
||||
async run({channel}) {
|
||||
const time = moment().utc();
|
||||
|
@ -370,7 +369,7 @@ export default new Command({
|
|||
});
|
||||
}
|
||||
}),
|
||||
daylight: new Command({
|
||||
daylight: new NamedCommand({
|
||||
description: "Provides information on the daylight savings region",
|
||||
run: DST_NOTE_INFO
|
||||
})
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import {parseVars} from "./lib";
|
||||
import {Collection} from "discord.js";
|
||||
import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember, GuildChannel} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
import {parseVars} from "../lib";
|
||||
import {
|
||||
Collection,
|
||||
Client,
|
||||
Message,
|
||||
TextChannel,
|
||||
DMChannel,
|
||||
NewsChannel,
|
||||
Guild,
|
||||
User,
|
||||
GuildMember,
|
||||
GuildChannel
|
||||
} from "discord.js";
|
||||
import {getPrefix} from "../structures";
|
||||
import {SingleMessageOptions} from "./libd";
|
||||
import {hasPermission, getPermissionLevel, getPermissionName} from "./permissions";
|
||||
import {client} from "../index";
|
||||
|
||||
export enum TYPES {
|
||||
SUBCOMMAND,
|
||||
USER,
|
||||
NUMBER,
|
||||
ANY,
|
||||
NONE
|
||||
}
|
||||
|
||||
// RegEx patterns used for identifying/extracting each type from a string argument.
|
||||
const patterns = {
|
||||
channel: /^<#(\d{17,19})>$/,
|
||||
|
@ -30,7 +32,7 @@ const patterns = {
|
|||
// Therefore, there won't by any type narrowing on channel or guild of CommandMenu until this is fixed.
|
||||
// Otherwise, you'd have to define channelType for every single subcommand, which would get very tedious.
|
||||
// Just use type assertions when you specify a channel type.
|
||||
export enum CHANNEL_TYPE {
|
||||
enum CHANNEL_TYPE {
|
||||
ANY,
|
||||
GUILD,
|
||||
DM
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import {client} from "../index";
|
||||
import {loadableCommands} from "./loader";
|
||||
import {Permissions, Message} from "discord.js";
|
||||
import {getPrefix} from "./structures";
|
||||
import {Config} from "./structures";
|
||||
import {getPrefix} from "../structures";
|
||||
import {defaultMetadata} from "./command";
|
||||
|
||||
// For custom message events that want to cancel the command handler on certain conditions.
|
||||
|
@ -99,13 +98,3 @@ client.on("message", async (message) => {
|
|||
);
|
||||
}
|
||||
});
|
||||
|
||||
client.once("ready", () => {
|
||||
if (client.user) {
|
||||
console.ready(`Logged in as ${client.user.tag}.`);
|
||||
client.user.setActivity({
|
||||
type: "LISTENING",
|
||||
name: `${Config.prefix}help`
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
15
src/core/index.ts
Normal file
15
src/core/index.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export {Command, NamedCommand} from "./command";
|
||||
export {addInterceptRule} from "./handler";
|
||||
export {
|
||||
SingleMessageOptions,
|
||||
botHasPermission,
|
||||
paginate,
|
||||
prompt,
|
||||
ask,
|
||||
askYesOrNo,
|
||||
askMultipleChoice,
|
||||
getMemberByUsername,
|
||||
callMemberByUsername
|
||||
} from "./libd";
|
||||
export {loadableCommands, categories} from "./loader";
|
||||
export {hasPermission, getPermissionLevel, getPermissionName} from "./permissions";
|
|
@ -1,5 +1,5 @@
|
|||
import {User, GuildMember, Permissions} from "discord.js";
|
||||
import {Config} from "./structures";
|
||||
import {Config} from "../structures";
|
||||
|
||||
interface PermissionLevel {
|
||||
name: string;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import "./modules/globals";
|
||||
import {Client} from "discord.js";
|
||||
import setup from "./modules/setup";
|
||||
import {Config} from "./core/structures";
|
||||
import {Config} from "./structures";
|
||||
|
||||
// This is here in order to make it much less of a headache to access the client from other files.
|
||||
// This of course won't actually do anything until the setup process is complete and it logs in.
|
||||
|
@ -16,6 +16,7 @@ setup.init().then(() => {
|
|||
// Initialize Modules //
|
||||
import "./core/handler"; // Command loading will start as soon as an instance of "core/command" is loaded, which is loaded in "core/handler".
|
||||
import "./core/eventListeners";
|
||||
import "./modules/ready";
|
||||
import "./modules/presence";
|
||||
import "./modules/lavalink";
|
||||
import "./modules/emoteRegistry";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Library for pure functions
|
||||
import {get} from "https";
|
||||
import FileManager from "./storage";
|
||||
import FileManager from "./modules/storage";
|
||||
|
||||
/**
|
||||
* Splits a command by spaces while accounting for quotes which capture string arguments.
|
|
@ -1,6 +1,6 @@
|
|||
import {client} from "../index";
|
||||
import FileManager from "../core/storage";
|
||||
import {EmoteRegistryDump} from "../core/structures";
|
||||
import FileManager from "./storage";
|
||||
import {EmoteRegistryDump} from "../structures";
|
||||
|
||||
function updateGlobalEmoteRegistry(): void {
|
||||
const data: EmoteRegistryDump = {version: 1, list: []};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import attachClientToLavalink from "discord.js-lavalink-lib";
|
||||
import {Config} from "../core/structures";
|
||||
import {Config} from "../structures";
|
||||
import {client} from "../index";
|
||||
|
||||
// Although the example showed to do "client.music = LavaLink(...)" and "(client as any).music = Lavalink(...)" was done to match that, nowhere in the library is client.music ever actually used nor does the function return anything. In other words, client.music is undefined and is never used.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {client} from "../index";
|
||||
import {TextChannel, APIMessage, MessageEmbed} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
import {getPrefix} from "../structures";
|
||||
import {DiscordAPIError} from "discord.js";
|
||||
|
||||
client.on("message", async (message) => {
|
||||
|
|
12
src/modules/ready.ts
Normal file
12
src/modules/ready.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import {client} from "../index";
|
||||
import {Config} from "../structures";
|
||||
|
||||
client.once("ready", () => {
|
||||
if (client.user) {
|
||||
console.ready(`Logged in as ${client.user.tag}.`);
|
||||
client.user.setActivity({
|
||||
type: "LISTENING",
|
||||
name: `${Config.prefix}help`
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import {existsSync as exists, readFileSync as read, writeFile as write} from "fs";
|
||||
import inquirer from "inquirer";
|
||||
import Storage, {generateHandler} from "../core/storage";
|
||||
import {Config} from "../core/structures";
|
||||
import Storage, {generateHandler} from "./storage";
|
||||
import {Config} from "../structures";
|
||||
|
||||
// The template should be built with a reductionist mentality.
|
||||
// Provide everything the user needs and then let them remove whatever they want.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import FileManager from "./storage";
|
||||
import FileManager from "./modules/storage";
|
||||
import {select, GenericJSON, GenericStructure} from "./lib";
|
||||
import {watch} from "fs";
|
||||
import {Guild as DiscordGuild, Snowflake} from "discord.js";
|
Loading…
Reference in a new issue