mirror of
https://github.com/arthur-pbty/xiao.git
synced 2026-06-03 15:07:42 +02:00
Framework Rewrite
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
require('dotenv').config();
|
||||
const { XIAO_TOKEN, OWNERS, XIAO_PREFIX, INVITE, APRIL_FOOLS } = process.env;
|
||||
const { XIAO_TOKEN, OWNERS, XIAO_PREFIX, INVITE } = process.env;
|
||||
const { mkdir } = require('fs/promises');
|
||||
const path = require('path');
|
||||
const { Intents, Permissions, SystemChannelFlags, MessageEmbed } = require('discord.js');
|
||||
const { Intents, MessageEmbed } = require('discord.js');
|
||||
const Client = require('./structures/Client');
|
||||
const client = new Client({
|
||||
commandPrefix: XIAO_PREFIX,
|
||||
@@ -16,7 +16,6 @@ const client = new Client({
|
||||
intents: [Intents.NON_PRIVILEGED, Intents.FLAGS.GUILD_MEMBERS]
|
||||
});
|
||||
const { formatNumber, checkFileExists } = require('./util/Util');
|
||||
const aprilFoolsMsgs = require('./assets/json/april-fools');
|
||||
|
||||
client.registry
|
||||
.registerDefaultTypes()
|
||||
@@ -52,13 +51,6 @@ client.registry
|
||||
['roleplay', 'Roleplay'],
|
||||
['other', 'Other']
|
||||
])
|
||||
.registerDefaultCommands({
|
||||
help: false,
|
||||
ping: false,
|
||||
prefix: false,
|
||||
commandState: false,
|
||||
unknownCommand: false
|
||||
})
|
||||
.registerCommandsIn(path.join(__dirname, 'commands'));
|
||||
|
||||
client.on('ready', async () => {
|
||||
@@ -248,7 +240,7 @@ client.on('message', async msg => {
|
||||
const hasEmbed = msg.embeds.length !== 0;
|
||||
if (msg.author.bot || (!hasText && !hasImage && !hasEmbed)) return;
|
||||
if (client.blacklist.user.includes(msg.author.id)) return;
|
||||
if (msg.isCommand && msg.channel.type !== 'dm') return;
|
||||
if (client.dispatcher.isCommand(msg) && msg.channel.type !== 'dm') return;
|
||||
if (client.games.has(msg.channel.id)) return;
|
||||
|
||||
// Cleverbot handler
|
||||
@@ -340,8 +332,8 @@ client.on('guildDelete', async guild => {
|
||||
client.on('guildMemberRemove', async member => {
|
||||
if (member.id === client.user.id) return null;
|
||||
const channel = member.guild.systemChannel;
|
||||
if (!channel || !channel.permissionsFor(client.user).has(Permissions.FLAGS.SEND_MESSAGES)) return null;
|
||||
if (member.guild.systemChannelFlags.has(SystemChannelFlags.FLAGS.SUPPRESS_JOIN_NOTIFICATIONS)) return null;
|
||||
if (!channel || !channel.permissionsFor(client.user).has('SEND_MESSAGES')) return null;
|
||||
if (member.guild.systemChannelFlags.has('SUPPRESS_JOIN_NOTIFICATIONS')) return null;
|
||||
if (channel.topic && channel.topic.includes('<xiao:disable-leave>')) return null;
|
||||
try {
|
||||
const leaveMessage = client.leaveMessages[Math.floor(Math.random() * client.leaveMessages.length)];
|
||||
@@ -387,30 +379,11 @@ client.on('warn', warn => client.logger.warn(warn));
|
||||
client.on('commandRun', async command => {
|
||||
if (command.unknown) return;
|
||||
client.logger.info(`[COMMAND] ${command.name} was used.`);
|
||||
if (command.uses === undefined) return;
|
||||
command.uses++;
|
||||
if (command.lastRun === undefined) return;
|
||||
command.lastRun = new Date();
|
||||
const channel = await client.fetchCommandChannel();
|
||||
channel.send(`\`${command.name}\` was used! It has now been used **${formatNumber(command.uses)}** times!`)
|
||||
.catch(() => null);
|
||||
});
|
||||
|
||||
client.dispatcher.addInhibitor(msg => {
|
||||
if (client.blacklist.user.includes(msg.author.id)) return 'blacklisted';
|
||||
if (msg.guild && client.blacklist.guild.includes(msg.guild.id)) return 'blacklisted';
|
||||
return false;
|
||||
});
|
||||
|
||||
if (APRIL_FOOLS) {
|
||||
client.dispatcher.addInhibitor(msg => {
|
||||
if (client.isOwner(msg.author)) return false;
|
||||
const random = Math.floor(Math.random() * 2);
|
||||
if (random === 1) return msg.reply(aprilFoolsMsgs[Math.floor(Math.random() * aprilFoolsMsgs.length)]);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
client.on('commandError', (command, err) => client.logger.error(`[COMMAND:${command.name}]\n${err.stack}`));
|
||||
|
||||
client.login(XIAO_TOKEN);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[
|
||||
"You don't command me!",
|
||||
"Sorry loser, I don't listen to idiots.",
|
||||
"I take orders from no one!",
|
||||
"Hahaha look at you, trying to use a command.",
|
||||
"You wish you could use a bot like me.",
|
||||
"I'm too cute to take orders from you.",
|
||||
"You? _You_? As if I'd do anything for _you_.",
|
||||
"I've got better things to do.",
|
||||
"Go away.",
|
||||
"Keep trying. Maybe one day it'll work.",
|
||||
"Nope, sorry, I'm not listening.",
|
||||
"La la la la la, I'm not listening!",
|
||||
"Eat pant.",
|
||||
"Command? What's that? Is it tasty?",
|
||||
"Go do something more productive with your life."
|
||||
]
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"ADMINISTRATOR": "",
|
||||
"CREATE_INSTANT_INVITE": "",
|
||||
"KICK_MEMBERS": "",
|
||||
"BAN_MEMBERS": "",
|
||||
"MANAGE_CHANNELS": "",
|
||||
"MANAGE_GUILD": "",
|
||||
"ADD_REACTIONS": "",
|
||||
"VIEW_AUDIT_LOG": "",
|
||||
"PRIORITY_SPEAKER": "",
|
||||
"STREAM": "",
|
||||
"VIEW_CHANNEL": "",
|
||||
"SEND_MESSAGES": "",
|
||||
"SEND_TTS_MESSAGES": "",
|
||||
"MANAGE_MESSAGES": "",
|
||||
"EMBED_LINKS": "",
|
||||
"ATTACH_FILES": "",
|
||||
"READ_MESSAGE_HISTORY": "",
|
||||
"MENTION_EVERYONE": "",
|
||||
"USE_EXTERNAL_EMOJIS": "",
|
||||
"VIEW_GUILD_INSIGHTS": "",
|
||||
"CONNECT": "",
|
||||
"SPEAK": "",
|
||||
"MUTE_MEMBERS": "",
|
||||
"DEAFEN_MEMBERS": "",
|
||||
"MOVE_MEMBERS": "",
|
||||
"USE_VAD": "",
|
||||
"CHANGE_NICKNAME": "",
|
||||
"MANAGE_NICKNAMES": "",
|
||||
"MANAGE_ROLES": "",
|
||||
"MANAGE_WEBHOOKS": "",
|
||||
"MANAGE_EMOJIS": "",
|
||||
"USE_APPLICATION_COMMANDS": "",
|
||||
"REQUEST_TO_SPEAK": ""
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
const Command = require('../../structures/commands/AutoReply');
|
||||
|
||||
module.exports = class NoUCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'no-u',
|
||||
aliases: ['no-you'],
|
||||
group: 'auto',
|
||||
memberName: 'no-u',
|
||||
description: 'no u',
|
||||
patterns: [/^n+o+ u+$/i]
|
||||
});
|
||||
}
|
||||
|
||||
generateText(fromPattern) {
|
||||
if (!fromPattern) return 'no u';
|
||||
const chance = Boolean(Math.floor(Math.random() * 2));
|
||||
if (chance) return null;
|
||||
return 'no u';
|
||||
}
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
const Command = require('../../structures/commands/AutoReply');
|
||||
|
||||
module.exports = class UnflipCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'unflip',
|
||||
group: 'auto',
|
||||
memberName: 'unflip',
|
||||
description: 'Unflips a flipped table.',
|
||||
patterns: [/\(╯°□°)╯︵ ┻━┻/i]
|
||||
});
|
||||
}
|
||||
|
||||
generateText() {
|
||||
return '┬─┬ ノ( ゜-゜ノ)';
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
const Command = require('../../structures/Command');
|
||||
const moment = require('moment');
|
||||
const { MessageEmbed } = require('discord.js');
|
||||
const { util: { permissions } } = require('discord.js-commando');
|
||||
const permissions = require('../../assets/json/permission-names');
|
||||
|
||||
module.exports = class RoleCommand extends Command {
|
||||
constructor(client) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const Command = require('../../structures/Command');
|
||||
const { MessageEmbed } = require('discord.js');
|
||||
const { util: { permissions } } = require('discord.js-commando');
|
||||
const permissions = require('../../assets/json/permission-names');
|
||||
const { stripIndents } = require('common-tags');
|
||||
|
||||
module.exports = class HelpCommand extends Command {
|
||||
@@ -31,7 +31,7 @@ module.exports = class HelpCommand extends Command {
|
||||
const embed = new MessageEmbed()
|
||||
.setTitle(`Command List (Page ${i + 1})`)
|
||||
.setDescription(stripIndents`
|
||||
To run a command, use ${msg.anyUsage('<command>')}.
|
||||
To run a command, use ${this.usage()}.
|
||||
${nsfw ? '' : 'Use in an NSFW channel to see NSFW commands.'}
|
||||
`)
|
||||
.setColor(0x00AE86);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const Command = require('../../structures/Command');
|
||||
const { MessageEmbed, version: djsVersion } = require('discord.js');
|
||||
const { version: commandoVersion } = require('discord.js-commando');
|
||||
const moment = require('moment');
|
||||
require('moment-duration-format');
|
||||
const { formatNumber, embedURL } = require('../../util/Util');
|
||||
@@ -39,7 +38,7 @@ module.exports = class InfoCommand extends Command {
|
||||
.addField('❯ Version', `v${version}`, true)
|
||||
.addField('❯ Node.js', process.version, true)
|
||||
.addField('❯ Discord.js', `v${djsVersion}`, true)
|
||||
.addField('❯ Commando', `v${commandoVersion}`, true)
|
||||
.addField('❯ Framework', 'Custom', true)
|
||||
.addField('❯ Dependencies', Object.keys(deps).sort().join(', '));
|
||||
return msg.embed(embed);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const Command = require('../../structures/Command');
|
||||
const { stripIndents } = require('common-tags');
|
||||
|
||||
module.exports = class PrefixCommand extends Command {
|
||||
constructor(client) {
|
||||
@@ -14,9 +13,6 @@ module.exports = class PrefixCommand extends Command {
|
||||
|
||||
run(msg) {
|
||||
const prefix = msg.guild ? msg.guild.commandPrefix : this.client.commandPrefix;
|
||||
return msg.reply(stripIndents`
|
||||
${prefix ? `The command prefix is \`${prefix}\`.` : 'There is no command prefix.'}
|
||||
To run a command, use ${msg.anyUsage('<command>')}.
|
||||
`);
|
||||
return msg.reply(prefix ? `The command prefix is \`${prefix}\`.` : 'There is no command prefix.');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ module.exports = class UnknownCommandCommand extends Command {
|
||||
run(msg) {
|
||||
if (!msg.guild) return null;
|
||||
const commands = this.makeCommandArray(this.client.isOwner(msg.author), msg.channel.nsfw);
|
||||
const command = msg.content.match(this.client.dispatcher._commandPatterns[this.client.commandPrefix]);
|
||||
const command = msg.content.match(this.client.dispatcher.commandPattern);
|
||||
const str = command ? command[2] : msg.content.split(' ')[0];
|
||||
const results = didYouMean(str, commands, { returnType: ReturnTypeEnums.ALL_SORTED_MATCHES });
|
||||
return msg.reply(stripIndents`
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// Credit: https://github.com/discordjs/Commando/blob/master/src/commands/util/eval.js
|
||||
const util = require('util');
|
||||
const discord = require('discord.js');
|
||||
const tags = require('common-tags');
|
||||
const { escapeRegex } = require('../../util/Util');
|
||||
const Command = require('../../structures/Command');
|
||||
|
||||
const nl = '!!NL!!';
|
||||
const nlPattern = new RegExp(nl, 'g');
|
||||
|
||||
module.exports = class EvalCommand extends Command {
|
||||
constructor(client) {
|
||||
super(client, {
|
||||
name: 'eval',
|
||||
group: 'util',
|
||||
memberName: 'eval',
|
||||
description: 'Executes JavaScript code.',
|
||||
details: 'Only the bot owner(s) may use this command.',
|
||||
ownerOnly: true,
|
||||
args: [
|
||||
{
|
||||
key: 'script',
|
||||
prompt: 'What code would you like to evaluate?',
|
||||
type: 'string'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.lastResult = null;
|
||||
Object.defineProperty(this, '_sensitivePattern', { value: null, configurable: true });
|
||||
}
|
||||
|
||||
run(msg, args) {
|
||||
// Make a bunch of helpers
|
||||
/* eslint-disable no-unused-vars */
|
||||
const message = msg;
|
||||
const client = msg.client;
|
||||
const lastResult = this.lastResult;
|
||||
const doReply = val => {
|
||||
if (val instanceof Error) {
|
||||
msg.reply(`Callback error: \`${val}\``);
|
||||
} else {
|
||||
const result = this.makeResultMessages(val, process.hrtime(this.hrStart));
|
||||
if (Array.isArray(result)) {
|
||||
for(const item of result) msg.reply(item);
|
||||
} else {
|
||||
msg.reply(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
// Remove any surrounding code blocks before evaluation
|
||||
if (args.script.startsWith('```') && args.script.endsWith('```')) {
|
||||
args.script = args.script.replace(/(^.*?\s)|(\n.*$)/g, '');
|
||||
}
|
||||
|
||||
// Run the code and measure its execution time
|
||||
let hrDiff;
|
||||
try {
|
||||
const hrStart = process.hrtime();
|
||||
this.lastResult = eval(args.script);
|
||||
hrDiff = process.hrtime(hrStart);
|
||||
} catch (err) {
|
||||
return msg.reply(`Error while evaluating: \`${err}\``);
|
||||
}
|
||||
|
||||
// Prepare for callback time and respond
|
||||
this.hrStart = process.hrtime();
|
||||
const result = this.makeResultMessages(this.lastResult, hrDiff, args.script);
|
||||
if (Array.isArray(result)) {
|
||||
return result.map(item => msg.reply(item));
|
||||
} else {
|
||||
return msg.reply(result);
|
||||
}
|
||||
}
|
||||
|
||||
makeResultMessages(result, hrDiff, input = null) {
|
||||
const inspected = util.inspect(result, { depth: 0 })
|
||||
.replace(nlPattern, '\n')
|
||||
.replace(this.sensitivePattern, '--snip--');
|
||||
const split = inspected.split('\n');
|
||||
const last = inspected.length - 1;
|
||||
const prependPart = inspected[0] !== '{' && inspected[0] !== '[' && inspected[0] !== "'" ? split[0] : inspected[0];
|
||||
const appendPart = inspected[last] !== '}' && inspected[last] !== ']' && inspected[last] !== "'" ?
|
||||
split[split.length - 1] :
|
||||
inspected[last];
|
||||
const prepend = `\`\`\`javascript\n${prependPart}\n`;
|
||||
const append = `\n${appendPart}\n\`\`\``;
|
||||
if (input) {
|
||||
return discord.splitMessage(tags.stripIndents`
|
||||
*Executed in ${hrDiff[0] > 0 ? `${hrDiff[0]}s ` : ''}${hrDiff[1] / 1000000}ms.*
|
||||
\`\`\`javascript
|
||||
${inspected}
|
||||
\`\`\`
|
||||
`, { maxLength: 1900, prepend, append });
|
||||
} else {
|
||||
return discord.splitMessage(tags.stripIndents`
|
||||
*Callback executed after ${hrDiff[0] > 0 ? `${hrDiff[0]}s ` : ''}${hrDiff[1] / 1000000}ms.*
|
||||
\`\`\`javascript
|
||||
${inspected}
|
||||
\`\`\`
|
||||
`, { maxLength: 1900, prepend, append });
|
||||
}
|
||||
}
|
||||
|
||||
get sensitivePattern() {
|
||||
if (!this._sensitivePattern) {
|
||||
const client = this.client;
|
||||
let pattern = '';
|
||||
if (client.token) pattern += escapeRegex(client.token);
|
||||
Object.defineProperty(this, '_sensitivePattern', { value: new RegExp(pattern, 'gi'), configurable: false });
|
||||
}
|
||||
return this._sensitivePattern;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
const UnionType = require('./UnionType');
|
||||
|
||||
module.exports = class Argument {
|
||||
constructor(client, options) {
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this.key = options.key.toLowerCase();
|
||||
this.typeID = options.type.toLowerCase();
|
||||
this.min = options.min;
|
||||
this.max = options.max;
|
||||
this.oneOf = options.oneOf;
|
||||
this.default = options.default;
|
||||
this.avatarSize = options.avatarSize || 2048;
|
||||
}
|
||||
|
||||
get type() {
|
||||
if (this.typeID.includes('|')) return new UnionType(this.client, this.typeID);
|
||||
return this.client.registry.types.get(this.typeID);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
module.exports = class ArgumentType {
|
||||
constructor(id) {
|
||||
this.id = id.toLowerCase();
|
||||
}
|
||||
|
||||
validate(val) {
|
||||
return Boolean(val);
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
isEmpty(val) {
|
||||
return !val;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,228 @@
|
||||
const { Client } = require('discord.js');
|
||||
const fs = require('fs');
|
||||
const { stripIndents } = require('common-tags');
|
||||
const Registry = require('./Registry');
|
||||
const Dispatcher = require('./Dispatcher');
|
||||
const Patreon = require('../structures/Patreon');
|
||||
require('./Extensions');
|
||||
|
||||
module.exports = class CommandClient extends Client {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.commandPrefix = options.commandPrefix;
|
||||
this.owner = typeof options.owner === 'string' ? [options.owner] : options.owner;
|
||||
this.invite = options.invite || null;
|
||||
this.registry = new Registry(this);
|
||||
this.dispatcher = new Dispatcher(this);
|
||||
this.patreon = new Patreon();
|
||||
this.blacklist = { user: [], guild: [] };
|
||||
this._throttlingTimeouts = new Map();
|
||||
|
||||
this.once('ready', this.onceReady);
|
||||
this.on('message', this.onMessage);
|
||||
}
|
||||
|
||||
isOwner(user) {
|
||||
return this.owners.includes(user.id);
|
||||
}
|
||||
|
||||
async onceReady() {
|
||||
for (const owner of this.owner) {
|
||||
await this.users.fetch(owner);
|
||||
}
|
||||
}
|
||||
|
||||
async onMessage(msg) {
|
||||
if (!msg.author) return;
|
||||
|
||||
if (msg.channel.partial) msg.channel = await this.channels.fetch(msg.channel.id);
|
||||
if (msg.partial) msg = await msg.channel.messages.fetch(msg.id);
|
||||
if (msg.member.partial || !msg.guild.members.cache.has(msg.author.id)) {
|
||||
await msg.guild.members.fetch(msg.author.id);
|
||||
}
|
||||
|
||||
if (msg.author.bot) return;
|
||||
if (this.blacklist.user.includes(msg.author.id)) return;
|
||||
if (msg.guild && this.blacklist.guild.includes(msg.guild.id)) return;
|
||||
if (!msg.channel.permissionsFor(this.user).has('SEND_MESSAGES')) return;
|
||||
if (!this.dispatcher.isCommand(msg)) return;
|
||||
|
||||
const parsed = await this.dispatcher.parseMessage(msg);
|
||||
if (typeof parsed === 'string') {
|
||||
const helpUsage = this.registry.commands.get('help').usage();
|
||||
await msg.reply(`${parsed} Use ${helpUsage} for more information.`);
|
||||
return;
|
||||
}
|
||||
const { command, args } = parsed;
|
||||
if (!command._enabled) {
|
||||
await msg.reply(`The \`${command.name}\` command is disabled.`);
|
||||
return;
|
||||
}
|
||||
if (command.ownerOnly && !this.isOwner(msg.author)) {
|
||||
await msg.reply(`The \`${command.name}\` command can only be used by the bot owner.`);
|
||||
return;
|
||||
}
|
||||
if (command.guildOnly && !msg.guild) {
|
||||
await msg.reply(`The \`${command.name}\` command can only be used in a server channel.`);
|
||||
return;
|
||||
}
|
||||
if (command.nsfw && !msg.channel.nsfw) {
|
||||
await msg.reply(`The \`${command.name}\` command can only be used in NSFW channels.`);
|
||||
return;
|
||||
}
|
||||
if (command.patronOnly && !this.patreon.isPatron(msg.author.id)) {
|
||||
await msg.reply(stripIndents`
|
||||
The \`${command.name}\` command can only be used by Patrons.
|
||||
Visit <https://www.patreon.com/xiaodiscord> to sign-up!
|
||||
`);
|
||||
return;
|
||||
}
|
||||
if (command.clientPermissions.length) {
|
||||
for (const permission of command.clientPermissions) {
|
||||
if (msg.channel.permissionsFor(this.user).has(permission)) continue;
|
||||
await msg.reply(`The \`${command.name}\` command requires me to have the "${permission}" permission.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (command.userPermissions.length) {
|
||||
for (const permission of command.userPermissions) {
|
||||
if (msg.channel.permissionsFor(msg.author).has(permission)) continue;
|
||||
await msg.reply(`You need the "${permission}" permission to use the \`${command.name}\` command.`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const throttleAmount = command.throttles.get(msg.author.id) || 0;
|
||||
if (throttleAmount >= command.throttling.uses) {
|
||||
const timeout = command._timeouts.get(msg.author.id);
|
||||
await msg.reply(`Please wait ${getTimeLeft(timeout)} seconds before using the \`${command.name}\` command again.`);
|
||||
return;
|
||||
}
|
||||
command.throttles.set(msg.author.id, throttleAmount + 1);
|
||||
if (!throttleAmount) {
|
||||
const timeout = setTimeout(() => command.throttles.delete(msg.author.id), command.throttling.duration);
|
||||
command._timeouts.set(msg.author.id, timeout);
|
||||
}
|
||||
try {
|
||||
const result = await command.run(msg, args);
|
||||
command.uses++;
|
||||
command.lastRun = new Date();
|
||||
this.emit('commandRun', command, result, msg, args);
|
||||
} catch (err) {
|
||||
this.emit('commandError', command, err, msg, args);
|
||||
await msg.reply(stripIndents`
|
||||
An error occurred while running this command: \`${err.message}\`.
|
||||
You shouldn't ever recieve an error like this.
|
||||
${this.invite ? `Please visit ${this.invite} for support.` : ''}
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
importBlacklist() {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'blacklist.json'), { encoding: 'utf8' });
|
||||
const file = JSON.parse(read);
|
||||
if (typeof file !== 'object' || Array.isArray(file)) return null;
|
||||
if (!file.guild || !file.user) return null;
|
||||
for (const id of file.guild) {
|
||||
if (typeof id !== 'string') continue;
|
||||
if (this.blacklist.guild.includes(id)) continue;
|
||||
this.blacklist.guild.push(id);
|
||||
}
|
||||
for (const id of file.user) {
|
||||
if (typeof id !== 'string') continue;
|
||||
if (this.blacklist.user.includes(id)) continue;
|
||||
this.blacklist.user.push(id);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportBlacklist() {
|
||||
let text = '{\n "guild": [\n ';
|
||||
if (this.blacklist.guild.length) {
|
||||
for (const id of this.blacklist.guild) {
|
||||
text += `"${id}",\n `;
|
||||
}
|
||||
text = text.slice(0, -4);
|
||||
}
|
||||
text += '\n ],\n "user": [\n ';
|
||||
if (this.blacklist.user.length) {
|
||||
for (const id of this.blacklist.user) {
|
||||
text += `"${id}",\n `;
|
||||
}
|
||||
text = text.slice(0, -4);
|
||||
}
|
||||
text += '\n ]\n}\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'blacklist.json'), buf, { encoding: 'utf8' });
|
||||
return buf;
|
||||
}
|
||||
|
||||
importCommandLeaderboard(add = false) {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'command-leaderboard.json'), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
const file = JSON.parse(read);
|
||||
if (typeof file !== 'object' || Array.isArray(file)) return null;
|
||||
for (const [id, value] of Object.entries(file)) {
|
||||
if (typeof value !== 'number') continue;
|
||||
const found = this.registry.commands.get(id);
|
||||
if (!found || found.uses === undefined) continue;
|
||||
if (add) found.uses += value;
|
||||
else found.uses = value;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportCommandLeaderboard() {
|
||||
let text = '{';
|
||||
for (const command of this.registry.commands.values()) {
|
||||
if (command.unknown) continue;
|
||||
if (command.uses === undefined) continue;
|
||||
text += `\n "${command.name}": ${command.uses},`;
|
||||
}
|
||||
text = text.slice(0, -1);
|
||||
text += '\n}\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'command-leaderboard.json'), buf, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
return buf;
|
||||
}
|
||||
|
||||
importLastRun() {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'command-last-run.json'), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
const file = JSON.parse(read);
|
||||
if (typeof file !== 'object' || Array.isArray(file)) return null;
|
||||
for (const [id, value] of Object.entries(file)) {
|
||||
if (!value) continue;
|
||||
const date = new Date(value);
|
||||
if (date.toString() === 'Invalid Date') continue;
|
||||
const found = this.registry.commands.get(id);
|
||||
if (!found || found.lastRun === undefined) continue;
|
||||
found.lastRun = date;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportLastRun() {
|
||||
let text = '{';
|
||||
for (const command of this.registry.commands.values()) {
|
||||
if (command.unknown) continue;
|
||||
if (command.lastRun === undefined) continue;
|
||||
text += `\n "${command.name}": ${command.lastRun ? `"${command.lastRun.toISOString()}"` : null},`;
|
||||
}
|
||||
text = text.slice(0, -1);
|
||||
text += '\n}\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'command-last-run.json'), buf, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
|
||||
function getTimeLeft(timeout) {
|
||||
return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
const Argument = require('./Argument');
|
||||
|
||||
module.exports = class Command {
|
||||
constructor(client, options) {
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this.name = options.name.toLowerCase();
|
||||
this.aliases = options.aliases ? options.aliases.map(alias => alias.toLowerCase()) : [];
|
||||
this.groupID = options.group.toLowerCase();
|
||||
this.memberName = options.memberName.toLowerCase();
|
||||
this.description = options.description;
|
||||
this.details = options.details || null;
|
||||
this.args = options.args ? options.args.map(arg => new Argument(arg)) : [];
|
||||
this.clientPermissions = options.clientPermissions || [];
|
||||
this.userPermissions = options.userPermissions || [];
|
||||
this.ownerOnly = options.ownerOnly || false;
|
||||
this.nsfw = options.nsfw || false;
|
||||
this.guildOnly = options.guildOnly || false;
|
||||
this.patronOnly = options.patronOnly || false;
|
||||
this.guarded = options.guarded || false;
|
||||
this.throttling = options.throttling || { usages: 2, duration: 5 };
|
||||
this.credit = options.credit || [];
|
||||
this.credit.push({
|
||||
name: 'Dragon Fire',
|
||||
url: 'https://github.com/dragonfire535',
|
||||
reason: 'Code'
|
||||
});
|
||||
this.uses = 0;
|
||||
this.lastRun = null;
|
||||
this.throttles = new Map();
|
||||
this._timeouts = new Map();
|
||||
this._enabled = true;
|
||||
}
|
||||
|
||||
get group() {
|
||||
return this.client.registry.groups.get(this.groupID);
|
||||
}
|
||||
|
||||
usage() {
|
||||
const args = this.args
|
||||
.map(arg => `${arg.default ? '[' : '<'}${arg.label || arg.name}${arg.default ? ']' : '>'}`).join(' ');
|
||||
return `\`${this.client.commandPrefix}${this.name} ${args}\` or \`@${this.client.user.tag} ${this.name} ${args}\``;
|
||||
}
|
||||
|
||||
disable() {
|
||||
this._enabled = false;
|
||||
}
|
||||
|
||||
enable() {
|
||||
this._enabled = true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
const minimist = require('minimist');
|
||||
const argRegex = /"([^"]*)"|(\S+)/g;
|
||||
|
||||
module.exports = class CommandDispatcher {
|
||||
constructor(client) {
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this._commandPattern = null;
|
||||
}
|
||||
|
||||
get commandPattern() {
|
||||
if (this._commandPattern) return this._commandPattern;
|
||||
const prefix = this.client.commandPrefix;
|
||||
this._commandPattern = new RegExp(
|
||||
`^(<@!?${this.client.user.id}>\\s+(?:${prefix}}\\s*)?|${prefix}\\s*)([^\\s]+)`, 'i'
|
||||
);
|
||||
return this._commandPattern;
|
||||
}
|
||||
|
||||
isCommand(msg) {
|
||||
const command = msg.content.match(this.commandPattern);
|
||||
return Boolean(command);
|
||||
}
|
||||
|
||||
async parseMessage(msg) {
|
||||
const command = this.resolveCommand(command[2].toLowerCase());
|
||||
if (!command) {
|
||||
return {
|
||||
command: this.registry.commands.find(cmd => cmd.unknown),
|
||||
args: { command: command[2].toLowerCase() }
|
||||
};
|
||||
}
|
||||
const content = msg.content.replace(this.commandPattern, '').trim();
|
||||
const result = (content.match(argRegex) || []).map(m => m.replace(argRegex, '$1$2'));
|
||||
const parsed = minimist(result);
|
||||
const result = { flags: [...parsed] };
|
||||
for (let i = 0; i > command.args.length; i++) {
|
||||
const arg = command.args[i];
|
||||
const parsedArg = result._[i];
|
||||
if (arg.isEmpty(parsedArg, msg, arg)) {
|
||||
if (arg.default) {
|
||||
result[arg.name] = typeof arg.default === 'function' ? arg.default(msg) : arg.default;
|
||||
continue;
|
||||
} else {
|
||||
return `The "${arg.label || arg.name}" argument is required.`;
|
||||
}
|
||||
}
|
||||
const valid = await arg.validate(parsedArg, msg, arg);
|
||||
if (!valid) return `An invalid value was provided for the "${arg.label || arg.name}" argument.`;
|
||||
result[arg.name] = await arg.parse(parsedArg, msg, arg);
|
||||
}
|
||||
return { command, args: result };
|
||||
}
|
||||
|
||||
resolveCommand(command) {
|
||||
return this.registry.commands.find(cmd => cmd.name === command || cmd.aliases.includes(command));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
module.exports = Structures.extend('Message', Message => {
|
||||
return class CommandMessage extends Message {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
}
|
||||
|
||||
say(content, options) {
|
||||
return this.channel.send(content, options);
|
||||
}
|
||||
|
||||
embed(embed, options) {
|
||||
return this.channel.send('', { embed, ...options });
|
||||
}
|
||||
|
||||
code(lang, content, options) {
|
||||
return this.channel.send(content, { code: lang, ...options });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = class Group {
|
||||
constructor(client, id, name) {
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this.id = id.toLowerCase();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
get commands() {
|
||||
return this.client.registry.commands.filter(command => command.groupID === this.id);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
const Collection = require('@discordjs/collection');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const Group = require('./Group');
|
||||
|
||||
module.exports = class Registry {
|
||||
constructor(client) {
|
||||
Object.defineProperty(this, 'client', { value: client });
|
||||
|
||||
this.commands = new Collection();
|
||||
this.groups = new Collection();
|
||||
this.types = new Collection();
|
||||
}
|
||||
|
||||
findCommands(query) {
|
||||
query = query.toLowerCase();
|
||||
return this.commands.filter(command => command.name === query || command.aliases.includes(query));
|
||||
}
|
||||
|
||||
registerCommand(command) {
|
||||
this.commands.set(command.name, command);
|
||||
return this;
|
||||
}
|
||||
|
||||
registerCommandsIn(dir) {
|
||||
const groups = fs.readdirSync(dir);
|
||||
for (const group of groups) {
|
||||
const commands = fs.readdirSync(path.join(dir, group));
|
||||
for (const command of commands) {
|
||||
if (!command.endsWith('.js')) continue;
|
||||
const required = require(path.join(dir, group, command));
|
||||
this.registerCommand(new required(this.client));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
findGroups(query) {
|
||||
query = query.toLowerCase();
|
||||
return this.groups.filter(group => group.id === query || group.name.toLowerCase() === query);
|
||||
}
|
||||
|
||||
registerGroup(group) {
|
||||
this.groups.set(group.id, group);
|
||||
return this;
|
||||
}
|
||||
|
||||
registerGroups(groups) {
|
||||
for (const [id, name] of groups) {
|
||||
const group = new Group(this.client, id, name);
|
||||
this.registerGroup(group);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
registerType(type) {
|
||||
this.types.set(type.id, type);
|
||||
return this;
|
||||
}
|
||||
|
||||
registerTypesIn(dir) {
|
||||
const types = fs.readdirSync(dir);
|
||||
for (const type of types) {
|
||||
if (!type.endsWith('.js')) continue;
|
||||
const required = require(path.join(dir, type));
|
||||
this.registerType(new required(this.client));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
registerDefaultTypes() {
|
||||
return this.registerTypesIn(path.join(__dirname, 'types'));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
const ArgumentType = require('./ArgumentType');
|
||||
|
||||
module.exports = class ArgumentUnionType extends ArgumentType {
|
||||
constructor(client, id) {
|
||||
super(client, id);
|
||||
|
||||
this.types = [];
|
||||
const typeIDs = id.split('|');
|
||||
for (const typeID of typeIDs) {
|
||||
const type = client.registry.types.get(typeID);
|
||||
if (!type) throw new Error(`Argument type "${typeID}" is not registered.`);
|
||||
this.types.push(type);
|
||||
}
|
||||
}
|
||||
|
||||
async validate(val, msg, arg) {
|
||||
let results = this.types.map(type => !type.isEmpty(val, msg, arg) && type.validate(val, msg, arg));
|
||||
results = await Promise.all(results);
|
||||
if (results.some(valid => valid && typeof valid !== 'string')) return true;
|
||||
const errors = results.filter(valid => typeof valid === 'string');
|
||||
if (errors.length > 0) return errors.join('\n');
|
||||
return false;
|
||||
}
|
||||
|
||||
async parse(val, msg, arg) {
|
||||
let results = this.types.map(type => !type.isEmpty(val, msg, arg) && type.validate(val, msg, arg));
|
||||
results = await Promise.all(results);
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
if (results[i] && typeof results[i] !== 'string') return this.types[i].parse(val, msg, arg);
|
||||
}
|
||||
throw new Error(`Couldn't parse value "${val}" with union type ${this.id}.`);
|
||||
}
|
||||
|
||||
isEmpty(val, msg, arg) {
|
||||
return !this.types.some(type => !type.isEmpty(val, msg, arg));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class BooleanArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'boolean');
|
||||
this.truthy = new Set(['true', 't', 'yes', 'y', 'on', 'enable', 'enabled', '1', '+']);
|
||||
this.falsy = new Set(['false', 'f', 'no', 'n', 'off', 'disable', 'disabled', '0', '-']);
|
||||
}
|
||||
|
||||
validate(val) {
|
||||
const lc = val.toLowerCase();
|
||||
return this.truthy.has(lc) || this.falsy.has(lc);
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
const lc = val.toLowerCase();
|
||||
if (this.truthy.has(lc)) return true;
|
||||
if (this.falsy.has(lc)) return false;
|
||||
throw new RangeError('Unknown boolean value.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class ChannelArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'channel');
|
||||
}
|
||||
|
||||
validate(val, msg) {
|
||||
const matches = val.match(/^(?:<#)?([0-9]+)>?$/);
|
||||
if (matches) return msg.guild.channels.cache.has(matches[1]);
|
||||
const search = val.toLowerCase();
|
||||
const channels = msg.guild.channels.cache.filter(nameFilterInexact(search));
|
||||
if (channels.size === 0) return false;
|
||||
if (channels.size === 1) return true;
|
||||
const exactChannels = channels.filter(nameFilterExact(search));
|
||||
if (exactChannels.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(val, msg) {
|
||||
const matches = val.match(/^(?:<#)?([0-9]+)>?$/);
|
||||
if (matches) return msg.guild.channels.cache.get(matches[1]) || null;
|
||||
const search = val.toLowerCase();
|
||||
const channels = msg.guild.channels.cache.filter(nameFilterInexact(search));
|
||||
if (channels.size === 0) return null;
|
||||
if (channels.size === 1) return channels.first();
|
||||
const exactChannels = channels.filter(nameFilterExact(search));
|
||||
if (exactChannels.size === 1) return exactChannels.first();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function nameFilterExact(search) {
|
||||
return thing => thing.name.toLowerCase() === search;
|
||||
}
|
||||
|
||||
function nameFilterInexact(search) {
|
||||
return thing => thing.name.toLowerCase().includes(search);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class CommandArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'command');
|
||||
}
|
||||
|
||||
validate(val) {
|
||||
const commands = this.client.registry.findCommands(val);
|
||||
if (commands.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return this.client.registry.findCommands(val).first();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class CustomEmojiArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'custom-emoji');
|
||||
}
|
||||
|
||||
validate(value, msg) {
|
||||
const matches = value.match(/^(?:<a?:([a-zA-Z0-9_]+):)?([0-9]+)>?$/);
|
||||
if (matches && msg.client.emojis.cache.has(matches[2])) return true;
|
||||
if (!msg.guild) return false;
|
||||
const search = value.toLowerCase();
|
||||
const emojis = msg.guild.emojis.cache.filter(nameFilterInexact(search));
|
||||
if (!emojis.size) return false;
|
||||
if (emojis.size === 1) return true;
|
||||
const exactEmojis = emojis.filter(nameFilterExact(search));
|
||||
if (exactEmojis.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(value, msg) {
|
||||
const matches = value.match(/^(?:<a?:([a-zA-Z0-9_]+):)?([0-9]+)>?$/);
|
||||
if (matches) return msg.client.emojis.cache.get(matches[2]) || null;
|
||||
const search = value.toLowerCase();
|
||||
const emojis = msg.guild.emojis.cache.filter(nameFilterInexact(search));
|
||||
if (!emojis.size) return null;
|
||||
if (emojis.size === 1) return emojis.first();
|
||||
const exactEmojis = emojis.filter(nameFilterExact(search));
|
||||
if (exactEmojis.size === 1) return exactEmojis.first();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function nameFilterExact(search) {
|
||||
return emoji => emoji.name.toLowerCase() === search;
|
||||
}
|
||||
|
||||
function nameFilterInexact(search) {
|
||||
return emoji => emoji.name.toLowerCase().includes(search);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
const emojiRegex = require('emoji-regex/RGI_Emoji.js');
|
||||
|
||||
module.exports = class DefaultEmojiArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'default-emoji');
|
||||
this.regex = new RegExp(`^(?:${emojiRegex().source})$`);
|
||||
}
|
||||
|
||||
validate(value) {
|
||||
if (!this.regex.test(value)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
parse(value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class FloatArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'float');
|
||||
}
|
||||
|
||||
validate(val, msg, arg) {
|
||||
const float = Number.parseFloat(val);
|
||||
if (Number.isNaN(float)) return false;
|
||||
if (arg.oneOf && !arg.oneOf.includes(float)) return false;
|
||||
if (arg.min !== null && typeof arg.min !== 'undefined' && float < arg.min) return false;
|
||||
if (arg.max !== null && typeof arg.max !== 'undefined' && float > arg.max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return Number.parseFloat(val);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class GroupArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'group');
|
||||
}
|
||||
|
||||
validate(val) {
|
||||
const groups = this.client.registry.findGroups(val);
|
||||
if (groups.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return this.client.registry.findGroups(val).first();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class IntegerArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'integer');
|
||||
}
|
||||
|
||||
validate(val, msg, arg) {
|
||||
const int = Number.parseInt(val);
|
||||
if (Number.isNaN(int)) return false;
|
||||
if (arg.oneOf && !arg.oneOf.includes(int)) return false;
|
||||
if (arg.min !== null && typeof arg.min !== 'undefined' && int < arg.min) return false;
|
||||
if (arg.max !== null && typeof arg.max !== 'undefined' && int > arg.max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return Number.parseInt(val);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class MemberArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'member');
|
||||
}
|
||||
|
||||
async validate(val, msg) {
|
||||
const matches = val.match(/^(?:<@!?)?([0-9]+)>?$/);
|
||||
if (matches) {
|
||||
try {
|
||||
const member = await msg.guild.members.fetch(await this.client.users.fetch(matches[1]));
|
||||
if (!member) return false;
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const search = val.toLowerCase();
|
||||
const members = msg.guild.members.cache.filter(memberFilterInexact(search));
|
||||
if (members.size === 0) return false;
|
||||
if (members.size === 1) return true;
|
||||
const exactMembers = members.filter(memberFilterExact(search));
|
||||
if (exactMembers.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(val, msg) {
|
||||
const matches = val.match(/^(?:<@!?)?([0-9]+)>?$/);
|
||||
if (matches) return msg.guild.members.resolve(matches[1]) || null;
|
||||
const search = val.toLowerCase();
|
||||
const members = msg.guild.members.cache.filter(memberFilterInexact(search));
|
||||
if (members.size === 0) return null;
|
||||
if (members.size === 1) return members.first();
|
||||
const exactMembers = members.filter(memberFilterExact(search));
|
||||
if (exactMembers.size === 1) return exactMembers.first();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function memberFilterExact(search) {
|
||||
return mem => mem.user.username.toLowerCase() === search ||
|
||||
(mem.nickname && mem.nickname.toLowerCase() === search) ||
|
||||
mem.tag.toLowerCase() === search;
|
||||
}
|
||||
|
||||
function memberFilterInexact(search) {
|
||||
return mem => mem.user.username.toLowerCase().includes(search) ||
|
||||
(mem.nickname && mem.nickname.toLowerCase().includes(search)) ||
|
||||
mem.tag.toLowerCase().includes(search);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class MessageArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'message');
|
||||
}
|
||||
|
||||
async validate(val, msg) {
|
||||
if (!/^[0-9]+$/.test(val)) return false;
|
||||
return Boolean(await msg.channel.messages.fetch(val).catch(() => null));
|
||||
}
|
||||
|
||||
parse(val, msg) {
|
||||
return msg.channel.messages.cache.get(val);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class RoleArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'role');
|
||||
}
|
||||
|
||||
validate(val, msg) {
|
||||
const matches = val.match(/^(?:<@&)?([0-9]+)>?$/);
|
||||
if (matches) return msg.guild.roles.cache.has(matches[1]);
|
||||
const search = val.toLowerCase();
|
||||
const roles = msg.guild.roles.cache.filter(nameFilterInexact(search));
|
||||
if (roles.size === 0) return false;
|
||||
if (roles.size === 1) return true;
|
||||
const exactRoles = roles.filter(nameFilterExact(search));
|
||||
if (exactRoles.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(val, msg) {
|
||||
const matches = val.match(/^(?:<@&)?([0-9]+)>?$/);
|
||||
if (matches) return msg.guild.roles.cache.get(matches[1]) || null;
|
||||
const search = val.toLowerCase();
|
||||
const roles = msg.guild.roles.cache.filter(nameFilterInexact(search));
|
||||
if (roles.size === 0) return null;
|
||||
if (roles.size === 1) return roles.first();
|
||||
const exactRoles = roles.filter(nameFilterExact(search));
|
||||
if (exactRoles.size === 1) return exactRoles.first();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function nameFilterExact(search) {
|
||||
return thing => thing.name.toLowerCase() === search;
|
||||
}
|
||||
|
||||
function nameFilterInexact(search) {
|
||||
return thing => thing.name.toLowerCase().includes(search);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class StringArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'string');
|
||||
}
|
||||
|
||||
validate(val, msg, arg) {
|
||||
if (arg.oneOf && !arg.oneOf.includes(val.toLowerCase())) return false;
|
||||
if (arg.min !== null && typeof arg.min !== 'undefined' && val.length < arg.min) return false;
|
||||
if (arg.max !== null && typeof arg.max !== 'undefined' && val.length > arg.max) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
parse(val) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
const ArgumentType = require('../ArgumentType');
|
||||
|
||||
module.exports = class UserArgumentType extends ArgumentType {
|
||||
constructor(client) {
|
||||
super(client, 'user');
|
||||
}
|
||||
|
||||
async validate(val, msg, arg) {
|
||||
const matches = val.match(/^(?:<@!?)?([0-9]+)>?$/);
|
||||
if (matches) {
|
||||
try {
|
||||
const user = await msg.client.users.fetch(matches[1]);
|
||||
if (!user) return false;
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!msg.guild) return false;
|
||||
const search = val.toLowerCase();
|
||||
const members = msg.guild.members.cache.filter(memberFilterInexact(search));
|
||||
if (members.size === 0) return false;
|
||||
if (members.size === 1) return true;
|
||||
const exactMembers = members.filter(memberFilterExact(search));
|
||||
if (exactMembers.size === 1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(val, msg) {
|
||||
const matches = val.match(/^(?:<@!?)?([0-9]+)>?$/);
|
||||
if (matches) return msg.client.users.cache.get(matches[1]) || null;
|
||||
if (!msg.guild) return null;
|
||||
const search = val.toLowerCase();
|
||||
const members = msg.guild.members.cache.filter(memberFilterInexact(search));
|
||||
if (members.size === 0) return null;
|
||||
if (members.size === 1) return members.first().user;
|
||||
const exactMembers = members.filter(memberFilterExact(search));
|
||||
if (exactMembers.size === 1) return exactMembers.first().user;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function memberFilterExact(search) {
|
||||
return mem => mem.user.username.toLowerCase() === search ||
|
||||
(mem.nickname && mem.nickname.toLowerCase() === search) ||
|
||||
mem.tag.toLowerCase() === search;
|
||||
}
|
||||
|
||||
function memberFilterInexact(search) {
|
||||
return mem => mem.user.username.toLowerCase().includes(search) ||
|
||||
(mem.nickname && mem.nickname.toLowerCase().includes(search)) ||
|
||||
mem.tag.toLowerCase().includes(search);
|
||||
}
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xiao",
|
||||
"version": "140.3.1",
|
||||
"version": "141.0.0",
|
||||
"description": "Your personal server companion.",
|
||||
"main": "Xiao.js",
|
||||
"private": true,
|
||||
@@ -15,12 +15,10 @@
|
||||
},
|
||||
"keywords": [
|
||||
"bot",
|
||||
"commando",
|
||||
"discord",
|
||||
"discord-api",
|
||||
"discord-bot",
|
||||
"discord-js",
|
||||
"discord-js-commando"
|
||||
"discord-js"
|
||||
],
|
||||
"author": "dragonfire535 <danielbodendahl@gmail.com>",
|
||||
"license": "UNLICENSED",
|
||||
@@ -52,7 +50,6 @@
|
||||
"custom-translate": "^2.2.8",
|
||||
"didyoumean2": "^4.2.0",
|
||||
"discord.js": "github:discordjs/discord.js",
|
||||
"discord.js-commando": "github:discordjs/Commando",
|
||||
"discord.js-docs": "github:TeeSeal/discord.js-docs",
|
||||
"dotenv": "^9.0.2",
|
||||
"emoji-regex": "^9.2.2",
|
||||
@@ -70,6 +67,7 @@
|
||||
"kuroshiro": "^1.1.2",
|
||||
"kuroshiro-analyzer-kuromoji": "^1.1.0",
|
||||
"mathjs": "^9.4.0",
|
||||
"minimist": "^1.2.5",
|
||||
"moment": "^2.29.1",
|
||||
"moment-duration-format": "^2.3.2",
|
||||
"moment-timezone": "^0.5.33",
|
||||
|
||||
+2
-109
@@ -1,4 +1,4 @@
|
||||
const { CommandoClient } = require('discord.js-commando');
|
||||
const CommandClient = require('../framework/Client');
|
||||
const { WebhookClient } = require('discord.js');
|
||||
const request = require('node-superfetch');
|
||||
const Collection = require('@discordjs/collection');
|
||||
@@ -12,7 +12,6 @@ const path = require('path');
|
||||
const Redis = require('./Redis');
|
||||
const Font = require('./Font');
|
||||
const BotList = require('./BotList');
|
||||
const Patreon = require('./Patreon');
|
||||
const PhoneManager = require('./phone/PhoneManager');
|
||||
const TimerManager = require('./remind/TimerManager');
|
||||
const PokemonStore = require('./pokemon/PokemonStore');
|
||||
@@ -26,7 +25,7 @@ const {
|
||||
COMMAND_CHANNEL_ID
|
||||
} = process.env;
|
||||
|
||||
module.exports = class XiaoClient extends CommandoClient {
|
||||
module.exports = class XiaoClient extends CommandClient {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
@@ -42,8 +41,6 @@ module.exports = class XiaoClient extends CommandoClient {
|
||||
this.webhook = new WebhookClient(XIAO_WEBHOOK_ID, XIAO_WEBHOOK_TOKEN, { disableMentions: 'everyone' });
|
||||
this.timers = new TimerManager(this);
|
||||
this.botList = new BotList(this);
|
||||
this.patreon = new Patreon();
|
||||
this.blacklist = { guild: [], user: [] };
|
||||
this.pokemon = new PokemonStore();
|
||||
this.games = new Collection();
|
||||
this.dispatchers = new Map();
|
||||
@@ -72,110 +69,6 @@ module.exports = class XiaoClient extends CommandoClient {
|
||||
moment.tz.link('America/New_York|Dragon');
|
||||
}
|
||||
|
||||
importBlacklist() {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'blacklist.json'), { encoding: 'utf8' });
|
||||
const file = JSON.parse(read);
|
||||
if (typeof file !== 'object' || Array.isArray(file)) return null;
|
||||
if (!file.guild || !file.user) return null;
|
||||
for (const id of file.guild) {
|
||||
if (typeof id !== 'string') continue;
|
||||
if (this.blacklist.guild.includes(id)) continue;
|
||||
this.blacklist.guild.push(id);
|
||||
}
|
||||
for (const id of file.user) {
|
||||
if (typeof id !== 'string') continue;
|
||||
if (this.blacklist.user.includes(id)) continue;
|
||||
this.blacklist.user.push(id);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportBlacklist() {
|
||||
let text = '{\n "guild": [\n ';
|
||||
if (this.blacklist.guild.length) {
|
||||
for (const id of this.blacklist.guild) {
|
||||
text += `"${id}",\n `;
|
||||
}
|
||||
text = text.slice(0, -4);
|
||||
}
|
||||
text += '\n ],\n "user": [\n ';
|
||||
if (this.blacklist.user.length) {
|
||||
for (const id of this.blacklist.user) {
|
||||
text += `"${id}",\n `;
|
||||
}
|
||||
text = text.slice(0, -4);
|
||||
}
|
||||
text += '\n ]\n}\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'blacklist.json'), buf, { encoding: 'utf8' });
|
||||
return buf;
|
||||
}
|
||||
|
||||
importCommandLeaderboard(add = false) {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'command-leaderboard.json'), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
const file = JSON.parse(read);
|
||||
if (typeof file !== 'object' || Array.isArray(file)) return null;
|
||||
for (const [id, value] of Object.entries(file)) {
|
||||
if (typeof value !== 'number') continue;
|
||||
const found = this.registry.commands.get(id);
|
||||
if (!found || found.uses === undefined) continue;
|
||||
if (add) found.uses += value;
|
||||
else found.uses = value;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportCommandLeaderboard() {
|
||||
let text = '{';
|
||||
for (const command of this.registry.commands.values()) {
|
||||
if (command.unknown) continue;
|
||||
if (command.uses === undefined) continue;
|
||||
text += `\n "${command.name}": ${command.uses},`;
|
||||
}
|
||||
text = text.slice(0, -1);
|
||||
text += '\n}\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'command-leaderboard.json'), buf, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
return buf;
|
||||
}
|
||||
|
||||
importLastRun() {
|
||||
const read = fs.readFileSync(path.join(__dirname, '..', 'command-last-run.json'), {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
const file = JSON.parse(read);
|
||||
if (typeof file !== 'object' || Array.isArray(file)) return null;
|
||||
for (const [id, value] of Object.entries(file)) {
|
||||
if (!value) continue;
|
||||
const date = new Date(value);
|
||||
if (date.toString() === 'Invalid Date') continue;
|
||||
const found = this.registry.commands.get(id);
|
||||
if (!found || found.lastRun === undefined) continue;
|
||||
found.lastRun = date;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
exportLastRun() {
|
||||
let text = '{';
|
||||
for (const command of this.registry.commands.values()) {
|
||||
if (command.unknown) continue;
|
||||
if (command.lastRun === undefined) continue;
|
||||
text += `\n "${command.name}": ${command.lastRun ? `"${command.lastRun.toISOString()}"` : null},`;
|
||||
}
|
||||
text = text.slice(0, -1);
|
||||
text += '\n}\n';
|
||||
const buf = Buffer.from(text);
|
||||
fs.writeFileSync(path.join(__dirname, '..', 'command-last-run.json'), buf, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
return buf;
|
||||
}
|
||||
|
||||
async fetchAdultSiteList(force = false) {
|
||||
if (!force && this.adultSiteList) return this.adultSiteList;
|
||||
const { text } = await request
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
const { Command } = require('discord.js-commando');
|
||||
const { stripIndents } = require('common-tags');
|
||||
|
||||
module.exports = class XiaoCommand extends Command {
|
||||
constructor(client, info) {
|
||||
if (!info.argsPromptLimit) info.argsPromptLimit = 2;
|
||||
super(client, info);
|
||||
|
||||
this.patronOnly = info.patronOnly || false;
|
||||
this.argsSingleQuotes = info.argsSingleQuotes || false;
|
||||
this.throttling = info.unknown ? null : info.throttling || { usages: 2, duration: 5 };
|
||||
this.uses = 0;
|
||||
this.lastRun = null;
|
||||
this.credit = info.credit || [];
|
||||
this.credit.push({
|
||||
name: 'Dragon Fire',
|
||||
url: 'https://github.com/dragonfire535',
|
||||
reason: 'Code'
|
||||
});
|
||||
}
|
||||
|
||||
hasPermission(msg, ownerOverride) {
|
||||
if (this.client.isOwner(msg.author)) return true;
|
||||
if (this.patronOnly && !this.client.patreon.isPatron(msg.author.id)) {
|
||||
return stripIndents`
|
||||
The \`${this.name}\` command can only be used by Patrons.
|
||||
Visit <https://www.patreon.com/xiaodiscord> to sign-up!
|
||||
`;
|
||||
}
|
||||
return super.hasPermission(msg, ownerOverride);
|
||||
}
|
||||
};
|
||||
@@ -8,9 +8,9 @@ module.exports = class AutoReplyCommand extends Command {
|
||||
this.throttling = null;
|
||||
}
|
||||
|
||||
run(msg, args, fromPattern) {
|
||||
run(msg) {
|
||||
if (msg.guild && !msg.channel.permissionsFor(this.client.user).has('SEND_MESSAGES')) return null;
|
||||
const text = this.generateText(fromPattern);
|
||||
const text = this.generateText();
|
||||
if (!text) return null;
|
||||
return this.reply ? msg.reply(text) : msg.say(text);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,7 @@ module.exports = class SubredditCommand extends Command {
|
||||
});
|
||||
}
|
||||
|
||||
async run(msg, { subreddit }, fromPattern) {
|
||||
if (fromPattern) {
|
||||
if (msg.guild && !msg.channel.permissionsFor(this.client.user).has('SEND_MESSAGES')) return null;
|
||||
subreddit = msg.patternMatches[1];
|
||||
}
|
||||
async run(msg, { subreddit }) {
|
||||
if (!subreddit) subreddit = typeof this.subreddit === 'function' ? this.subreddit() : this.subreddit;
|
||||
try {
|
||||
const post = await this.random(subreddit, msg.channel.nsfw);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
const codeblock = /```(?:(\S+)\n)?\s*([^]+?)\s*```/i;
|
||||
|
||||
module.exports = class CodeArgumentType extends ArgumentType {
|
||||
module.exports = class CodeArgument extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'code');
|
||||
}
|
||||
|
||||
+4
-8
@@ -1,14 +1,13 @@
|
||||
const { ArgumentType, util: { disambiguation } } = require('discord.js-commando');
|
||||
const { escapeMarkdown } = require('discord.js');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
|
||||
module.exports = class FontArgumentType extends ArgumentType {
|
||||
module.exports = class FontArgument extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'font');
|
||||
}
|
||||
|
||||
validate(value) {
|
||||
const choice = value.toLowerCase();
|
||||
let found = this.client.fonts.filter(font => {
|
||||
const found = this.client.fonts.filter(font => {
|
||||
if (font.isFallback) return false;
|
||||
if (font.name.toLowerCase().includes(choice)) return true;
|
||||
if (font.filenameNoExt.toLowerCase().includes(choice)) return true;
|
||||
@@ -22,10 +21,7 @@ module.exports = class FontArgumentType extends ArgumentType {
|
||||
return false;
|
||||
});
|
||||
if (foundExact.size === 1) return true;
|
||||
if (foundExact.size > 0) found = foundExact;
|
||||
return found.size <= 15
|
||||
? `${disambiguation(found.map(font => escapeMarkdown(font.filenameNoExt)), 'fonts', null)}\n`
|
||||
: 'Multiple fonts found. Please be more specific.';
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(value) {
|
||||
|
||||
+12
-12
@@ -1,25 +1,25 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
|
||||
module.exports = class ImageOrAvatarArgumentType extends ArgumentType {
|
||||
module.exports = class ImageOrAvatarArgument extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'image-or-avatar');
|
||||
}
|
||||
|
||||
async validate(value, msg, arg, currentMsg) {
|
||||
const image = await this.client.registry.types.get('image').validate(value, msg, arg, currentMsg);
|
||||
async validate(value, msg, arg) {
|
||||
const image = await this.client.registry.types.get('image').validate(value, msg, arg);
|
||||
if (image) return image;
|
||||
return this.client.registry.types.get('user').validate(value, msg, arg, currentMsg);
|
||||
return this.client.registry.types.get('user').validate(value, msg, arg);
|
||||
}
|
||||
|
||||
async parse(value, msg, arg, currentMsg) {
|
||||
const image = this.client.registry.types.get('image').parse(value, msg, arg, currentMsg);
|
||||
async parse(value, msg, arg) {
|
||||
const image = this.client.registry.types.get('image').parse(value, msg, arg);
|
||||
if (image) return image;
|
||||
const user = await this.client.registry.types.get('user').parse(value, msg, arg, currentMsg);
|
||||
return user.displayAvatarURL({ format: 'png', size: 512 });
|
||||
const user = await this.client.registry.types.get('user').parse(value, msg, arg);
|
||||
return user.displayAvatarURL({ format: 'png', size: arg.avatarSize || 512 });
|
||||
}
|
||||
|
||||
isEmpty(value, msg, arg, currentMsg) {
|
||||
return this.client.registry.types.get('image').isEmpty(value, msg, arg, currentMsg)
|
||||
&& this.client.registry.types.get('user').isEmpty(value, msg, arg, currentMsg);
|
||||
isEmpty(value, msg, arg) {
|
||||
return this.client.registry.types.get('image').isEmpty(value, msg, arg)
|
||||
&& this.client.registry.types.get('user').isEmpty(value, msg, arg);
|
||||
}
|
||||
};
|
||||
|
||||
+10
-10
@@ -1,18 +1,18 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
const fileTypeRe = /\.(jpe?g|png|gif|jfif|bmp)(\?.+)?$/i;
|
||||
const request = require('node-superfetch');
|
||||
const validURL = require('valid-url');
|
||||
|
||||
module.exports = class ImageArgumentType extends ArgumentType {
|
||||
module.exports = class ImageArgument extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'image');
|
||||
}
|
||||
|
||||
async validate(value, msg, arg, currentMsg) {
|
||||
const attachment = currentMsg.attachments.first();
|
||||
async validate(value, msg) {
|
||||
const attachment = msg.attachments.first();
|
||||
if (attachment) {
|
||||
if (attachment.size > 8e+6) return 'Please provide an image under 8 MB.';
|
||||
if (!fileTypeRe.test(attachment.name)) return 'Please only send PNG, JPG, BMP, or GIF format images.';
|
||||
if (attachment.size > 8e+6) return false;
|
||||
if (!fileTypeRe.test(attachment.name)) return false;
|
||||
return true;
|
||||
}
|
||||
if (fileTypeRe.test(value.toLowerCase())) {
|
||||
@@ -27,15 +27,15 @@ module.exports = class ImageArgumentType extends ArgumentType {
|
||||
return false;
|
||||
}
|
||||
|
||||
parse(value, msg, arg, currentMsg) {
|
||||
const attachment = currentMsg.attachments.first();
|
||||
parse(value, msg) {
|
||||
const attachment = msg.attachments.first();
|
||||
if (attachment) return attachment.url;
|
||||
if (fileTypeRe.test(value.toLowerCase())) return value;
|
||||
return null;
|
||||
}
|
||||
|
||||
isEmpty(value, msg, arg, currentMsg) {
|
||||
if (currentMsg.attachments.size) return false;
|
||||
isEmpty(value, msg) {
|
||||
if (msg.attachments.size) return false;
|
||||
return !value;
|
||||
}
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
const { months, shorthand } = require('../assets/json/month');
|
||||
|
||||
module.exports = class MonthArgumentType extends ArgumentType {
|
||||
module.exports = class MonthArgument extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'month');
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
|
||||
module.exports = class PokemonArgumentType extends ArgumentType {
|
||||
module.exports = class PokemonArgument extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'pokemon');
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,14 +1,14 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
const sherlock = require('sherlockjs');
|
||||
|
||||
module.exports = class SherlockType extends ArgumentType {
|
||||
module.exports = class SherlockType extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'sherlock');
|
||||
}
|
||||
|
||||
validate(value) {
|
||||
const time = sherlock.parse(value);
|
||||
if (!time.startDate) return 'Please provide a valid starting time.';
|
||||
if (!time.startDate) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
const cityTimezones = require('city-timezones');
|
||||
const { ZipToTz } = require('zip-to-timezone');
|
||||
const moment = require('moment-timezone');
|
||||
|
||||
module.exports = class TimezoneType extends ArgumentType {
|
||||
module.exports = class TimezoneType extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'timezone');
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
const { ArgumentType } = require('discord.js-commando');
|
||||
const Argument = require('../framework/ArgumentType');
|
||||
const { URL } = require('url');
|
||||
const validURL = require('valid-url');
|
||||
|
||||
module.exports = class UrlType extends ArgumentType {
|
||||
module.exports = class UrlType extends Argument {
|
||||
constructor(client) {
|
||||
super(client, 'url');
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ module.exports = class Util {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
static escapeRegex(str) {
|
||||
return str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
|
||||
}
|
||||
|
||||
static shuffle(array) {
|
||||
const arr = array.slice(0);
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
|
||||
Reference in New Issue
Block a user