diff options
author | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-06-14 22:51:48 -0400 |
---|---|---|
committer | IRONM00N <64110067+IRONM00N@users.noreply.github.com> | 2021-06-14 22:51:48 -0400 |
commit | d055e0dbb86ef7fd4ee96a1531b51181e825fb4b (patch) | |
tree | e2ed9e956f2d8167e7f225383f9917e66d2a2803 /src | |
parent | 335f7c30994fc8c4e787f407dfd4c2de63b400e3 (diff) | |
download | tanzanite-d055e0dbb86ef7fd4ee96a1531b51181e825fb4b.tar.gz tanzanite-d055e0dbb86ef7fd4ee96a1531b51181e825fb4b.tar.bz2 tanzanite-d055e0dbb86ef7fd4ee96a1531b51181e825fb4b.zip |
made a few changes
Diffstat (limited to 'src')
30 files changed, 757 insertions, 389 deletions
diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index bab5f8e..2f1d45d 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -1,15 +1,16 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { MessageEmbed, Message } from 'discord.js'; -import { inspect, promisify } from 'util'; import { exec } from 'child_process'; +import { Constants } from 'discord-akairo'; +import { Message, MessageEmbed, MessageEmbedOptions, Util } from 'discord.js'; +import { transpile } from 'typescript'; +import { inspect, promisify } from 'util'; +import { BushCommand } from '../../lib/extensions/BushCommand'; const clean = (text) => { - if (typeof text === 'string') - return text.replace(/`/g, '`' + String.fromCharCode(8203)).replace(/@/g, '@' + String.fromCharCode(8203)); - else return text; + if (typeof text === 'string') { + return (text = Util.cleanCodeBlockContent(text)); + } else return text; }; - export default class EvalCommand extends BushCommand { public constructor() { super('eval', { @@ -17,29 +18,54 @@ export default class EvalCommand extends BushCommand { category: 'dev', description: { content: 'Use the command to eval stuff in the bot.', - usage: 'eval <code> [--silent] [--depth #]', + usage: 'eval [--depth #] <code> [--sudo] [--silent] [--delete] [--proto] [--hidden] [--ts]', examples: ['eval message.guild.name', 'eval this.client.ownerID'] }, args: [ { - id: 'depth', - match: 'option', - type: 'number', + id: 'selDepth', + match: Constants.ArgumentMatches.OPTION, + type: Constants.ArgumentTypes.NUMBER, flag: '--depth', default: 0 }, { + id: 'sudo', + match: Constants.ArgumentMatches.FLAG, + flag: '--sudo' + }, + { + id: 'deleteMSG', + match: Constants.ArgumentMatches.FLAG, + flag: '--delete' + }, + { id: 'silent', - match: 'flag', + match: Constants.ArgumentMatches.FLAG, flag: '--silent' }, { + id: 'typescript', + match: Constants.ArgumentMatches.FLAG, + flag: '--ts' + }, + { + id: 'hidden', + match: Constants.ArgumentMatches.FLAG, + flag: '--hidden' + }, + { + id: 'showProto', + match: Constants.ArgumentMatches.FLAG, + flag: '--proto' + }, + { id: 'code', - match: 'rest', - type: 'string', + match: Constants.ArgumentMatches.REST, + type: Constants.ArgumentTypes.STRING, prompt: { start: 'What would you like to eval?', - retry: 'Invalid code to eval. What would you like to eval?' + retry: '{error} Invalid code to eval.' } } ], @@ -48,11 +74,62 @@ export default class EvalCommand extends BushCommand { }); } + private redactCredentials(old: string) { + const mapping = { + ['token']: 'Token', + ['devToken']: 'Dev Token', + ['MongoDB']: 'MongoDB URI', + ['hypixelApiKey']: 'Hypixel Api Key', + ['webhookID']: 'Webhook ID', + ['webhookToken']: 'Webhook Token' + }; + return mapping[old] || old; + } + public async exec( message: Message, - { depth, code, silent }: { depth: number; code: string; silent: boolean } - ): Promise<void> { + { + selDepth, + code: codeArg, + sudo, + silent, + deleteMSG, + typescript, + hidden, + showProto + }: { + selDepth: number; + code: string; + sudo: boolean; + silent: boolean; + deleteMSG: boolean; + typescript: boolean; + hidden: boolean; + showProto: boolean; + } + ): Promise<unknown> { + if (!this.client.config.owners.includes(message.author.id)) + return await message.channel.send(`${this.client.util.emojis.error} Only my developers can run this command.`); + const code: { js?: string | null; ts?: string | null; lang?: 'js' | 'ts' } = {}; + codeArg = codeArg.replace(/[ββ]/g, '"'); + codeArg = codeArg.replace(/```/g, ''); + if (typescript) { + code.ts = codeArg; + code.js = transpile(codeArg); + code.lang = 'ts'; + } else { + code.ts = null; + code.js = codeArg; + code.lang = 'js'; + } + const embed: MessageEmbed = new MessageEmbed(); + const bad_phrases: string[] = ['delete', 'destroy']; + if (bad_phrases.some((p) => code[code.lang].includes(p)) && !sudo) { + return await message.util.send(`${this.client.util.emojis.error} This eval was blocked by smooth brain protectionβ’.`); + } + const embeds: (MessageEmbed | MessageEmbedOptions)[] = [new MessageEmbed()]; + embeds.some((embed) => embed); try { let output; @@ -62,59 +139,85 @@ export default class EvalCommand extends BushCommand { guild = message.guild, channel = message.channel, config = this.client.config, + members = message.guild.members, + roles = message.guild.roles, sh = promisify(exec), models = this.client.db.models, got = require('got'); // eslint-disable-line @typescript-eslint/no-var-requires - output = eval(code); - output = await output; - if (typeof output !== 'string') output = inspect(output, { depth }); - output = output.replace(new RegExp(this.client.token, 'g'), '[token omitted]'); + if (code[code.lang].replace(/ /g, '').includes('9+10' || '10+9')) { + output = 21; + } else { + output = eval(code.js); + output = await output; + } + let proto, outputProto; + if (showProto) { + proto = Object.getPrototypeOf(output); + outputProto = clean(inspect(proto, { depth: 1, getters: true, showHidden: true })); + } + if (typeof output !== 'string') + output = inspect(output, { depth: selDepth, showHidden: hidden, getters: true, showProxy: true }); + for (const credentialName in this.client.config.credentials) { + const credential = this.client.config.credentials[credentialName]; + const newCredential = this.redactCredentials(credentialName); + output = output.replace( + new RegExp(credential.toString().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), + `[${newCredential} Omitted]` + ); + output = output.replace( + new RegExp([...credential.toString().replace(/[.*+?^${}()|[\]\\]/g, '\\$&')].reverse().join(''), 'g'), + `[${newCredential} Omitted]` + ); + } + output = clean(output); + const inputJS = clean(code.js); + embed - .setTitle('β
Evaled code successfully') - .addField( - 'π₯ Input', - code.length > 1012 - ? 'Too large to display. Hastebin: ' + (await this.client.util.haste(code)) - : '```js\n' + code + '```' - ) - .addField( - 'π€ Output', - output.length > 1012 - ? 'Too large to display. Hastebin: ' + (await this.client.util.haste(output)) - : '```js\n' + output + '```' - ) - .setColor('#66FF00') - .setFooter(message.author.username, message.author.displayAvatarURL({ dynamic: true })) + .setTitle(`${this.client.util.emojis.successFull} Evaled code successfully`) + .setColor(this.client.util.colors.success) + .setFooter(message.author.tag, message.author.displayAvatarURL({ dynamic: true })) .setTimestamp(); + if (code.lang === 'ts') { + const inputTS = clean(code.ts); + embed + .addField('π₯ Input (typescript)', await this.client.util.codeblock(inputTS, 1024, 'ts')) + .addField('π₯ Input (transpiled javascript)', await this.client.util.codeblock(inputJS, 1024, 'js')); + } else { + embed.addField('π₯ Input', await this.client.util.codeblock(inputJS, 1024, 'js')); + } + embed.addField('π€ Output', await this.client.util.codeblock(output, 1024, 'js')); + if (showProto) embed.addField('βοΈ Proto', await this.client.util.codeblock(outputProto, 1024, 'js')); } catch (e) { + const inputJS = clean(code.js); embed - .setTitle('β Code was not able to be evaled') - .addField( - 'π₯ Input', - code.length > 1012 - ? 'Too large to display. Hastebin: ' + (await this.client.util.haste(code)) - : '```js\n' + code + '```' - ) - .addField( - 'π€ Output', - e.length > 1012 - ? 'Too large to display. Hastebin: ' + (await this.client.util.haste(e)) - : '```js\n' + e + '```Full stack:' + (await this.client.util.haste(e.stack)) - ) - .setColor('#FF0000') - .setFooter(message.author.username, message.author.displayAvatarURL({ dynamic: true })) + .setTitle(`${this.client.util.emojis.errorFull} Code was not able to be evaled.`) + .setColor(this.client.util.colors.error) + .setFooter(message.author.tag, message.author.displayAvatarURL({ dynamic: true })) .setTimestamp(); + if (code.lang === 'ts') { + const inputTS = clean(code.ts); + embed + .addField('π₯ Input (typescript)', await this.client.util.codeblock(inputTS, 1024, 'ts')) + .addField('π₯ Input (transpiled javascript)', await this.client.util.codeblock(inputJS, 1024, 'js')); + } else { + embed.addField('π₯ Input', await this.client.util.codeblock(inputJS, 1024, 'js')); + } + embed.addField('π€ Output', await this.client.util.codeblock(e?.stack, 1024, 'js')); } if (!silent) { - await message.util.send(embed); + await message.util.reply({ embeds: [embed] }); } else { try { - await message.author.send(embed); - await message.react('<a:Check_Mark:790373952760971294>'); + await message.author.send({ embeds: [embed] }); + if (!deleteMSG) await message.react(this.client.util.emojis.successFull); } catch (e) { - await message.react('β'); + if (!deleteMSG) await message.react(this.client.util.emojis.errorFull); } } + + if (deleteMSG && message.deletable) { + await message.delete(); + } } } diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts index 0cf32ce..3194ce2 100644 --- a/src/commands/dev/reload.ts +++ b/src/commands/dev/reload.ts @@ -1,9 +1,9 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; import { stripIndent } from 'common-tags'; +import { ApplicationCommandOptionType } from 'discord-api-types'; import { Message } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; -import { ApplicationCommandOptionType } from 'discord-api-types'; export default class ReloadCommand extends BushCommand { constructor() { @@ -55,7 +55,7 @@ export default class ReloadCommand extends BushCommand { await message.util.send(await this.getResponse(fast)); } - public async execSlash(message: CommandInteraction, { fast }: { fast: SlashCommandOption<boolean> }): Promise<void> { - await message.reply(await this.getResponse(fast?.value)); + public async execSlash(message: BushInteractionMessage, { fast }: { fast: SlashCommandOption<boolean> }): Promise<void> { + await message.interaction.reply(await this.getResponse(fast?.value)); } } diff --git a/src/commands/dev/setLevel.ts b/src/commands/dev/setLevel.ts index e57e2f8..7401699 100644 --- a/src/commands/dev/setLevel.ts +++ b/src/commands/dev/setLevel.ts @@ -1,8 +1,7 @@ import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction } from 'discord.js'; -import { User } from 'discord.js'; -import { Message } from 'discord.js'; +import { Message, User } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; import { Level } from '../../lib/models'; import AllowedMentions from '../../lib/utils/AllowedMentions'; @@ -68,16 +67,18 @@ export default class SetLevelCommand extends BushCommand { } async exec(message: Message, { user, level }: { user: User; level: number }): Promise<void> { - await message.util.send(await this.setLevel(user, level), { + await message.util.send({ + content: await this.setLevel(user, level), allowedMentions: AllowedMentions.none() }); } async execSlash( - message: CommandInteraction, + message: BushInteractionMessage, { user, level }: { user: SlashCommandOption<void>; level: SlashCommandOption<number> } ): Promise<void> { - await message.reply(await this.setLevel(user.user, level.value), { + await message.interaction.reply({ + content: await this.setLevel(user.user, level.value), allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts index ebbd0c9..66bf5af 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -1,6 +1,7 @@ -import { MessageEmbed, Message, CommandInteraction } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Message, MessageEmbed } from 'discord.js'; import { duration } from 'moment'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; export default class BotInfoCommand extends BushCommand { constructor() { @@ -33,7 +34,7 @@ export default class BotInfoCommand extends BushCommand { }, { name: 'User count', - value: this.client.users.cache.size, + value: this.client.users.cache.size.toString(), inline: true }, { @@ -46,10 +47,10 @@ export default class BotInfoCommand extends BushCommand { } public async exec(message: Message): Promise<void> { - await message.util.send(await this.generateEmbed()); + await message.util.send({ embeds: [await this.generateEmbed()] }); } - public async execSlash(message: CommandInteraction): Promise<void> { - await message.reply(await this.generateEmbed()); + public async execSlash(message: BushInteractionMessage): Promise<void> { + await message.interaction.reply({ embeds: [await this.generateEmbed()] }); } } diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index 0629bf1..317091e 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -1,8 +1,8 @@ -import { Message, MessageEmbed } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; import { stripIndent } from 'common-tags'; import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction } from 'discord.js'; +import { Message, MessageEmbed } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; export default class HelpCommand extends BushCommand { @@ -72,14 +72,14 @@ export default class HelpCommand extends BushCommand { } public async exec(message: Message, { command }: { command: BushCommand }): Promise<void> { - await message.util.send(this.generateEmbed(command)); + await message.util.send({ embeds: [this.generateEmbed(command)] }); } - public async execSlash(message: CommandInteraction, { command }: { command: SlashCommandOption<string> }): Promise<void> { + public async execSlash(message: BushInteractionMessage, { command }: { command: SlashCommandOption<string> }): Promise<void> { if (command) { - await message.reply(this.generateEmbed(this.handler.findCommand(command.value) as BushCommand)); + await message.interaction.reply({ embeds: [this.generateEmbed(this.handler.findCommand(command.value) as BushCommand)] }); } else { - await message.reply(this.generateEmbed()); + await message.interaction.reply({ embeds: [this.generateEmbed()] }); } } } diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index b130e6d..feb48ad 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -1,7 +1,6 @@ -import { CommandInteraction } from 'discord.js'; -import { Message } from 'discord.js'; -import { MessageEmbed } from 'discord.js'; +import { Message, MessageEmbed } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; export default class PingCommand extends BushCommand { constructor() { @@ -29,23 +28,23 @@ export default class PingCommand extends BushCommand { .setTimestamp(); await sentMessage.edit({ content: null, - embed + embeds: [embed] }); } - public async execSlash(message: CommandInteraction): Promise<void> { - const timestamp1 = message.createdTimestamp; - await message.reply('Pong!'); - const timestamp2 = await message.fetchReply().then((m) => (m as Message).createdTimestamp); + public async execSlash(message: BushInteractionMessage): Promise<void> { + const timestamp1 = message.interaction.createdTimestamp; + await message.interaction.reply('Pong!'); + const timestamp2 = await message.interaction.fetchReply().then((m) => (m as Message).createdTimestamp); const botLatency = `\`\`\`\n ${Math.floor(timestamp2 - timestamp1)}ms \`\`\``; const apiLatency = `\`\`\`\n ${Math.round(this.client.ws.ping)}ms \`\`\``; const embed = new MessageEmbed() .setTitle('Pong! π') .addField('Bot Latency', botLatency, true) .addField('API Latency', apiLatency, true) - .setFooter(message.user.username, message.user.displayAvatarURL({ dynamic: true })) + .setFooter(message.interaction.user.username, message.interaction.user.displayAvatarURL({ dynamic: true })) .setTimestamp(); - await message.editReply({ + await message.interaction.editReply({ content: null, embeds: [embed] }); diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index 740eb68..faf3aa2 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -1,8 +1,8 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { User, Message, MessageEmbed } from 'discord.js'; -import got, { HTTPError } from 'got'; -import { CommandInteraction } from 'discord.js'; import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction, Message, MessageEmbed, User } from 'discord.js'; +import got, { HTTPError } from 'got'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; export const pronounMapping = { @@ -56,7 +56,7 @@ export default class PronounsCommand extends BushCommand { required: false } ], - slashEmphemeral: true // I'll add dynamic checking to this later + slashEphemeral: true // I'll add dynamic checking to this later }); } async sendResponse(message: Message | CommandInteraction, user: User, author: boolean): Promise<void> { @@ -65,15 +65,17 @@ export default class PronounsCommand extends BushCommand { .get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${user.id}`) .json(); if (message instanceof Message) { - message.reply( - new MessageEmbed({ - title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`, - description: pronounMapping[apiRes.pronouns], - footer: { - text: 'Data provided by https://pronoundb.org/' - } - }) - ); + message.reply({ + embeds: [ + new MessageEmbed({ + title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`, + description: pronounMapping[apiRes.pronouns], + footer: { + text: 'Data provided by https://pronoundb.org/' + } + }) + ] + }); } else { message.reply({ embeds: [ @@ -105,8 +107,8 @@ export default class PronounsCommand extends BushCommand { const u = user || message.author; await this.sendResponse(message, u, u.id === message.author.id); } - async execSlash(message: CommandInteraction, { user }: { user?: SlashCommandOption<void> }): Promise<void> { - const u = user?.user || message.user; - await this.sendResponse(message, u, u.id === message.user.id); + async execSlash(message: BushInteractionMessage, { user }: { user?: SlashCommandOption<void> }): Promise<void> { + const u = user?.user || message.author; + await this.sendResponse(message.interaction, u, u.id === message.author.id); } } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 0d57203..f843ac4 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,12 +1,10 @@ -import { User } from 'discord.js'; -import { Guild } from '../../lib/models'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { Ban, Modlog, ModlogType } from '../../lib/models'; +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction, Message, User } from 'discord.js'; import moment from 'moment'; -import { Message } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; -import { ApplicationCommandOptionType } from 'discord-api-types'; +import { Ban, Guild, Modlog, ModlogType } from '../../lib/models'; const durationAliases: Record<string, string[]> = { weeks: ['w', 'weeks', 'week', 'wk', 'wks'], @@ -170,7 +168,7 @@ export default class BanCommand extends BushCommand { } async execSlash( - message: CommandInteraction, + message: BushInteractionMessage, { user, reason, @@ -181,7 +179,7 @@ export default class BanCommand extends BushCommand { time: SlashCommandOption<string>; } ): Promise<void> { - for await (const response of this.genResponses(message, user.user, reason?.value, time?.value)) { + for await (const response of this.genResponses(message.interaction, user.user, reason?.value, time?.value)) { await message.reply(response); } } diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index c5581f6..eed0122 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -1,8 +1,8 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction, GuildMember, Message } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { Guild, Modlog, ModlogType } from '../../lib/models'; -import { GuildMember, Message } from 'discord.js'; -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction } from 'discord.js'; export default class KickCommand extends BushCommand { constructor() { @@ -100,9 +100,9 @@ export default class KickCommand extends BushCommand { } } - async execSlash(message: CommandInteraction, { user, reason }: { user: GuildMember; reason?: string }): Promise<void> { - for await (const response of this.genResponses(message, user, reason)) { - await message.reply(response); + async execSlash(message: BushInteractionMessage, { user, reason }: { user: GuildMember; reason?: string }): Promise<void> { + for await (const response of this.genResponses(message.interaction, user, reason)) { + await message.interaction.reply(response); } } } diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 806c00a..862a26d 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -1,10 +1,9 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { Message } from 'discord.js'; -import { Modlog } from '../../lib/models'; -import { MessageEmbed } from 'discord.js'; -import moment from 'moment'; import { stripIndent } from 'common-tags'; import { Argument } from 'discord-akairo'; +import { Message, MessageEmbed } from 'discord.js'; +import moment from 'moment'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Modlog } from '../../lib/models'; export default class ModlogCommand extends BushCommand { constructor() { @@ -87,10 +86,10 @@ export default class ModlogCommand extends BushCommand { }) ); if (page) { - await message.util.send(embedPages[page - 1]); + await message.util.send({ embeds: [embedPages[page - 1]] }); return; } else { - await message.util.send(embedPages[0]); + await message.util.send({ embeds: [embedPages[0]] }); return; } } else if (search) { @@ -99,38 +98,40 @@ export default class ModlogCommand extends BushCommand { await message.util.send('That modlog does not exist.'); return; } - await message.util.send( - new MessageEmbed({ - title: `Modlog ${entry.id}`, - fields: [ - { - name: 'Type', - value: entry.type.toLowerCase(), - inline: true - }, - { - name: 'Duration', - value: `${entry.duration ? moment.duration(entry.duration, 'milliseconds').humanize() : 'N/A'}`, - inline: true - }, - { - name: 'Reason', - value: `${entry.reason || 'None given'}`, - inline: true - }, - { - name: 'Moderator', - value: `<@!${entry.moderator}> (${entry.moderator})`, - inline: true - }, - { - name: 'User', - value: `<@!${entry.user}> (${entry.user})`, - inline: true - } - ] - }) - ); + await message.util.send({ + embeds: [ + new MessageEmbed({ + title: `Modlog ${entry.id}`, + fields: [ + { + name: 'Type', + value: entry.type.toLowerCase(), + inline: true + }, + { + name: 'Duration', + value: `${entry.duration ? moment.duration(entry.duration, 'milliseconds').humanize() : 'N/A'}`, + inline: true + }, + { + name: 'Reason', + value: `${entry.reason || 'None given'}`, + inline: true + }, + { + name: 'Moderator', + value: `<@!${entry.moderator}> (${entry.moderator})`, + inline: true + }, + { + name: 'User', + value: `<@!${entry.user}> (${entry.user})`, + inline: true + } + ] + }) + ] + }); } } } diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index b01d578..8951560 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-empty-function */ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { GuildMember, Message, Role } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; import AllowedMentions from '../../lib/utils/AllowedMentions'; -import { Message, Role, GuildMember } from 'discord.js'; -import { ApplicationCommandOptionType } from 'discord-api-types'; export default class RoleCommand extends BushCommand { private roleWhitelist: Record<string, string[]> = { @@ -77,43 +77,39 @@ export default class RoleCommand extends BushCommand { if (!message.member.permissions.has('MANAGE_ROLES') && !this.client.ownerID.includes(message.author.id)) { const mappedRole = this.client.util.moulberryBushRoleMap.find((m) => m.id === role.id); if (!mappedRole || !this.roleWhitelist[mappedRole.name]) { - return message.util.reply( - `<:error:837123021016924261> <@&${role.id}> is not whitelisted, and you do not have manage roles permission.`, - { - allowedMentions: AllowedMentions.none() - } - ); + return message.util.reply({ + content: `<:error:837123021016924261> <@&${role.id}> is not whitelisted, and you do not have manage roles permission.`, + allowedMentions: AllowedMentions.none() + }); } const allowedRoles = this.roleWhitelist[mappedRole.name].map((r) => { return this.client.util.moulberryBushRoleMap.find((m) => m.name === r).id; }); if (!message.member.roles.cache.some((role) => allowedRoles.includes(role.id))) { - return message.util.reply( - `<:error:837123021016924261> <@&${role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, - { - allowedMentions: AllowedMentions.none() - } - ); + return message.util.reply({ + content: `<:error:837123021016924261> <@&${role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, + allowedMentions: AllowedMentions.none() + }); } } if (!this.client.ownerID.includes(message.author.id)) { if (role.comparePositionTo(message.member.roles.highest) >= 0) { - return message.util.reply(`<:error:837123021016924261> <@&${role.id}> is higher or equal to your highest role.`, { + return message.util.reply({ + content: `<:error:837123021016924261> <@&${role.id}> is higher or equal to your highest role.`, allowedMentions: AllowedMentions.none() }); } if (role.comparePositionTo(message.guild.me.roles.highest) >= 0) { - return message.util.reply(`<:error:837123021016924261> <@&${role.id}> is higher or equal to my highest role.`, { + return message.util.reply({ + content: `<:error:837123021016924261> <@&${role.id}> is higher or equal to my highest role.`, allowedMentions: AllowedMentions.none() }); } if (role.managed) { - await message.util.reply( - `<:error:837123021016924261> <@&${role.id}> is managed by an integration and cannot be managed.`, - { - allowedMentions: AllowedMentions.none() - } - ); + await message.util.reply({ + content: `<:error:837123021016924261> <@&${role.id}> is managed by an integration and cannot be managed.`, + allowedMentions: AllowedMentions.none() + }); } } // No checks if the user has MANAGE_ROLES @@ -121,22 +117,26 @@ export default class RoleCommand extends BushCommand { try { await user.roles.remove(role.id); } catch { - return message.util.reply(`<:error:837123021016924261> Could not remove <@&${role.id}> from <@${user.id}>.`, { + return message.util.reply({ + content: `<:error:837123021016924261> Could not remove <@&${role.id}> from <@${user.id}>.`, allowedMentions: AllowedMentions.none() }); } - return message.util.reply(`<:checkmark:837109864101707807> Successfully removed <@&${role.id}> from <@${user.id}>!`, { + return message.util.reply({ + content: `<:checkmark:837109864101707807> Successfully removed <@&${role.id}> from <@${user.id}>!`, allowedMentions: AllowedMentions.none() }); } else { try { await user.roles.add(role.id); } catch { - return message.util.reply(`<:error:837123021016924261> Could not add <@&${role.id}> to <@${user.id}>.`, { + return message.util.reply({ + content: `<:error:837123021016924261> Could not add <@&${role.id}> to <@${user.id}>.`, allowedMentions: AllowedMentions.none() }); } - return message.util.reply(`<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, { + return message.util.reply({ + content: `<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, allowedMentions: AllowedMentions.none() }); } diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts index a0edf8c..7eb90c5 100644 --- a/src/commands/moulberry-bush/capePerms.ts +++ b/src/commands/moulberry-bush/capePerms.ts @@ -1,9 +1,8 @@ import { ApplicationCommandOptionType } from 'discord-api-types'; -import { MessageEmbed } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; -import { Message } from 'discord.js'; +import { Message, MessageEmbed } from 'discord.js'; import got from 'got'; import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; interface Capeperms { @@ -75,12 +74,12 @@ export default class CapePermissionsCommand extends BushCommand { ] }); } - private async getResponse(user: string): Promise<string | MessageEmbed> { + private async getResponse(user: string): Promise<{ content?: string; embeds?: MessageEmbed[] }> { let capeperms: Capeperms, uuid: string; try { uuid = await this.client.util.mcUUID(user); } catch (e) { - return `<:error:837123021016924261> \`${user}\` doesn't appear to be a valid username.`; + return { content: `<:error:837123021016924261> \`${user}\` doesn't appear to be a valid username.` }; } try { @@ -89,19 +88,19 @@ export default class CapePermissionsCommand extends BushCommand { capeperms = null; } if (capeperms == null) { - return `<:error:837123021016924261> There was an error finding cape perms for \`${user}\`.`; + return { content: `<:error:837123021016924261> There was an error finding cape perms for \`${user}\`.` }; } else { if (capeperms?.perms) { const foundUser = capeperms.perms.find((u) => u._id === uuid); - if (foundUser == null) return `<:error:837123021016924261> \`${user}\` does not appear to have any capes.`; + if (foundUser == null) return { content: `<:error:837123021016924261> \`${user}\` does not appear to have any capes.` }; const userPerm: string[] = foundUser.perms; const embed = this.client.util .createEmbed(this.client.util.colors.default) .setTitle(`${user}'s Capes`) .setDescription(userPerm.join('\n')); - return embed; + return { embeds: [embed] }; } else { - return `<:error:837123021016924261> There was an error finding cape perms for ${user}.`; + return { content: `<:error:837123021016924261> There was an error finding cape perms for ${user}.` }; } } } @@ -109,7 +108,7 @@ export default class CapePermissionsCommand extends BushCommand { await message.reply(await this.getResponse(user)); } - public async execSlash(message: CommandInteraction, { user }: { user: SlashCommandOption<string> }): Promise<void> { + public async execSlash(message: BushInteractionMessage, { user }: { user: SlashCommandOption<string> }): Promise<void> { await message.reply(await this.getResponse(user.value)); } } diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts index 34de257..9a03140 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -1,8 +1,6 @@ +import { Message, NewsChannel, TextChannel, WebhookClient } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; import AllowedMentions from '../../lib/utils/AllowedMentions'; -import { Message, WebhookClient } from 'discord.js'; -import { TextChannel } from 'discord.js'; -import { NewsChannel } from 'discord.js'; export default class GiveawayPingCommand extends BushCommand { constructor() { @@ -39,13 +37,12 @@ export default class GiveawayPingCommand extends BushCommand { const webhook = webhooks.first(); webhookClient = new WebhookClient(webhook.id, webhook.token); } - return webhookClient.send( - 'π <@&767782793261875210> Giveaway.\n\n<:mad:783046135392239626> Spamming, line breaking, gibberish etc. disqualifies you from winning. We can and will ban you from giveaways. Winners will all be checked and rerolled if needed.', - { - username: `${message.member.nickname || message.author.username}`, - avatarURL: message.author.avatarURL({ dynamic: true }), - allowedMentions: AllowedMentions.roles() - } - ); + return webhookClient.send({ + content: + 'π <@&767782793261875210> Giveaway.\n\n<:mad:783046135392239626> Spamming, line breaking, gibberish etc. disqualifies you from winning. We can and will ban you from giveaways. Winners will all be checked and rerolled if needed.', + username: `${message.member.nickname || message.author.username}`, + avatarURL: message.author.avatarURL({ dynamic: true }), + allowedMentions: AllowedMentions.roles() + }); } } diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/moulberry-bush/level.ts index 0375fd8..f53aa64 100644 --- a/src/commands/moulberry-bush/level.ts +++ b/src/commands/moulberry-bush/level.ts @@ -1,9 +1,7 @@ import { ApplicationCommandOptionType } from 'discord-api-types'; -import { Message } from 'discord.js'; -import { CommandInteractionOption } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; -import { User } from 'discord.js'; +import { CommandInteractionOption, Message, User } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { Level } from '../../lib/models'; /* import canvas from 'canvas'; @@ -150,13 +148,13 @@ export default class LevelCommand extends BushCommand { // ); await message.reply(await this.getResponse(user || message.author)); } - async execSlash(message: CommandInteraction, { user }: { user?: CommandInteractionOption }): Promise<void> { + async execSlash(message: BushInteractionMessage, { user }: { user?: CommandInteractionOption }): Promise<void> { // await message.reply( // new MessageAttachment( // await this.getImage(user?.user || message.user), // 'lel.png' // ) // ); - await message.reply(await this.getResponse(user?.user || message.user)); + await message.reply(await this.getResponse(user?.user || message.author)); } } diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index ba4f9e6..b71b42f 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -1,8 +1,8 @@ import { Argument } from 'discord-akairo'; -import { Message, MessageEmbed, User } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction } from 'discord.js'; +import { CommandInteraction, Message, MessageEmbed, User } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; export default class RuleCommand extends BushCommand { @@ -118,12 +118,12 @@ export default class RuleCommand extends BushCommand { message: Message | CommandInteraction, rule?: number, user?: User - ): string | MessageEmbed | [string, MessageEmbed] { + ): { content?: string; embeds?: MessageEmbed[] } | [string, MessageEmbed] { if ( message.guild.id !== '516977525906341928' && !this.client.ownerID.includes(message instanceof Message ? message.author.id : message.user.id) ) { - return "<:no:787549684196704257> This command can only be run in Moulberry's Bush."; + return { content: "<:no:787549684196704257> This command can only be run in Moulberry's Bush." }; } let rulesEmbed = new MessageEmbed().setColor('ef3929'); if (message instanceof Message) { @@ -138,7 +138,7 @@ export default class RuleCommand extends BushCommand { } } if (!user) { - return rulesEmbed; + return { embeds: [rulesEmbed] }; } else { return [`<@!${user.id}>`, rulesEmbed]; } @@ -146,8 +146,9 @@ export default class RuleCommand extends BushCommand { public async exec(message: Message, { rule, user }: { rule?: number; user?: User }): Promise<void> { const response = this.getResponse(message, rule, user); if (Array.isArray(response)) { - await message.util.send(response[0], { - embed: response[1] + await message.util.send({ + content: response[0], + embeds: [response[1]] }); } else { await message.util.send(response); @@ -156,16 +157,17 @@ export default class RuleCommand extends BushCommand { } public async execSlash( - message: CommandInteraction, + message: BushInteractionMessage, { rule, user }: { rule?: SlashCommandOption<number>; user?: SlashCommandOption<void> } ): Promise<void> { - const response = this.getResponse(message, rule?.value, user?.user); + const response = this.getResponse(message.interaction, rule?.value, user?.user); if (Array.isArray(response)) { - await message.reply(response[0], { + await message.interaction.reply({ + content: response[0], embeds: [response[1]] }); } else { - await message.reply(response); + await message.interaction.reply(response); } } } diff --git a/src/commands/server-config/prefix.ts b/src/commands/server-config/prefix.ts index c60f8fb..9cdc331 100644 --- a/src/commands/server-config/prefix.ts +++ b/src/commands/server-config/prefix.ts @@ -1,6 +1,7 @@ import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction, Message, Guild as DiscordGuild } from 'discord.js'; +import { Guild as DiscordGuild, Message } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushInteractionMessage } from '../../lib/extensions/BushInteractionMessage'; import { SlashCommandOption } from '../../lib/extensions/Util'; import { Guild } from '../../lib/models'; @@ -57,7 +58,7 @@ export default class PrefixCommand extends BushCommand { } } - async execSlash(message: CommandInteraction, { prefix }: { prefix?: SlashCommandOption<string> }): Promise<void> { + async execSlash(message: BushInteractionMessage, { prefix }: { prefix?: SlashCommandOption<string> }): Promise<void> { await this.changePrefix(message.guild, prefix?.value); if (prefix) { await message.reply(`Sucessfully set prefix to \`${prefix.value}\``); diff --git a/src/config/example-options.ts b/src/config/example-options.ts index ebd8c21..a00ba42 100644 --- a/src/config/example-options.ts +++ b/src/config/example-options.ts @@ -1,22 +1,24 @@ +import { Snowflake } from 'discord.js'; + // Credentials export const credentials = { botToken: 'token here' }; // Options -export const owners = [ +export const owners: Snowflake[] = [ '322862723090219008', //IRONM00N '464970779944157204', //TrashCan '487443883127472129' //Tyman ]; export const prefix = '-' as string; export const dev = true as boolean; -export const devGuild = '695310188764332072' as string; -export const channels = { - log: 'id here', - error: 'id here', - dm: 'id here', - command: 'id here' +export const devGuild = '1000000000000000' as Snowflake; +export const channels: { log: Snowflake; error: Snowflake; dm: Snowflake; command: Snowflake } = { + log: '1000000000000000', + error: '1000000000000000', + dm: '1000000000000000', + command: '1000000000000000' }; // Database specific diff --git a/src/lib/extensions/BushClient.ts b/src/lib/extensions/BushClient.ts index 8a0fc8c..e2e889b 100644 --- a/src/lib/extensions/BushClient.ts +++ b/src/lib/extensions/BushClient.ts @@ -1,14 +1,15 @@ -import { AkairoClient, CommandHandler, InhibitorHandler, ListenerHandler, TaskHandler } from 'discord-akairo'; -import { Guild } from 'discord.js'; +import chalk from 'chalk'; +import { AkairoClient, InhibitorHandler, ListenerHandler, TaskHandler } from 'discord-akairo'; +import { Guild, Intents, Snowflake } from 'discord.js'; import * as path from 'path'; -import { Sequelize } from 'sequelize'; -import * as Models from '../models'; -import { Util } from './Util'; import { exit } from 'process'; -import { Intents } from 'discord.js'; +import { Sequelize } from 'sequelize'; import * as config from '../../config/options'; -import { Logger } from '../utils/Logger'; -import chalk from 'chalk'; +import * as Models from '../models'; +import AllowedMentions from '../utils/AllowedMentions'; +import { BushLogger } from '../utils/Logger'; +import { BushCommandHandler } from './BushCommandHandler'; +import { BushUtil } from './Util'; export type BotConfig = typeof config; @@ -16,21 +17,21 @@ export class BushClient extends AkairoClient { public config: BotConfig; public listenerHandler: ListenerHandler; public inhibitorHandler: InhibitorHandler; - public commandHandler: CommandHandler; + public commandHandler: BushCommandHandler; public taskHandler: TaskHandler; - public util: Util; - public ownerID: string[]; + public util: BushUtil; + public ownerID: Snowflake[]; public db: Sequelize; - public logger: Logger; + public logger: BushLogger; constructor(config: BotConfig) { super( { ownerID: config.owners, - intents: Intents.NON_PRIVILEGED + intents: Intents.ALL }, { - allowedMentions: { parse: ['users'] }, // No everyone or role mentions by default - intents: Intents.NON_PRIVILEGED + allowedMentions: AllowedMentions.users(), // No everyone or role mentions by default + intents: Intents.ALL } ); @@ -58,7 +59,7 @@ export class BushClient extends AkairoClient { }); // Create command handler - this.commandHandler = new CommandHandler(this, { + this.commandHandler = new BushCommandHandler(this, { directory: path.join(__dirname, '..', '..', 'commands'), prefix: async ({ guild }: { guild: Guild }) => { const row = await Models.Guild.findByPk(guild.id); @@ -82,14 +83,14 @@ export class BushClient extends AkairoClient { automateCategories: true }); - this.util = new Util(this); + this.util = new BushUtil(this); this.db = new Sequelize(this.config.dev ? 'bushbot-dev' : 'bushbot', this.config.db.username, this.config.db.password, { dialect: 'postgres', host: this.config.db.host, port: this.config.db.port, logging: false }); - this.logger = new Logger(this); + this.logger = new BushLogger(this); } // Initialize everything diff --git a/src/lib/extensions/BushCommand.ts b/src/lib/extensions/BushCommand.ts index 4f9dc6e..2b34c69 100644 --- a/src/lib/extensions/BushCommand.ts +++ b/src/lib/extensions/BushCommand.ts @@ -13,6 +13,7 @@ export interface BushCommandOptions extends CommandOptions { export class BushCommand extends Command { public client: BushClient; + options: BushCommandOptions; constructor(id: string, options?: BushCommandOptions) { super(id, options); this.options = options; diff --git a/src/lib/extensions/BushCommandHandler.ts b/src/lib/extensions/BushCommandHandler.ts new file mode 100644 index 0000000..6ef44d7 --- /dev/null +++ b/src/lib/extensions/BushCommandHandler.ts @@ -0,0 +1,15 @@ +import { CommandHandler, CommandHandlerOptions } from 'discord-akairo'; +import { Collection } from 'discord.js'; +import { BushClient } from './BushClient'; +import { BushCommand } from './BushCommand'; + +export interface BushCommandHandlerOptions extends CommandHandlerOptions {} + +export class BushCommandHandler extends CommandHandler { + public constructor(client: BushClient, options: BushCommandHandlerOptions) { + super(client, options); + this.client = client; + } + + declare modules: Collection<string, BushCommand>; +} diff --git a/src/lib/extensions/BushInteractionMessage.ts b/src/lib/extensions/BushInteractionMessage.ts new file mode 100644 index 0000000..9bdc291 --- /dev/null +++ b/src/lib/extensions/BushInteractionMessage.ts @@ -0,0 +1,15 @@ +import { AkairoMessage } from 'discord-akairo'; +import { CommandInteraction } from 'discord.js'; +import { BushClient } from './BushClient'; + +export class BushInteractionMessage extends AkairoMessage { + public constructor( + client: BushClient, + interaction: CommandInteraction, + { slash, replied }: { slash?: boolean; replied?: boolean } + ) { + super(client, interaction, { slash, replied }); + this.client = client; + this.interaction = interaction; + } +} diff --git a/src/lib/extensions/BushListenerHandler.ts b/src/lib/extensions/BushListenerHandler.ts new file mode 100644 index 0000000..28615fc --- /dev/null +++ b/src/lib/extensions/BushListenerHandler.ts @@ -0,0 +1,6 @@ +import { ListenerHandler } from 'discord-akairo'; +import { BushClient } from './BushClient'; + +export class BushListenerHandler extends ListenerHandler { + declare client: BushClient; +} diff --git a/src/lib/extensions/BushMessage.ts b/src/lib/extensions/BushMessage.ts new file mode 100644 index 0000000..e7146f6 --- /dev/null +++ b/src/lib/extensions/BushMessage.ts @@ -0,0 +1,11 @@ +import { DMChannel, Message, NewsChannel, TextChannel } from 'discord.js'; +import { BushClient } from './BushClient'; + +export class BushMessage extends Message { + declare client: BushClient; + constructor(client: BushClient, data: unknown, channel: TextChannel | DMChannel | NewsChannel) { + super(client, data, channel); + this.client = client; + this.channel = channel; + } +} diff --git a/src/lib/extensions/BushTaskHandler.ts b/src/lib/extensions/BushTaskHandler.ts new file mode 100644 index 0000000..f783eb3 --- /dev/null +++ b/src/lib/extensions/BushTaskHandler.ts @@ -0,0 +1,12 @@ +import { AkairoHandlerOptions, TaskHandler } from 'discord-akairo'; +import { BushClient } from './BushClient'; + +export interface BushTaskHandlerOptions extends AkairoHandlerOptions {} + +export class BushTaskHandler extends TaskHandler { + public constructor(client: BushClient, options: BushTaskHandlerOptions) { + super(client, options); + this.client; + } + declare client: BushClient; +} diff --git a/src/lib/extensions/Util.ts b/src/lib/extensions/Util.ts index 78fba12..3913437 100644 --- a/src/lib/extensions/Util.ts +++ b/src/lib/extensions/Util.ts @@ -1,20 +1,33 @@ -import { ClientUtil } from 'discord-akairo'; -import { BushClient } from './BushClient'; -import { promisify } from 'util'; +import chalk from 'chalk'; import { exec } from 'child_process'; -import got from 'got'; -import { MessageEmbed, GuildMember, User } from 'discord.js'; -import { CommandInteractionOption } from 'discord.js'; +import { ClientUtil, Command } from 'discord-akairo'; import { - ApplicationCommandOptionType, - APIInteractionDataResolvedGuildMember, APIInteractionDataResolvedChannel, - APIRole + APIInteractionDataResolvedGuildMember, + APIRole, + ApplicationCommandOptionType } from 'discord-api-types'; -import { GuildChannel } from 'discord.js'; -import { Role } from 'discord.js'; -import chalk from 'chalk'; -import { Guild } from 'discord.js'; +import { + ButtonInteraction, + CommandInteractionOption, + Constants, + Guild, + GuildChannel, + GuildMember, + MessageActionRow, + MessageButton, + MessageComponentInteraction, + MessageEmbed, + MessageOptions, + Role, + Snowflake, + User, + Util +} from 'discord.js'; +import got from 'got'; +import { promisify } from 'util'; +import { BushClient } from './BushClient'; +import { BushMessage } from './BushMessage'; interface hastebinRes { key: string; @@ -23,11 +36,7 @@ interface hastebinRes { export interface uuidRes { uuid: string; username: string; - username_history?: - | { - username: string; - }[] - | null; + username_history?: { username: string }[] | null; textures: { custom: boolean; slim: boolean; @@ -54,7 +63,7 @@ export interface SlashCommandOption<T> { role?: Role | APIRole; } -export class Util extends ClientUtil { +export class BushUtil extends ClientUtil { /** * The client of this ClientUtil * @type {BushClient} @@ -92,7 +101,7 @@ export class Util extends ClientUtil { * @param ids The list of IDs to map * @returns The list of users mapped */ - public async mapIDs(ids: string[]): Promise<User[]> { + public async mapIDs(ids: Snowflake[]): Promise<User[]> { return await Promise.all(ids.map((id) => this.client.users.fetch(id))); } @@ -144,7 +153,7 @@ export class Util extends ClientUtil { const idMatch = text.match(idReg); if (idMatch) { try { - const user = await this.client.users.fetch(text); + const user = await this.client.users.fetch(text as Snowflake); return user; } catch { // pass @@ -154,7 +163,7 @@ export class Util extends ClientUtil { const mentionMatch = text.match(mentionReg); if (mentionMatch) { try { - const user = await this.client.users.fetch(mentionMatch.groups.id); + const user = await this.client.users.fetch(mentionMatch.groups.id as Snowflake); return user; } catch { // pass @@ -195,24 +204,37 @@ export class Util extends ClientUtil { */ public colors = { default: '#1FD8F1', - error: '#ff0000', - success: '#00ff02', + error: '#EF4947', + warn: '#FEBA12', + success: '#3BB681', red: '#ff0000', - blue: '#0055ff', + orange: '#E86100', + gold: '#b59400', + yellow: '#ffff00', + green: '#00ff1e', + darkGreen: '#008f11', aqua: '#00bbff', - purple: '#8400ff', + blue: '#0055ff', blurple: '#5440cd', + purple: '#8400ff', pink: '#ff00e6', - green: '#00ff1e', - darkgreen: '#008f11', - gold: '#b59400', - yellow: '#ffff00', white: '#ffffff', gray: '#a6a6a6', - lightgray: '#cfcfcf', - darkgray: '#7a7a7a', - black: '#000000', - orange: '#E86100' + lightGray: '#cfcfcf', + darkGray: '#7a7a7a', + black: '#000000' + }; + + public emojis = { + success: '<:checkmark:837109864101707807>', + warn: '<:warn:848726900876247050> ', + error: '<:error:837123021016924261>', + successFull: '<:checkmark_full:850118767576088646>', + warnFull: '<:warn_full:850118767391539312>', + errorFull: '<:error_full:850118767295201350>', + mad: '<:mad:783046135392239626>', + join: '<:join:850198029809614858>', + leave: '<:leave:850198048205307919>' }; /** @@ -238,7 +260,7 @@ export class Util extends ClientUtil { return apiRes.uuid.replace(/-/g, ''); } - public async syncSlashCommands(force = false, guild?: string): Promise<void> { + public async syncSlashCommands(force = false, guild?: Snowflake): Promise<void> { let fetchedGuild: Guild; if (guild) fetchedGuild = this.client.guilds.cache.get(guild); try { @@ -260,7 +282,7 @@ export class Util extends ClientUtil { for (const [, botCommand] of this.client.commandHandler.modules) { if (botCommand.execSlash) { const found = registered.find((i) => i.name == botCommand.id); - + Command; const slashdata = { name: botCommand.id, description: botCommand.description.content, @@ -332,6 +354,168 @@ export class Util extends ClientUtil { { name: 'No Giveaways', id: '808265422334984203' }, { name: 'No Support', id: '790247359824396319' } ]; + + private paginateEmojis = { + begging: '853667381335162910', + back: '853667410203770881', + stop: '853667471110570034', + forward: '853667492680564747', + end: '853667514915225640' + }; + + public async buttonPaginate( + message: BushMessage, + embeds: MessageEmbed[], + text: string | null = null, + deleteOnExit?: boolean + ): Promise<void> { + if (deleteOnExit === undefined) deleteOnExit = true; + embeds.forEach((_e, i) => { + embeds[i] = embeds[i].setFooter(`Page ${i + 1}/${embeds.length}`); + }); + + const style = Constants.MessageButtonStyles.PRIMARY; + let curPage = 0; + if (typeof embeds !== 'object') throw 'embeds must be an object'; + const msg = await message.util.reply({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow()] }); + const filter = (interaction: ButtonInteraction) => + interaction.customID.startsWith('paginate_') && interaction.message == msg; + const collector = msg.createMessageComponentInteractionCollector(filter, { time: 300000 }); + collector.on('collect', async (interaction: MessageComponentInteraction) => { + if (interaction.user.id == message.author.id || message.client.config.owners.includes(interaction.user.id)) { + switch (interaction.customID) { + case 'paginate_beginning': { + curPage = 0; + await edit(interaction); + break; + } + case 'paginate_back': { + curPage--; + await edit(interaction); + break; + } + case 'paginate_stop': { + if (deleteOnExit) { + await interaction.deferUpdate(); + await msg.delete(); + } else { + await interaction?.update({ + content: `${text ? text + '\n' : ''}Command closed by user.`, + embeds: [], + components: [] + }); + } + return; + } + case 'paginate_next': { + curPage++; + await edit(interaction); + break; + } + case 'paginate_end': { + curPage = embeds.length - 1; + await edit(interaction); + break; + } + } + } else { + return await interaction?.deferUpdate(); + } + }); + + collector.on('end', async () => { + await msg.edit({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow(true)] }).catch(() => {}); + }); + + async function edit(interaction: MessageComponentInteraction): Promise<void> { + return await interaction?.update({ content: text, embeds: [embeds[curPage]], components: [getPaginationRow()] }); + } + function getPaginationRow(disableAll = false): MessageActionRow { + return new MessageActionRow().addComponents( + new MessageButton({ + style, + customID: 'paginate_beginning', + emoji: this.paginateEmojis.begging, + disabled: disableAll || curPage == 0 + }), + new MessageButton({ + style, + customID: 'paginate_back', + emoji: this.paginateEmojis.back, + disabled: disableAll || curPage == 0 + }), + new MessageButton({ style, customID: 'paginate_stop', emoji: this.paginateEmojis.stop, disabled: disableAll }), + new MessageButton({ + style, + customID: 'paginate_next', + emoji: this.paginateEmojis.forward, + disabled: disableAll || curPage == embeds.length - 1 + }), + new MessageButton({ + style, + customID: 'paginate_end', + emoji: this.paginateEmojis.end, + disabled: disableAll || curPage == embeds.length - 1 + }) + ); + } + } + + public async sendWithDeleteButton(message: BushMessage, options: MessageOptions): Promise<void> { + updateOptions(); + const msg = await message.util.reply(options as MessageOptions & { split?: false }); + const filter = (interaction: ButtonInteraction) => interaction.customID == 'paginate__stop' && interaction.message == msg; + const collector = msg.createMessageComponentInteractionCollector(filter, { time: 300000 }); + collector.on('collect', async (interaction: MessageComponentInteraction) => { + if (interaction.user.id == message.author.id || message.client.config.owners.includes(interaction.user.id)) { + await interaction.deferUpdate(); + await msg.delete(); + return; + } else { + return await interaction?.deferUpdate(); + } + }); + + collector.on('end', async () => { + updateOptions(true, true); + await msg.edit(options); + }); + + function updateOptions(edit?: boolean, disable?: boolean) { + if (edit == undefined) edit = false; + if (disable == undefined) disable = false; + options.components = [ + new MessageActionRow().addComponents( + new MessageButton({ + style: Constants.MessageButtonStyles.PRIMARY, + customID: 'paginate__stop', + emoji: this.paginateEmojis.stop, + disabled: disable + }) + ) + ]; + if (edit) { + options.reply = undefined; + } + } + } + /** + * Surrounds text in a code block with the specified language and puts it in a haste bin if it too long. + * + * * Embed Description Limit = 2048 characters + * * Embed Field Limit = 1024 characters + */ + public async codeblock(code: string, length: number, language: 'ts' | 'js' | 'sh' | 'json' | '' = ''): Promise<string> { + let hasteOut = ''; + const tildes = '```'; + const formattingLength = 2 * tildes.length + language.length + 2 * '\n'.length; + if (code.length + formattingLength > length) hasteOut = 'Too large to display. Hastebin: ' + (await this.haste(code)); + + const code2 = code.length > length ? code.substring(0, length - (hasteOut.length + '\n'.length + formattingLength)) : code; + return ( + tildes + language + '\n' + Util.cleanCodeBlockContent(code2) + '\n' + tildes + (hasteOut.length ? '\n' + hasteOut : '') + ); + } } // I just copy pasted this code from stackoverflow don't yell at me if there is issues for it diff --git a/src/lib/models/Ban.ts b/src/lib/models/Ban.ts index 66c1be9..8ba55ec 100644 --- a/src/lib/models/Ban.ts +++ b/src/lib/models/Ban.ts @@ -1,7 +1,8 @@ +import { Snowflake } from 'discord.js'; import { DataTypes, Sequelize } from 'sequelize'; -import { BaseModel } from './BaseModel'; -import * as Models from './'; import { v4 as uuidv4 } from 'uuid'; +import * as Models from './'; +import { BaseModel } from './BaseModel'; export interface BanModel { id: string; @@ -28,11 +29,11 @@ export class Ban extends BaseModel<BanModel, BanModelCreationAttributes> impleme /** * The user who is banned */ - user: string; + user: Snowflake; /** * The guild they are banned from */ - guild: string; + guild: Snowflake; /** * The reason they are banned (optional) */ diff --git a/src/lib/utils/Console.ts b/src/lib/utils/Console.ts index b74ea07..a3bf326 100644 --- a/src/lib/utils/Console.ts +++ b/src/lib/utils/Console.ts @@ -1,93 +1,115 @@ -// import chalk from 'chalk'; -// import { BushClient } from '../extensions/BushClient'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import chalk from 'chalk'; +import { BushClient } from '../extensions/BushClient'; -// export class CustomConsole { -// private client: BushClient; -// public constructor(client: BushClient) { -// this.client = client; -// } +export class Log { + client: BushClient; -// private parseColors(content: any, color: 'blueBright' | 'blackBright' | 'redBright' | 'yellowBright' | 'greenBright'): string | any { -// if (typeof content === 'string') { -// const newContent: Array<string> = content.split(/<<|>>/); -// const tempParsedArray: Array<string> = []; -// newContent.forEach((value, index) => { -// if (index % 2 !== 0) { -// tempParsedArray.push(chalk[color](value)); -// } else { -// tempParsedArray.push(value); -// } -// }); -// return tempParsedArray.join(''); -// } else { -// return content; -// } -// } + public constructor(client: BushClient) { + this.client = client; + } -// private timeStamp(): string { -// const now = new Date(); -// const hours = now.getHours(); -// const minute = now.getMinutes(); -// let hour = hours; -// let amOrPm: 'AM' | 'PM' = 'AM'; -// if (hour > 12) { -// amOrPm = 'PM'; -// hour = hour - 12; -// } + private parseColors( + content: any, + color: 'blueBright' | 'blackBright' | 'redBright' | 'yellowBright' | 'greenBright' + ): string | any { + if (typeof content === 'string') { + const newContent: Array<string> = content.split(/<<|>>/); + const tempParsedArray: Array<string> = []; + newContent.forEach((value, index) => { + if (index % 2 !== 0) { + tempParsedArray.push(chalk[color](value)); + } else { + tempParsedArray.push(value); + } + }); + return tempParsedArray.join(''); + } else { + return content; + } + } -// return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 ? minute : `0${minute}`} ${amOrPm}`; -// } + private timeStamp(): string { + const now = new Date(); + const hours = now.getHours(); + const minute = now.getMinutes(); + let hour = hours; + let amOrPm: 'AM' | 'PM' = 'AM'; + if (hour > 12) { + amOrPm = 'PM'; + hour = hour - 12; + } -// /** -// * Logs debug information. -// * @param content - The content to log. -// */ -// public debug(content: any): void { -// console.log(`${chalk.bgGrey(this.timeStamp())} ${chalk.grey('[Debug]')}`, content); -// } + return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 ? minute : `0${minute}`} ${amOrPm}`; + } -// /** -// * Logs verbose information. Highlight information by surrounding it in `<<>>`. -// * @param header - The header displayed before the content, displayed in grey. -// * @param content - The content to log, highlights displayed in bright black. -// */ -// public verbose(header: string, content: string): void { -// return console.info(`${chalk.bgGrey(this.timeStamp())} ${chalk.grey(`[${header}]`)} ` + this.parseColors(content, 'blackBright')); -// } + /** + * Logs debug information. Only works in dev is enabled in the config. + * @param content - The content to log. + */ + public debug(...content: any): void { + if (this.client.config.dev) { + console.log(`${chalk.bgGrey(this.timeStamp())} ${chalk.grey('[Debug]')}`, ...content); + } + } -// /** -// * Logs information. Highlight information by surrounding it in `<<>>`. -// * @param header - The header displayed before the content, displayed in cyan. -// * @param content - The content to log, highlights displayed in bright blue. -// */ -// public info(header: string, content: string): void { -// return console.info(`${chalk.bgCyan(this.timeStamp())} ${chalk.cyan(`[${header}]`)} ` + this.parseColors(content, 'blueBright')); -// } + /** + * Logs verbose information. Highlight information by surrounding it in `<<>>`. + * @param header - The header displayed before the content, displayed in grey. + * @param content - The content to log, highlights displayed in bright black. + */ + public verbose(header: string, content: any): void { + if (this.client.config.logging.verbose) { + return console.info( + `${chalk.bgGrey(this.timeStamp())} ${chalk.grey(`[${header}]`)} ` + this.parseColors(content, 'blackBright') + ); + } + } -// /** -// * Logs warnings. Highlight information by surrounding it in `<<>>`. -// * @param header - The header displayed before the content, displayed in yellow. -// * @param content - The content to log, highlights displayed in bright yellow. -// */ -// public warn(header: string, content: string): void { -// return console.warn(`${chalk.bgYellow(this.timeStamp())} ${chalk.yellow(`[${header}]`)} ` + this.parseColors(content, 'yellowBright')); -// } + /** + * Logs information. Highlight information by surrounding it in `<<>>`. + * @param header - The header displayed before the content, displayed in cyan. + * @param content - The content to log, highlights displayed in bright blue. + */ + public info(header: string, content: any): void { + if (this.client.config.logging.info) { + return console.info( + `${chalk.bgCyan(this.timeStamp())} ${chalk.cyan(`[${header}]`)} ` + this.parseColors(content, 'blueBright') + ); + } + } -// /** -// * Logs errors. Highlight information by surrounding it in `<<>>`. -// * @param header - The header displayed before the content, displayed in bright red. -// * @param content - The content to log, highlights displayed in bright red. -// */ -// public error(header: string, content: string): void { -// return console.error(`${chalk.bgRedBright(this.timeStamp())} ${chalk.redBright(`[${header}]`)} ` + this.parseColors(content, 'redBright')); -// } + /** + * Logs warnings. Highlight information by surrounding it in `<<>>`. + * @param header - The header displayed before the content, displayed in yellow. + * @param content - The content to log, highlights displayed in bright yellow. + */ + public warn(header: string, content: any): void { + return console.warn( + `${chalk.bgYellow(this.timeStamp())} ${chalk.yellow(`[${header}]`)} ` + this.parseColors(content, 'yellowBright') + ); + } -// /** -// * Logs successes. Highlight information by surrounding it in `<<>>`. -// * @param header - The header displayed before the content, displayed in green. -// * @param content - The content to log, highlights displayed in bright green. -// */ -// public success(header: string, content: string): void { -// return console.log(`${chalk.bgGreen(this.timeStamp())} ${chalk.greenBright(`[${header}]`)} ` + this.parseColors(content, 'greenBright')); -// } -// } + /** + * Logs errors. Highlight information by surrounding it in `<<>>`. + * @param header - The header displayed before the content, displayed in bright red. + * @param content - The content to log, highlights displayed in bright red. + */ + public error(header: string, content: any): void { + return console.error( + `${chalk.bgRedBright(this.timeStamp())} ${chalk.redBright(`[${header}]`)} ` + this.parseColors(content, 'redBright') + ); + } + + /** + * Logs successes. Highlight information by surrounding it in `<<>>`. + * @param header - The header displayed before the content, displayed in green. + * @param content - The content to log, highlights displayed in bright green. + */ + public success(header: string, content: any): void { + return console.log( + `${chalk.bgGreen(this.timeStamp())} ${chalk.greenBright(`[${header}]`)} ` + this.parseColors(content, 'greenBright') + ); + } +} diff --git a/src/lib/utils/Logger.ts b/src/lib/utils/Logger.ts index 96837b6..0675e3d 100644 --- a/src/lib/utils/Logger.ts +++ b/src/lib/utils/Logger.ts @@ -1,8 +1,8 @@ +import chalk from 'chalk'; import { TextChannel } from 'discord.js'; import { BushClient } from '../extensions/BushClient'; -import chalk from 'chalk'; -export class Logger { +export class BushLogger { private client: BushClient; public constructor(client: BushClient) { this.client = client; diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index 0e52140..06aa55f 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -1,9 +1,7 @@ +import { stripIndents } from 'common-tags'; +import { Message, MessageEmbed, TextChannel } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; import { BushListener } from '../../lib/extensions/BushListener'; -import { stripIndents } from 'common-tags'; -import { Message } from 'discord.js'; -import { MessageEmbed } from 'discord.js'; -import { TextChannel } from 'discord.js'; export default class CommandErrorListener extends BushListener { constructor() { @@ -36,7 +34,7 @@ export default class CommandErrorListener extends BushListener { ); } const channel = (await this.client.channels.fetch(this.client.config.channels.log)) as TextChannel; - await channel.send(errorDevEmbed); - if (errorUserEmbed) await message.reply(errorUserEmbed); + await channel.send({ embeds: [errorDevEmbed] }); + if (errorUserEmbed) await message.reply({ embeds: [errorUserEmbed] }); } } diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts index e4c3bb0..b8d9b6b 100644 --- a/src/listeners/commands/slashCommandError.ts +++ b/src/listeners/commands/slashCommandError.ts @@ -1,9 +1,7 @@ +import { stripIndents } from 'common-tags'; +import { CommandInteraction, MessageEmbed, TextChannel } from 'discord.js'; import { BushCommand } from '../../lib/extensions/BushCommand'; import { BushListener } from '../../lib/extensions/BushListener'; -import { stripIndents } from 'common-tags'; -import { MessageEmbed } from 'discord.js'; -import { TextChannel } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; export default class SlashCommandErrorListener extends BushListener { constructor() { @@ -36,7 +34,7 @@ export default class SlashCommandErrorListener extends BushListener { ); } const channel = (await this.client.channels.fetch(this.client.config.channels.log)) as TextChannel; - await channel.send(errorDevEmbed); - if (errorUserEmbed) await message.reply(errorUserEmbed); + await channel.send({ embeds: [errorDevEmbed] }); + if (errorUserEmbed) await message.reply({ embeds: [errorUserEmbed] }); } } |