From 0caccda67d97dd74405aa4ece5d3f07e7c7dfc66 Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Wed, 26 May 2021 21:36:50 -0400 Subject: some rebranding --- src/commands/moulberry-bush/rule.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/commands/moulberry-bush/rule.ts') diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index bd8b08a..69dcb6e 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -1,11 +1,11 @@ import { Argument } from 'discord-akairo'; import { Message, MessageEmbed, User } from 'discord.js'; -import { BotCommand } from '../../lib/extensions/BotCommand'; +import { BushCommand } from '../../lib/extensions/BushCommand'; import { ApplicationCommandOptionType } from 'discord-api-types'; import { CommandInteraction } from 'discord.js'; import { SlashCommandOption } from '../../lib/extensions/Util'; -export default class RuleCommand extends BotCommand { +export default class RuleCommand extends BushCommand { private rules = [ { title: "Follow Discord's TOS", -- cgit From cd0f853a2e4732cea5356f9ee3603bb804b0ab1f Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Wed, 26 May 2021 21:53:35 -0400 Subject: made some more changes --- src/commands/dev/eval.ts | 138 ------------- src/commands/dev/evalCommand.ts | 138 +++++++++++++ src/commands/dev/reload.ts | 44 ---- src/commands/dev/reloadCommand.ts | 44 ++++ src/commands/dev/setLevelCommand.ts | 91 +++++++++ src/commands/dev/setlevel.ts | 91 --------- src/commands/info/botInfoCommand.ts | 65 ++++++ src/commands/info/botinfo.ts | 65 ------ src/commands/info/help.ts | 107 ---------- src/commands/info/helpCommand.ts | 107 ++++++++++ src/commands/info/ping.ts | 68 ------- src/commands/info/pingCommand.ts | 68 +++++++ src/commands/info/pronouns.ts | 121 ----------- src/commands/info/pronounsCommand.ts | 121 +++++++++++ src/commands/moderation/ban.ts | 223 -------------------- src/commands/moderation/banCommand.ts | 224 +++++++++++++++++++++ src/commands/moderation/kick.ts | 120 ----------- src/commands/moderation/kickCommand.ts | 121 +++++++++++ src/commands/moderation/modlog.ts | 148 -------------- src/commands/moderation/modlogCommand.ts | 149 ++++++++++++++ src/commands/moderation/role.ts | 181 ----------------- src/commands/moderation/roleCommand.ts | 181 +++++++++++++++++ src/commands/moderation/warn.ts | 67 ------ src/commands/moderation/warnCommand.ts | 68 +++++++ .../moulberry-bush/capePermissionsCommand.ts | 126 ++++++++++++ src/commands/moulberry-bush/capeperms.ts | 126 ------------ src/commands/moulberry-bush/giveawayPingCommand.ts | 68 +++++++ src/commands/moulberry-bush/giveawayping.ts | 68 ------- src/commands/moulberry-bush/level.ts | 62 ------ src/commands/moulberry-bush/levelCommand.ts | 62 ++++++ src/commands/moulberry-bush/rule.ts | 188 ----------------- src/commands/moulberry-bush/ruleCommand.ts | 188 +++++++++++++++++ src/commands/server-config/prefix.ts | 71 ------- src/commands/server-config/prefixCommand.ts | 71 +++++++ src/listeners/client/ready.ts | 18 -- src/listeners/client/readyListener.ts | 18 ++ src/listeners/client/syncSlashCommandsListener.ts | 22 ++ src/listeners/client/syncslashcommands.ts | 22 -- src/listeners/commands/commandBlockedListener.ts | 34 ++++ src/listeners/commands/commandErrorListener.ts | 48 +++++ src/listeners/commands/commandStartedListener.ts | 24 +++ src/listeners/commands/commandblocked.ts | 34 ---- src/listeners/commands/commandstarted.ts | 24 --- src/listeners/commands/error.ts | 48 ----- .../commands/slashCommandErrorListener.ts | 48 +++++ src/listeners/commands/slashError.ts | 48 ----- src/listeners/guild/syncUnbanListener.ts | 24 +++ src/listeners/guild/syncunban.ts | 24 --- src/listeners/message/levelListener.ts | 40 ++++ src/listeners/message/levels.ts | 39 ---- src/tasks/Unban.ts | 49 ----- src/tasks/UnbanTask.ts | 49 +++++ 52 files changed, 2199 insertions(+), 2194 deletions(-) delete mode 100644 src/commands/dev/eval.ts create mode 100644 src/commands/dev/evalCommand.ts delete mode 100644 src/commands/dev/reload.ts create mode 100644 src/commands/dev/reloadCommand.ts create mode 100644 src/commands/dev/setLevelCommand.ts delete mode 100644 src/commands/dev/setlevel.ts create mode 100644 src/commands/info/botInfoCommand.ts delete mode 100644 src/commands/info/botinfo.ts delete mode 100644 src/commands/info/help.ts create mode 100644 src/commands/info/helpCommand.ts delete mode 100644 src/commands/info/ping.ts create mode 100644 src/commands/info/pingCommand.ts delete mode 100644 src/commands/info/pronouns.ts create mode 100644 src/commands/info/pronounsCommand.ts delete mode 100644 src/commands/moderation/ban.ts create mode 100644 src/commands/moderation/banCommand.ts delete mode 100644 src/commands/moderation/kick.ts create mode 100644 src/commands/moderation/kickCommand.ts delete mode 100644 src/commands/moderation/modlog.ts create mode 100644 src/commands/moderation/modlogCommand.ts delete mode 100644 src/commands/moderation/role.ts create mode 100644 src/commands/moderation/roleCommand.ts delete mode 100644 src/commands/moderation/warn.ts create mode 100644 src/commands/moderation/warnCommand.ts create mode 100644 src/commands/moulberry-bush/capePermissionsCommand.ts delete mode 100644 src/commands/moulberry-bush/capeperms.ts create mode 100644 src/commands/moulberry-bush/giveawayPingCommand.ts delete mode 100644 src/commands/moulberry-bush/giveawayping.ts delete mode 100644 src/commands/moulberry-bush/level.ts create mode 100644 src/commands/moulberry-bush/levelCommand.ts delete mode 100644 src/commands/moulberry-bush/rule.ts create mode 100644 src/commands/moulberry-bush/ruleCommand.ts delete mode 100644 src/commands/server-config/prefix.ts create mode 100644 src/commands/server-config/prefixCommand.ts delete mode 100644 src/listeners/client/ready.ts create mode 100644 src/listeners/client/readyListener.ts create mode 100644 src/listeners/client/syncSlashCommandsListener.ts delete mode 100644 src/listeners/client/syncslashcommands.ts create mode 100644 src/listeners/commands/commandBlockedListener.ts create mode 100644 src/listeners/commands/commandErrorListener.ts create mode 100644 src/listeners/commands/commandStartedListener.ts delete mode 100644 src/listeners/commands/commandblocked.ts delete mode 100644 src/listeners/commands/commandstarted.ts delete mode 100644 src/listeners/commands/error.ts create mode 100644 src/listeners/commands/slashCommandErrorListener.ts delete mode 100644 src/listeners/commands/slashError.ts create mode 100644 src/listeners/guild/syncUnbanListener.ts delete mode 100644 src/listeners/guild/syncunban.ts create mode 100644 src/listeners/message/levelListener.ts delete mode 100644 src/listeners/message/levels.ts delete mode 100644 src/tasks/Unban.ts create mode 100644 src/tasks/UnbanTask.ts (limited to 'src/commands/moulberry-bush/rule.ts') diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts deleted file mode 100644 index 83e63f6..0000000 --- a/src/commands/dev/eval.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* 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'; - -const clean = (text) => { - if (typeof text === 'string') - return text - .replace(/`/g, '`' + String.fromCharCode(8203)) - .replace(/@/g, '@' + String.fromCharCode(8203)); - else return text; -}; - -export default class EvalCommand extends BushCommand { - public constructor() { - super('eval', { - aliases: ['eval', 'ev'], - category: 'dev', - description: { - content: 'Use the command to eval stuff in the bot.', - usage: 'eval [--silent] [--depth #]', - examples: ['eval message.guild.name', 'eval this.client.ownerID'] - }, - args: [ - { - id: 'depth', - match: 'option', - type: 'number', - flag: '--depth', - default: 0 - }, - { - id: 'silent', - match: 'flag', - flag: '--silent' - }, - { - id: 'code', - match: 'rest', - type: 'string', - prompt: { - start: 'What would you like to eval?', - retry: 'Invalid code to eval. What would you like to eval?' - } - } - ], - ownerOnly: true, - clientPermissions: ['EMBED_LINKS'] - }); - } - - public async exec( - message: Message, - { depth, code, silent }: { depth: number; code: string; silent: boolean } - ): Promise { - const embed: MessageEmbed = new MessageEmbed(); - - try { - let output; - const me = message.member, - member = message.member, - bot = this.client, - guild = message.guild, - channel = message.channel, - config = this.client.config, - 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]' - ); - output = clean(output); - 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 }) - ) - .setTimestamp(); - } catch (e) { - 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 }) - ) - .setTimestamp(); - } - if (!silent) { - await message.util.send(embed); - } else { - try { - await message.author.send(embed); - await message.react(''); - } catch (e) { - await message.react('❌'); - } - } - } -} diff --git a/src/commands/dev/evalCommand.ts b/src/commands/dev/evalCommand.ts new file mode 100644 index 0000000..83e63f6 --- /dev/null +++ b/src/commands/dev/evalCommand.ts @@ -0,0 +1,138 @@ +/* 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'; + +const clean = (text) => { + if (typeof text === 'string') + return text + .replace(/`/g, '`' + String.fromCharCode(8203)) + .replace(/@/g, '@' + String.fromCharCode(8203)); + else return text; +}; + +export default class EvalCommand extends BushCommand { + public constructor() { + super('eval', { + aliases: ['eval', 'ev'], + category: 'dev', + description: { + content: 'Use the command to eval stuff in the bot.', + usage: 'eval [--silent] [--depth #]', + examples: ['eval message.guild.name', 'eval this.client.ownerID'] + }, + args: [ + { + id: 'depth', + match: 'option', + type: 'number', + flag: '--depth', + default: 0 + }, + { + id: 'silent', + match: 'flag', + flag: '--silent' + }, + { + id: 'code', + match: 'rest', + type: 'string', + prompt: { + start: 'What would you like to eval?', + retry: 'Invalid code to eval. What would you like to eval?' + } + } + ], + ownerOnly: true, + clientPermissions: ['EMBED_LINKS'] + }); + } + + public async exec( + message: Message, + { depth, code, silent }: { depth: number; code: string; silent: boolean } + ): Promise { + const embed: MessageEmbed = new MessageEmbed(); + + try { + let output; + const me = message.member, + member = message.member, + bot = this.client, + guild = message.guild, + channel = message.channel, + config = this.client.config, + 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]' + ); + output = clean(output); + 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 }) + ) + .setTimestamp(); + } catch (e) { + 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 }) + ) + .setTimestamp(); + } + if (!silent) { + await message.util.send(embed); + } else { + try { + await message.author.send(embed); + await message.react(''); + } catch (e) { + await message.react('❌'); + } + } + } +} diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts deleted file mode 100644 index 64e5745..0000000 --- a/src/commands/dev/reload.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { stripIndent } from 'common-tags'; -import { Message } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; - -export default class ReloadCommand extends BushCommand { - constructor() { - super('reload', { - aliases: ['reload'], - category: 'dev', - description: { - content: 'Reloads the bot', - usage: 'reload', - examples: ['reload'] - }, - ownerOnly: true, - typing: true - }); - } - - private async getResponse(): Promise { - try { - await this.client.util.shell('yarn rimraf dist/'); - await this.client.util.shell('yarn tsc'); - this.client.commandHandler.reloadAll(); - this.client.listenerHandler.reloadAll(); - this.client.inhibitorHandler.reloadAll(); - return '🔁 Successfully reloaded!'; - } catch (e) { - return stripIndent` - An error occured while reloading: - ${await this.client.util.haste(e.stack)} - `; - } - } - - public async exec(message: Message): Promise { - await message.util.send(await this.getResponse()); - } - - public async execSlash(message: CommandInteraction): Promise { - await message.reply(await this.getResponse()); - } -} diff --git a/src/commands/dev/reloadCommand.ts b/src/commands/dev/reloadCommand.ts new file mode 100644 index 0000000..64e5745 --- /dev/null +++ b/src/commands/dev/reloadCommand.ts @@ -0,0 +1,44 @@ +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { stripIndent } from 'common-tags'; +import { Message } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; + +export default class ReloadCommand extends BushCommand { + constructor() { + super('reload', { + aliases: ['reload'], + category: 'dev', + description: { + content: 'Reloads the bot', + usage: 'reload', + examples: ['reload'] + }, + ownerOnly: true, + typing: true + }); + } + + private async getResponse(): Promise { + try { + await this.client.util.shell('yarn rimraf dist/'); + await this.client.util.shell('yarn tsc'); + this.client.commandHandler.reloadAll(); + this.client.listenerHandler.reloadAll(); + this.client.inhibitorHandler.reloadAll(); + return '🔁 Successfully reloaded!'; + } catch (e) { + return stripIndent` + An error occured while reloading: + ${await this.client.util.haste(e.stack)} + `; + } + } + + public async exec(message: Message): Promise { + await message.util.send(await this.getResponse()); + } + + public async execSlash(message: CommandInteraction): Promise { + await message.reply(await this.getResponse()); + } +} diff --git a/src/commands/dev/setLevelCommand.ts b/src/commands/dev/setLevelCommand.ts new file mode 100644 index 0000000..d1701c5 --- /dev/null +++ b/src/commands/dev/setLevelCommand.ts @@ -0,0 +1,91 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction } from 'discord.js'; +import { User } from 'discord.js'; +import { Message } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { Level } from '../../lib/models'; +import AllowedMentions from '../../lib/utils/AllowedMentions'; + +export default class SetLevelCommand extends BushCommand { + constructor() { + super('setlevel', { + aliases: ['setlevel'], + category: 'dev', + description: { + content: 'Sets the level of a user', + usage: 'setlevel ', + examples: ['setlevel @Moulberry 69'] //nice + }, + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to change the level of?', + retry: + 'Invalid user. What user would you like to change the level of?' + } + }, + { + id: 'level', + type: 'number', + prompt: { + start: 'What level would you like to set?', + retry: 'Invalid user. What level would you like to set?' + } + } + ], + ownerOnly: true, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to change the level of', + required: true + }, + { + type: ApplicationCommandOptionType.INTEGER, + name: 'level', + description: 'The level to set the user to', + required: true + } + ] + }); + } + + private async setLevel(user: User, level: number): Promise { + const [levelEntry] = await Level.findOrBuild({ + where: { + id: user.id + }, + defaults: { + id: user.id + } + }); + levelEntry.xp = Level.convertLevelToXp(level); + await levelEntry.save(); + return `Successfully set level of <@${user.id}> to \`${level}\` (\`${levelEntry.xp}\` XP)`; + } + + async exec( + message: Message, + { user, level }: { user: User; level: number } + ): Promise { + await message.util.send(await this.setLevel(user, level), { + allowedMentions: AllowedMentions.none() + }); + } + + async execSlash( + message: CommandInteraction, + { + user, + level + }: { user: SlashCommandOption; level: SlashCommandOption } + ): Promise { + await message.reply(await this.setLevel(user.user, level.value), { + allowedMentions: AllowedMentions.none() + }); + } +} diff --git a/src/commands/dev/setlevel.ts b/src/commands/dev/setlevel.ts deleted file mode 100644 index d1701c5..0000000 --- a/src/commands/dev/setlevel.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction } from 'discord.js'; -import { User } from 'discord.js'; -import { Message } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { SlashCommandOption } from '../../lib/extensions/Util'; -import { Level } from '../../lib/models'; -import AllowedMentions from '../../lib/utils/AllowedMentions'; - -export default class SetLevelCommand extends BushCommand { - constructor() { - super('setlevel', { - aliases: ['setlevel'], - category: 'dev', - description: { - content: 'Sets the level of a user', - usage: 'setlevel ', - examples: ['setlevel @Moulberry 69'] //nice - }, - args: [ - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to change the level of?', - retry: - 'Invalid user. What user would you like to change the level of?' - } - }, - { - id: 'level', - type: 'number', - prompt: { - start: 'What level would you like to set?', - retry: 'Invalid user. What level would you like to set?' - } - } - ], - ownerOnly: true, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to change the level of', - required: true - }, - { - type: ApplicationCommandOptionType.INTEGER, - name: 'level', - description: 'The level to set the user to', - required: true - } - ] - }); - } - - private async setLevel(user: User, level: number): Promise { - const [levelEntry] = await Level.findOrBuild({ - where: { - id: user.id - }, - defaults: { - id: user.id - } - }); - levelEntry.xp = Level.convertLevelToXp(level); - await levelEntry.save(); - return `Successfully set level of <@${user.id}> to \`${level}\` (\`${levelEntry.xp}\` XP)`; - } - - async exec( - message: Message, - { user, level }: { user: User; level: number } - ): Promise { - await message.util.send(await this.setLevel(user, level), { - allowedMentions: AllowedMentions.none() - }); - } - - async execSlash( - message: CommandInteraction, - { - user, - level - }: { user: SlashCommandOption; level: SlashCommandOption } - ): Promise { - await message.reply(await this.setLevel(user.user, level.value), { - allowedMentions: AllowedMentions.none() - }); - } -} diff --git a/src/commands/info/botInfoCommand.ts b/src/commands/info/botInfoCommand.ts new file mode 100644 index 0000000..e6a4fbb --- /dev/null +++ b/src/commands/info/botInfoCommand.ts @@ -0,0 +1,65 @@ +import { MessageEmbed, Message, CommandInteraction } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { duration } from 'moment'; + +export default class BotInfoCommand extends BushCommand { + constructor() { + super('botinfo', { + aliases: ['botinfo'], + description: { + content: 'Shows information about the bot', + usage: 'botinfo', + examples: ['botinfo'] + } + }); + } + + private async generateEmbed(): Promise { + const owners = (await this.client.util.mapIDs(this.client.ownerID)) + .map((u) => u.tag) + .join('\n'); + const currentCommit = ( + await this.client.util.shell('git rev-parse HEAD') + ).stdout.replace('\n', ''); + const repoUrl = ( + await this.client.util.shell('git remote get-url origin') + ).stdout.replace('\n', ''); + const embed = new MessageEmbed() + .setTitle('Bot Info:') + .addFields([ + { + name: 'Owners', + value: owners, + inline: true + }, + { + name: 'Uptime', + value: this.client.util.capitalize( + duration(this.client.uptime, 'milliseconds').humanize() + ) + }, + { + name: 'User count', + value: this.client.users.cache.size, + inline: true + }, + { + name: 'Current commit', + value: `[${currentCommit.substring( + 0, + 7 + )}](${repoUrl}/commit/${currentCommit})` + } + ]) + .setTimestamp(); + return embed; + } + + public async exec(message: Message): Promise { + await message.util.send(await this.generateEmbed()); + } + + public async execSlash(message: CommandInteraction): Promise { + await message.reply(await this.generateEmbed()); + } +} diff --git a/src/commands/info/botinfo.ts b/src/commands/info/botinfo.ts deleted file mode 100644 index e6a4fbb..0000000 --- a/src/commands/info/botinfo.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { MessageEmbed, Message, CommandInteraction } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { duration } from 'moment'; - -export default class BotInfoCommand extends BushCommand { - constructor() { - super('botinfo', { - aliases: ['botinfo'], - description: { - content: 'Shows information about the bot', - usage: 'botinfo', - examples: ['botinfo'] - } - }); - } - - private async generateEmbed(): Promise { - const owners = (await this.client.util.mapIDs(this.client.ownerID)) - .map((u) => u.tag) - .join('\n'); - const currentCommit = ( - await this.client.util.shell('git rev-parse HEAD') - ).stdout.replace('\n', ''); - const repoUrl = ( - await this.client.util.shell('git remote get-url origin') - ).stdout.replace('\n', ''); - const embed = new MessageEmbed() - .setTitle('Bot Info:') - .addFields([ - { - name: 'Owners', - value: owners, - inline: true - }, - { - name: 'Uptime', - value: this.client.util.capitalize( - duration(this.client.uptime, 'milliseconds').humanize() - ) - }, - { - name: 'User count', - value: this.client.users.cache.size, - inline: true - }, - { - name: 'Current commit', - value: `[${currentCommit.substring( - 0, - 7 - )}](${repoUrl}/commit/${currentCommit})` - } - ]) - .setTimestamp(); - return embed; - } - - public async exec(message: Message): Promise { - await message.util.send(await this.generateEmbed()); - } - - public async execSlash(message: CommandInteraction): Promise { - await message.reply(await this.generateEmbed()); - } -} diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts deleted file mode 100644 index 30c2a21..0000000 --- a/src/commands/info/help.ts +++ /dev/null @@ -1,107 +0,0 @@ -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 { SlashCommandOption } from '../../lib/extensions/Util'; - -export default class HelpCommand extends BushCommand { - constructor() { - super('help', { - aliases: ['help'], - description: { - content: 'Shows the commands of the bot', - usage: 'help', - examples: ['help'] - }, - clientPermissions: ['EMBED_LINKS'], - args: [ - { - id: 'command', - type: 'commandAlias' - } - ], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.STRING, - name: 'command', - description: 'The command to get help for', - required: false - } - ] - }); - } - - private generateEmbed(command?: BushCommand): MessageEmbed { - const prefix = this.handler.prefix; - if (!command) { - const embed = new MessageEmbed() - .addField( - 'Commands', - stripIndent`A list of available commands. - For additional info on a command, type \`${prefix}help \` - ` - ) - .setFooter( - `For more information about a command use "${this.client.config.prefix}help "` - ) - .setTimestamp(); - for (const category of this.handler.categories.values()) { - embed.addField( - `${category.id.replace(/(\b\w)/gi, (lc): string => - lc.toUpperCase() - )}`, - `${category - .filter((cmd): boolean => cmd.aliases.length > 0) - .map((cmd): string => `\`${cmd.aliases[0]}\``) - .join(' ')}` - ); - } - return embed; - } else { - const embed = new MessageEmbed() - .setColor([155, 200, 200]) - .setTitle( - `\`${command.description.usage ? command.description.usage : ''}\`` - ) - .addField( - 'Description', - `${command.description.content ? command.description.content : ''} ${ - command.ownerOnly ? '\n__Owner Only__' : '' - }` - ); - - if (command.aliases.length > 1) - embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true); - if (command.description.examples && command.description.examples.length) - embed.addField( - 'Examples', - `\`${command.description.examples.join('`\n`')}\``, - true - ); - return embed; - } - } - - public async exec( - message: Message, - { command }: { command: BushCommand } - ): Promise { - await message.util.send(this.generateEmbed(command)); - } - - public async execSlash( - message: CommandInteraction, - { command }: { command: SlashCommandOption } - ): Promise { - if (command) { - await message.reply( - this.generateEmbed( - this.handler.findCommand(command.value) as BushCommand - ) - ); - } else { - await message.reply(this.generateEmbed()); - } - } -} diff --git a/src/commands/info/helpCommand.ts b/src/commands/info/helpCommand.ts new file mode 100644 index 0000000..30c2a21 --- /dev/null +++ b/src/commands/info/helpCommand.ts @@ -0,0 +1,107 @@ +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 { SlashCommandOption } from '../../lib/extensions/Util'; + +export default class HelpCommand extends BushCommand { + constructor() { + super('help', { + aliases: ['help'], + description: { + content: 'Shows the commands of the bot', + usage: 'help', + examples: ['help'] + }, + clientPermissions: ['EMBED_LINKS'], + args: [ + { + id: 'command', + type: 'commandAlias' + } + ], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.STRING, + name: 'command', + description: 'The command to get help for', + required: false + } + ] + }); + } + + private generateEmbed(command?: BushCommand): MessageEmbed { + const prefix = this.handler.prefix; + if (!command) { + const embed = new MessageEmbed() + .addField( + 'Commands', + stripIndent`A list of available commands. + For additional info on a command, type \`${prefix}help \` + ` + ) + .setFooter( + `For more information about a command use "${this.client.config.prefix}help "` + ) + .setTimestamp(); + for (const category of this.handler.categories.values()) { + embed.addField( + `${category.id.replace(/(\b\w)/gi, (lc): string => + lc.toUpperCase() + )}`, + `${category + .filter((cmd): boolean => cmd.aliases.length > 0) + .map((cmd): string => `\`${cmd.aliases[0]}\``) + .join(' ')}` + ); + } + return embed; + } else { + const embed = new MessageEmbed() + .setColor([155, 200, 200]) + .setTitle( + `\`${command.description.usage ? command.description.usage : ''}\`` + ) + .addField( + 'Description', + `${command.description.content ? command.description.content : ''} ${ + command.ownerOnly ? '\n__Owner Only__' : '' + }` + ); + + if (command.aliases.length > 1) + embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true); + if (command.description.examples && command.description.examples.length) + embed.addField( + 'Examples', + `\`${command.description.examples.join('`\n`')}\``, + true + ); + return embed; + } + } + + public async exec( + message: Message, + { command }: { command: BushCommand } + ): Promise { + await message.util.send(this.generateEmbed(command)); + } + + public async execSlash( + message: CommandInteraction, + { command }: { command: SlashCommandOption } + ): Promise { + if (command) { + await message.reply( + this.generateEmbed( + this.handler.findCommand(command.value) as BushCommand + ) + ); + } else { + await message.reply(this.generateEmbed()); + } + } +} diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts deleted file mode 100644 index 62b8e60..0000000 --- a/src/commands/info/ping.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { CommandInteraction } from 'discord.js'; -import { Message } from 'discord.js'; -import { MessageEmbed } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; - -export default class PingCommand extends BushCommand { - constructor() { - super('ping', { - aliases: ['ping'], - description: { - content: 'Gets the latency of the bot', - usage: 'ping', - examples: ['ping'] - } - }); - } - - public async exec(message: Message): Promise { - const sentMessage = await message.util.send('Pong!'); - const timestamp: number = message.editedTimestamp - ? message.editedTimestamp - : message.createdTimestamp; - const botLatency = `\`\`\`\n ${Math.floor( - sentMessage.createdTimestamp - timestamp - )}ms \`\`\``; - const apiLatency = `\`\`\`\n ${Math.round( - message.client.ws.ping - )}ms \`\`\``; - const embed = new MessageEmbed() - .setTitle('Pong! 🏓') - .addField('Bot Latency', botLatency, true) - .addField('API Latency', apiLatency, true) - .setFooter( - message.author.username, - message.author.displayAvatarURL({ dynamic: true }) - ) - .setTimestamp(); - await sentMessage.edit({ - content: null, - embed - }); - } - - public async execSlash(message: CommandInteraction): Promise { - const timestamp1 = message.createdTimestamp; - await message.reply('Pong!'); - const timestamp2 = await message - .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 }) - ) - .setTimestamp(); - await message.editReply({ - content: null, - embeds: [embed] - }); - } -} diff --git a/src/commands/info/pingCommand.ts b/src/commands/info/pingCommand.ts new file mode 100644 index 0000000..62b8e60 --- /dev/null +++ b/src/commands/info/pingCommand.ts @@ -0,0 +1,68 @@ +import { CommandInteraction } from 'discord.js'; +import { Message } from 'discord.js'; +import { MessageEmbed } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; + +export default class PingCommand extends BushCommand { + constructor() { + super('ping', { + aliases: ['ping'], + description: { + content: 'Gets the latency of the bot', + usage: 'ping', + examples: ['ping'] + } + }); + } + + public async exec(message: Message): Promise { + const sentMessage = await message.util.send('Pong!'); + const timestamp: number = message.editedTimestamp + ? message.editedTimestamp + : message.createdTimestamp; + const botLatency = `\`\`\`\n ${Math.floor( + sentMessage.createdTimestamp - timestamp + )}ms \`\`\``; + const apiLatency = `\`\`\`\n ${Math.round( + message.client.ws.ping + )}ms \`\`\``; + const embed = new MessageEmbed() + .setTitle('Pong! 🏓') + .addField('Bot Latency', botLatency, true) + .addField('API Latency', apiLatency, true) + .setFooter( + message.author.username, + message.author.displayAvatarURL({ dynamic: true }) + ) + .setTimestamp(); + await sentMessage.edit({ + content: null, + embed + }); + } + + public async execSlash(message: CommandInteraction): Promise { + const timestamp1 = message.createdTimestamp; + await message.reply('Pong!'); + const timestamp2 = await message + .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 }) + ) + .setTimestamp(); + await message.editReply({ + content: null, + embeds: [embed] + }); + } +} diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts deleted file mode 100644 index f30a981..0000000 --- a/src/commands/info/pronouns.ts +++ /dev/null @@ -1,121 +0,0 @@ -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 { SlashCommandOption } from '../../lib/extensions/Util'; - -export const pronounMapping = { - unspecified: 'Unspecified', - hh: 'He/Him', - hi: 'He/It', - hs: 'He/She', - ht: 'He/They', - ih: 'It/Him', - ii: 'It/Its', - is: 'It/She', - it: 'It/They', - shh: 'She/He', - sh: 'She/Her', - si: 'She/It', - st: 'She/They', - th: 'They/He', - ti: 'They/It', - ts: 'They/She', - tt: 'They/Them', - any: 'Any pronouns', - other: 'Other pronouns', - ask: 'Ask me my pronouns', - avoid: 'Avoid pronouns, use my name' -}; -export type pronounsType = keyof typeof pronounMapping; - -export default class PronounsCommand extends BushCommand { - constructor() { - super('pronouns', { - aliases: ['pronouns', 'pronoun'], - category: 'utilities', - description: { - usage: 'pronouns ', - examples: ['pronouns IRONM00N'], - content: 'Finds the pronouns of a user using https://pronoundb.org.' - }, - args: [ - { - id: 'user', - type: 'user', - default: null - } - ], - clientPermissions: ['SEND_MESSAGES'], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to get pronouns for', - required: false - } - ], - slashEmphemeral: true // I'll add dynamic checking to this later - }); - } - async sendResponse( - message: Message | CommandInteraction, - user: User, - author: boolean - ): Promise { - try { - const apiRes: { pronouns: pronounsType } = await got - .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/' - } - }) - ); - } else { - message.reply({ - embeds: [ - new MessageEmbed({ - title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`, - description: pronounMapping[apiRes.pronouns], - footer: { - text: 'Data provided by https://pronoundb.org/' - } - }) - ] - }); - } - } catch (e) { - if (e instanceof HTTPError && e.response.statusCode === 404) { - if (author) { - await message.reply( - 'You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.' - ); - } else { - await message.reply( - `${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.` - ); - } - } else throw e; - } - } - async exec(message: Message, { user }: { user?: User }): Promise { - const u = user || message.author; - await this.sendResponse(message, u, u.id === message.author.id); - } - async execSlash( - message: CommandInteraction, - { user }: { user?: SlashCommandOption } - ): Promise { - const u = user?.user || message.user; - await this.sendResponse(message, u, u.id === message.user.id); - } -} diff --git a/src/commands/info/pronounsCommand.ts b/src/commands/info/pronounsCommand.ts new file mode 100644 index 0000000..f30a981 --- /dev/null +++ b/src/commands/info/pronounsCommand.ts @@ -0,0 +1,121 @@ +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 { SlashCommandOption } from '../../lib/extensions/Util'; + +export const pronounMapping = { + unspecified: 'Unspecified', + hh: 'He/Him', + hi: 'He/It', + hs: 'He/She', + ht: 'He/They', + ih: 'It/Him', + ii: 'It/Its', + is: 'It/She', + it: 'It/They', + shh: 'She/He', + sh: 'She/Her', + si: 'She/It', + st: 'She/They', + th: 'They/He', + ti: 'They/It', + ts: 'They/She', + tt: 'They/Them', + any: 'Any pronouns', + other: 'Other pronouns', + ask: 'Ask me my pronouns', + avoid: 'Avoid pronouns, use my name' +}; +export type pronounsType = keyof typeof pronounMapping; + +export default class PronounsCommand extends BushCommand { + constructor() { + super('pronouns', { + aliases: ['pronouns', 'pronoun'], + category: 'utilities', + description: { + usage: 'pronouns ', + examples: ['pronouns IRONM00N'], + content: 'Finds the pronouns of a user using https://pronoundb.org.' + }, + args: [ + { + id: 'user', + type: 'user', + default: null + } + ], + clientPermissions: ['SEND_MESSAGES'], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to get pronouns for', + required: false + } + ], + slashEmphemeral: true // I'll add dynamic checking to this later + }); + } + async sendResponse( + message: Message | CommandInteraction, + user: User, + author: boolean + ): Promise { + try { + const apiRes: { pronouns: pronounsType } = await got + .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/' + } + }) + ); + } else { + message.reply({ + embeds: [ + new MessageEmbed({ + title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`, + description: pronounMapping[apiRes.pronouns], + footer: { + text: 'Data provided by https://pronoundb.org/' + } + }) + ] + }); + } + } catch (e) { + if (e instanceof HTTPError && e.response.statusCode === 404) { + if (author) { + await message.reply( + 'You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.' + ); + } else { + await message.reply( + `${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.` + ); + } + } else throw e; + } + } + async exec(message: Message, { user }: { user?: User }): Promise { + const u = user || message.author; + await this.sendResponse(message, u, u.id === message.author.id); + } + async execSlash( + message: CommandInteraction, + { user }: { user?: SlashCommandOption } + ): Promise { + const u = user?.user || message.user; + await this.sendResponse(message, u, u.id === message.user.id); + } +} diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts deleted file mode 100644 index 1c3b074..0000000 --- a/src/commands/moderation/ban.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { User } from 'discord.js'; -import { Guild } from '../../lib/models'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { Ban, Modlog, ModlogType } from '../../lib/models'; -import moment from 'moment'; -import { Message } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; -import { SlashCommandOption } from '../../lib/extensions/Util'; -import { ApplicationCommandOptionType } from 'discord-api-types'; - -const durationAliases: Record = { - weeks: ['w', 'weeks', 'week', 'wk', 'wks'], - days: ['d', 'days', 'day'], - hours: ['h', 'hours', 'hour', 'hr', 'hrs'], - minutes: ['m', 'min', 'mins', 'minutes', 'minute'], - months: ['mo', 'month', 'months'] -}; -const durationRegex = - /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; - -export default class PrefixCommand extends BushCommand { - constructor() { - super('ban', { - aliases: ['ban'], - args: [ - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to ban?', - retry: 'Invalid response. What user would you like to ban?' - } - }, - { - id: 'reason', - match: 'rest' - }, - { - id: 'time', - match: 'option', - flag: '--time' - } - ], - clientPermissions: ['BAN_MEMBERS'], - userPermissions: ['BAN_MEMBERS'], - description: { - content: - 'Ban a member and log it in modlogs (with optional time to unban)', - usage: 'ban [--time]', - examples: [ - 'ban @Tyman being cool', - 'ban @Tyman being cool --time 7days' - ] - }, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to ban', - required: true - }, - { - type: ApplicationCommandOptionType.STRING, - name: 'reason', - description: 'The reason to show in modlogs and audit log', - required: false - }, - { - type: ApplicationCommandOptionType.STRING, - name: 'time', - description: - 'The time the user should be banned for (default permanent)', - required: false - } - ] - }); - } - async *genResponses( - message: Message | CommandInteraction, - user: User, - reason?: string, - time?: string - ): AsyncIterable { - const duration = moment.duration(); - let modlogEnry: Modlog; - let banEntry: Ban; - const translatedTime: string[] = []; - // Create guild entry so postgres doesn't get mad when I try and add a modlog entry - await Guild.findOrCreate({ - where: { - id: message.guild.id - }, - defaults: { - id: message.guild.id - } - }); - try { - try { - if (time) { - const parsed = [...time.matchAll(durationRegex)]; - if (parsed.length < 1) { - yield 'Invalid time.'; - return; - } - for (const part of parsed) { - const translated = Object.keys(durationAliases).find((k) => - durationAliases[k].includes(part[2]) - ); - translatedTime.push(part[1] + ' ' + translated); - duration.add( - Number(part[1]), - translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes' - ); - } - modlogEnry = Modlog.build({ - user: user.id, - guild: message.guild.id, - reason, - type: ModlogType.TEMPBAN, - duration: duration.asMilliseconds(), - moderator: - message instanceof CommandInteraction - ? message.user.id - : message.author.id - }); - banEntry = Ban.build({ - user: user.id, - guild: message.guild.id, - reason, - expires: new Date(new Date().getTime() + duration.asMilliseconds()), - modlog: modlogEnry.id - }); - } else { - modlogEnry = Modlog.build({ - user: user.id, - guild: message.guild.id, - reason, - type: ModlogType.BAN, - moderator: - message instanceof CommandInteraction - ? message.user.id - : message.author.id - }); - banEntry = Ban.build({ - user: user.id, - guild: message.guild.id, - reason, - modlog: modlogEnry.id - }); - } - await modlogEnry.save(); - await banEntry.save(); - } catch (e) { - console.error(e); - yield 'Error saving to database. Please report this to a developer.'; - return; - } - try { - await user.send( - `You were banned in ${message.guild.name} ${ - translatedTime.length >= 1 - ? `for ${translatedTime.join(', ')}` - : 'permanently' - } with reason \`${reason || 'No reason given'}\`` - ); - } catch (e) { - yield 'Error sending message to user'; - } - await message.guild.members.ban(user, { - reason: `Banned by ${ - message instanceof CommandInteraction - ? message.user.tag - : message.author.tag - } with ${reason ? `reason ${reason}` : 'no reason'}` - }); - yield `Banned <@!${user.id}> ${ - translatedTime.length >= 1 - ? `for ${translatedTime.join(', ')}` - : 'permanently' - } with reason \`${reason || 'No reason given'}\``; - } catch { - yield 'Error banning :/'; - await banEntry.destroy(); - await modlogEnry.destroy(); - return; - } - } - async exec( - message: Message, - { user, reason, time }: { user: User; reason?: string; time?: string } - ): Promise { - for await (const response of this.genResponses( - message, - user, - reason, - time - )) { - await message.util.send(response); - } - } - - async execSlash( - message: CommandInteraction, - { - user, - reason, - time - }: { - user: SlashCommandOption; - reason: SlashCommandOption; - time: SlashCommandOption; - } - ): Promise { - for await (const response of this.genResponses( - message, - user.user, - reason?.value, - time?.value - )) { - await message.reply(response); - } - } -} diff --git a/src/commands/moderation/banCommand.ts b/src/commands/moderation/banCommand.ts new file mode 100644 index 0000000..354fc0d --- /dev/null +++ b/src/commands/moderation/banCommand.ts @@ -0,0 +1,224 @@ +import { User } from 'discord.js'; +import { Guild } from '../../lib/models'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Ban, Modlog, ModlogType } from '../../lib/models'; +import moment from 'moment'; +import { Message } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { ApplicationCommandOptionType } from 'discord-api-types'; + +const durationAliases: Record = { + weeks: ['w', 'weeks', 'week', 'wk', 'wks'], + days: ['d', 'days', 'day'], + hours: ['h', 'hours', 'hour', 'hr', 'hrs'], + minutes: ['m', 'min', 'mins', 'minutes', 'minute'], + months: ['mo', 'month', 'months'] +}; +const durationRegex = + /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; + +export default class BanCommand extends BushCommand { + constructor() { + super('ban', { + aliases: ['ban'], + category: "moderation", + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to ban?', + retry: 'Invalid response. What user would you like to ban?' + } + }, + { + id: 'reason', + match: 'rest' + }, + { + id: 'time', + match: 'option', + flag: '--time' + } + ], + clientPermissions: ['BAN_MEMBERS'], + userPermissions: ['BAN_MEMBERS'], + description: { + content: + 'Ban a member and log it in modlogs (with optional time to unban)', + usage: 'ban [--time]', + examples: [ + 'ban @Tyman being cool', + 'ban @Tyman being cool --time 7days' + ] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to ban', + required: true + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'reason', + description: 'The reason to show in modlogs and audit log', + required: false + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'time', + description: + 'The time the user should be banned for (default permanent)', + required: false + } + ] + }); + } + async *genResponses( + message: Message | CommandInteraction, + user: User, + reason?: string, + time?: string + ): AsyncIterable { + const duration = moment.duration(); + let modlogEnry: Modlog; + let banEntry: Ban; + const translatedTime: string[] = []; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + try { + if (time) { + const parsed = [...time.matchAll(durationRegex)]; + if (parsed.length < 1) { + yield 'Invalid time.'; + return; + } + for (const part of parsed) { + const translated = Object.keys(durationAliases).find((k) => + durationAliases[k].includes(part[2]) + ); + translatedTime.push(part[1] + ' ' + translated); + duration.add( + Number(part[1]), + translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes' + ); + } + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + reason, + type: ModlogType.TEMPBAN, + duration: duration.asMilliseconds(), + moderator: + message instanceof CommandInteraction + ? message.user.id + : message.author.id + }); + banEntry = Ban.build({ + user: user.id, + guild: message.guild.id, + reason, + expires: new Date(new Date().getTime() + duration.asMilliseconds()), + modlog: modlogEnry.id + }); + } else { + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + reason, + type: ModlogType.BAN, + moderator: + message instanceof CommandInteraction + ? message.user.id + : message.author.id + }); + banEntry = Ban.build({ + user: user.id, + guild: message.guild.id, + reason, + modlog: modlogEnry.id + }); + } + await modlogEnry.save(); + await banEntry.save(); + } catch (e) { + console.error(e); + yield 'Error saving to database. Please report this to a developer.'; + return; + } + try { + await user.send( + `You were banned in ${message.guild.name} ${ + translatedTime.length >= 1 + ? `for ${translatedTime.join(', ')}` + : 'permanently' + } with reason \`${reason || 'No reason given'}\`` + ); + } catch (e) { + yield 'Error sending message to user'; + } + await message.guild.members.ban(user, { + reason: `Banned by ${ + message instanceof CommandInteraction + ? message.user.tag + : message.author.tag + } with ${reason ? `reason ${reason}` : 'no reason'}` + }); + yield `Banned <@!${user.id}> ${ + translatedTime.length >= 1 + ? `for ${translatedTime.join(', ')}` + : 'permanently' + } with reason \`${reason || 'No reason given'}\``; + } catch { + yield 'Error banning :/'; + await banEntry.destroy(); + await modlogEnry.destroy(); + return; + } + } + async exec( + message: Message, + { user, reason, time }: { user: User; reason?: string; time?: string } + ): Promise { + for await (const response of this.genResponses( + message, + user, + reason, + time + )) { + await message.util.send(response); + } + } + + async execSlash( + message: CommandInteraction, + { + user, + reason, + time + }: { + user: SlashCommandOption; + reason: SlashCommandOption; + time: SlashCommandOption; + } + ): Promise { + for await (const response of this.genResponses( + message, + user.user, + reason?.value, + time?.value + )) { + await message.reply(response); + } + } +} diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts deleted file mode 100644 index c21352f..0000000 --- a/src/commands/moderation/kick.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; -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() { - super('kick', { - aliases: ['kick'], - args: [ - { - id: 'user', - type: 'member', - prompt: { - start: 'What user would you like to kick?', - retry: 'Invalid response. What user would you like to kick?' - } - }, - { - id: 'reason' - } - ], - clientPermissions: ['KICK_MEMBERS'], - userPermissions: ['KICK_MEMBERS'], - description: { - content: 'Kick a member and log it in modlogs', - usage: 'kick ', - examples: ['kick @Tyman being cool'] - }, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to kick', - required: true - }, - { - type: ApplicationCommandOptionType.STRING, - name: 'reason', - description: 'The reason to show in modlogs and audit log', - required: false - } - ] - }); - } - - private async *genResponses( - message: Message | CommandInteraction, - user: GuildMember, - reason?: string - ): AsyncIterable { - let modlogEnry: Modlog; - // Create guild entry so postgres doesn't get mad when I try and add a modlog entry - await Guild.findOrCreate({ - where: { - id: message.guild.id - }, - defaults: { - id: message.guild.id - } - }); - try { - modlogEnry = Modlog.build({ - user: user.id, - guild: message.guild.id, - moderator: - message instanceof Message ? message.author.id : message.user.id, - type: ModlogType.KICK, - reason - }); - await modlogEnry.save(); - } catch (e) { - console.error(e); - yield 'Error saving to database. Please report this to a developer.'; - return; - } - try { - await user.send( - `You were kicked in ${message.guild.name} with reason \`${ - reason || 'No reason given' - }\`` - ); - } catch (e) { - yield 'Error sending message to user'; - } - try { - await user.kick( - `Kicked by ${ - message instanceof Message ? message.author.tag : message.user.tag - } with ${reason ? `reason ${reason}` : 'no reason'}` - ); - } catch { - yield 'Error kicking :/'; - await modlogEnry.destroy(); - return; - } - yield `Kicked <@!${user.id}> with reason \`${ - reason || 'No reason given' - }\``; - } - - async exec( - message: Message, - { user, reason }: { user: GuildMember; reason?: string } - ): Promise { - for await (const response of this.genResponses(message, user, reason)) { - await message.util.send(response); - } - } - - async execSlash( - message: CommandInteraction, - { user, reason }: { user: GuildMember; reason?: string } - ): Promise { - for await (const response of this.genResponses(message, user, reason)) { - await message.reply(response); - } - } -} diff --git a/src/commands/moderation/kickCommand.ts b/src/commands/moderation/kickCommand.ts new file mode 100644 index 0000000..132ca31 --- /dev/null +++ b/src/commands/moderation/kickCommand.ts @@ -0,0 +1,121 @@ +import { BushCommand } from '../../lib/extensions/BushCommand'; +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() { + super('kick', { + aliases: ['kick'], + category: "moderation", + args: [ + { + id: 'user', + type: 'member', + prompt: { + start: 'What user would you like to kick?', + retry: 'Invalid response. What user would you like to kick?' + } + }, + { + id: 'reason' + } + ], + clientPermissions: ['KICK_MEMBERS'], + userPermissions: ['KICK_MEMBERS'], + description: { + content: 'Kick a member and log it in modlogs', + usage: 'kick ', + examples: ['kick @Tyman being cool'] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to kick', + required: true + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'reason', + description: 'The reason to show in modlogs and audit log', + required: false + } + ] + }); + } + + private async *genResponses( + message: Message | CommandInteraction, + user: GuildMember, + reason?: string + ): AsyncIterable { + let modlogEnry: Modlog; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + moderator: + message instanceof Message ? message.author.id : message.user.id, + type: ModlogType.KICK, + reason + }); + await modlogEnry.save(); + } catch (e) { + console.error(e); + yield 'Error saving to database. Please report this to a developer.'; + return; + } + try { + await user.send( + `You were kicked in ${message.guild.name} with reason \`${ + reason || 'No reason given' + }\`` + ); + } catch (e) { + yield 'Error sending message to user'; + } + try { + await user.kick( + `Kicked by ${ + message instanceof Message ? message.author.tag : message.user.tag + } with ${reason ? `reason ${reason}` : 'no reason'}` + ); + } catch { + yield 'Error kicking :/'; + await modlogEnry.destroy(); + return; + } + yield `Kicked <@!${user.id}> with reason \`${ + reason || 'No reason given' + }\``; + } + + async exec( + message: Message, + { user, reason }: { user: GuildMember; reason?: string } + ): Promise { + for await (const response of this.genResponses(message, user, reason)) { + await message.util.send(response); + } + } + + async execSlash( + message: CommandInteraction, + { user, reason }: { user: GuildMember; reason?: string } + ): Promise { + for await (const response of this.genResponses(message, user, reason)) { + await message.reply(response); + } + } +} diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts deleted file mode 100644 index b7923ca..0000000 --- a/src/commands/moderation/modlog.ts +++ /dev/null @@ -1,148 +0,0 @@ -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'; - -export default class ModlogCommand extends BushCommand { - constructor() { - super('modlog', { - aliases: ['modlog', 'modlogs'], - args: [ - { - id: 'search', - prompt: { - start: 'What modlog id or user would you like to see?' - } - }, - { - id: 'page', - type: 'number' - } - ], - userPermissions: ['MANAGE_MESSAGES'], - description: { - content: "View a user's modlogs, or view a specific modlog entry", - usage: 'warn [page]', - examples: ['modlogs @Tyman', 'modlogs @Tyman 3'] - } - }); - } - *args(): unknown { - const search = yield { - id: 'search', - type: Argument.union('user', 'string'), - prompt: { - start: 'What modlog id or user would you like to see?' - } - }; - if (typeof search === 'string') return { search, page: null }; - else { - const page = yield { - id: 'page', - type: 'number', - prompt: { - start: 'What page?', - retry: 'Not a number. What page?', - optional: true - } - }; - return { search, page }; - } - } - async exec( - message: Message, - { search, page }: { search: string; page: number } - ): Promise { - const foundUser = await this.client.util.resolveUserAsync(search); - if (foundUser) { - const logs = await Modlog.findAll({ - where: { - guild: message.guild.id, - user: foundUser.id - }, - order: [['createdAt', 'ASC']] - }); - const niceLogs: string[] = []; - for (const log of logs) { - niceLogs.push(stripIndent` - ID: ${log.id} - Type: ${log.type.toLowerCase()} - User: <@!${log.user}> (${log.user}) - Moderator: <@!${log.moderator}> (${log.moderator}) - Duration: ${ - log.duration - ? moment.duration(log.duration, 'milliseconds').humanize() - : 'N/A' - } - Reason: ${log.reason || 'None given'} - ${this.client.util.ordinal(logs.indexOf(log) + 1)} action - `); - } - const chunked: string[][] = this.client.util.chunk(niceLogs, 3); - const embedPages = chunked.map( - (e, i) => - new MessageEmbed({ - title: `Modlogs page ${i + 1}`, - description: e.join( - '\n-------------------------------------------------------\n' - ), - footer: { - text: `Page ${i + 1}/${chunked.length}` - } - }) - ); - if (page) { - await message.util.send(embedPages[page - 1]); - return; - } else { - await message.util.send(embedPages[0]); - return; - } - } else if (search) { - const entry = await Modlog.findByPk(search); - if (!entry) { - 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 - } - ] - }) - ); - } - } -} diff --git a/src/commands/moderation/modlogCommand.ts b/src/commands/moderation/modlogCommand.ts new file mode 100644 index 0000000..3e394ba --- /dev/null +++ b/src/commands/moderation/modlogCommand.ts @@ -0,0 +1,149 @@ +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'; + +export default class ModlogCommand extends BushCommand { + constructor() { + super('modlog', { + aliases: ['modlog', 'modlogs'], + category: "moderation", + args: [ + { + id: 'search', + prompt: { + start: 'What modlog id or user would you like to see?' + } + }, + { + id: 'page', + type: 'number' + } + ], + userPermissions: ['MANAGE_MESSAGES'], + description: { + content: "View a user's modlogs, or view a specific modlog entry", + usage: 'warn [page]', + examples: ['modlogs @Tyman', 'modlogs @Tyman 3'] + } + }); + } + *args(): unknown { + const search = yield { + id: 'search', + type: Argument.union('user', 'string'), + prompt: { + start: 'What modlog id or user would you like to see?' + } + }; + if (typeof search === 'string') return { search, page: null }; + else { + const page = yield { + id: 'page', + type: 'number', + prompt: { + start: 'What page?', + retry: 'Not a number. What page?', + optional: true + } + }; + return { search, page }; + } + } + async exec( + message: Message, + { search, page }: { search: string; page: number } + ): Promise { + const foundUser = await this.client.util.resolveUserAsync(search); + if (foundUser) { + const logs = await Modlog.findAll({ + where: { + guild: message.guild.id, + user: foundUser.id + }, + order: [['createdAt', 'ASC']] + }); + const niceLogs: string[] = []; + for (const log of logs) { + niceLogs.push(stripIndent` + ID: ${log.id} + Type: ${log.type.toLowerCase()} + User: <@!${log.user}> (${log.user}) + Moderator: <@!${log.moderator}> (${log.moderator}) + Duration: ${ + log.duration + ? moment.duration(log.duration, 'milliseconds').humanize() + : 'N/A' + } + Reason: ${log.reason || 'None given'} + ${this.client.util.ordinal(logs.indexOf(log) + 1)} action + `); + } + const chunked: string[][] = this.client.util.chunk(niceLogs, 3); + const embedPages = chunked.map( + (e, i) => + new MessageEmbed({ + title: `Modlogs page ${i + 1}`, + description: e.join( + '\n-------------------------------------------------------\n' + ), + footer: { + text: `Page ${i + 1}/${chunked.length}` + } + }) + ); + if (page) { + await message.util.send(embedPages[page - 1]); + return; + } else { + await message.util.send(embedPages[0]); + return; + } + } else if (search) { + const entry = await Modlog.findByPk(search); + if (!entry) { + 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 + } + ] + }) + ); + } + } +} diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts deleted file mode 100644 index 29339a3..0000000 --- a/src/commands/moderation/role.ts +++ /dev/null @@ -1,181 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -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 = { - 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Suggester': [ - '*', - 'Admin Perms', - 'Sr. Moderator', - 'Moderator', - 'Helper', - 'Trial Helper', - 'Contributor' - ], - 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Giveaways': [ - '*', - 'Admin Perms', - 'Sr. Moderator', - 'Moderator', - 'Helper' - ], - 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (100m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (50m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (25m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (10m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (5m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] - }; - constructor() { - super('role', { - aliases: ['role', 'addrole', 'removerole'], - category: "Moulberry's Bush", - description: { - content: "Manages users' roles.", - usage: 'role ', - examples: ['role add tyman adminperms'] - }, - clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - typing: true, - args: [ - { - id: 'user', - type: 'member', - prompt: { - start: `What user do you want to add/remove the role on?`, - retry: `<:error:837123021016924261> Choose a valid user to add/remove the role on.` - } - }, - { - id: 'role', - type: 'role', - match: 'restContent', - prompt: { - start: `What role do you want to add/remove?`, - retry: `<:error:837123021016924261> Choose a valid role to add/remove.` - } - } - ], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to add/remove the role on', - required: true - }, - { - type: ApplicationCommandOptionType.ROLE, - name: 'role', - description: 'The role to add/remove', - required: true - } - ] - }); - } - - public async exec( - message: Message, - { user, role }: { user: GuildMember; role: Role } - ): Promise { - 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() - } - ); - } - 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() - } - ); - } - } - 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.`, - { - 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.`, - { - 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() - } - ); - } - } - // No checks if the user has MANAGE_ROLES - if (user.roles.cache.has(role.id)) { - try { - await user.roles.remove(role.id); - } catch { - return message.util.reply( - `<: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}>!`, - { 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}>.`, - { allowedMentions: AllowedMentions.none() } - ); - } - return message.util.reply( - `<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, - { allowedMentions: AllowedMentions.none() } - ); - } - } -} diff --git a/src/commands/moderation/roleCommand.ts b/src/commands/moderation/roleCommand.ts new file mode 100644 index 0000000..04437ba --- /dev/null +++ b/src/commands/moderation/roleCommand.ts @@ -0,0 +1,181 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +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 = { + 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Suggester': [ + '*', + 'Admin Perms', + 'Sr. Moderator', + 'Moderator', + 'Helper', + 'Trial Helper', + 'Contributor' + ], + 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Giveaways': [ + '*', + 'Admin Perms', + 'Sr. Moderator', + 'Moderator', + 'Helper' + ], + 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (100m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (50m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (25m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (10m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (5m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] + }; + constructor() { + super('role', { + aliases: ['role', 'addrole', 'removerole'], + category: "moderation", + description: { + content: "Manages users' roles.", + usage: 'role ', + examples: ['role add tyman adminperms'] + }, + clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'], + channel: 'guild', + typing: true, + args: [ + { + id: 'user', + type: 'member', + prompt: { + start: `What user do you want to add/remove the role on?`, + retry: `<:error:837123021016924261> Choose a valid user to add/remove the role on.` + } + }, + { + id: 'role', + type: 'role', + match: 'restContent', + prompt: { + start: `What role do you want to add/remove?`, + retry: `<:error:837123021016924261> Choose a valid role to add/remove.` + } + } + ], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to add/remove the role on', + required: true + }, + { + type: ApplicationCommandOptionType.ROLE, + name: 'role', + description: 'The role to add/remove', + required: true + } + ] + }); + } + + public async exec( + message: Message, + { user, role }: { user: GuildMember; role: Role } + ): Promise { + 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() + } + ); + } + 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() + } + ); + } + } + 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.`, + { + 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.`, + { + 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() + } + ); + } + } + // No checks if the user has MANAGE_ROLES + if (user.roles.cache.has(role.id)) { + try { + await user.roles.remove(role.id); + } catch { + return message.util.reply( + `<: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}>!`, + { 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}>.`, + { allowedMentions: AllowedMentions.none() } + ); + } + return message.util.reply( + `<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, + { allowedMentions: AllowedMentions.none() } + ); + } + } +} diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts deleted file mode 100644 index e8b1401..0000000 --- a/src/commands/moderation/warn.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { GuildMember, Message } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { Guild, Modlog, ModlogType } from '../../lib/models'; - -export default class WarnCommand extends BushCommand { - public constructor() { - super('warn', { - aliases: ['warn'], - userPermissions: ['MANAGE_MESSAGES'], - args: [ - { - id: 'member', - type: 'member' - }, - { - id: 'reason', - match: 'rest' - } - ], - description: { - content: 'Warn a member and log it in modlogs', - usage: 'warn ', - examples: ['warn @Tyman being cool'] - } - }); - } - public async exec( - message: Message, - { member, reason }: { member: GuildMember; reason: string } - ): Promise { - // Create guild entry so postgres doesn't get mad when I try and add a modlog entry - await Guild.findOrCreate({ - where: { - id: message.guild.id - }, - defaults: { - id: message.guild.id - } - }); - try { - const entry = Modlog.build({ - user: member.id, - guild: message.guild.id, - moderator: message.author.id, - type: ModlogType.WARN, - reason - }); - await entry.save(); - } catch (e) { - await message.util.send( - 'Error saving to database, please contact the developers' - ); - return; - } - try { - await member.send( - `You were warned in ${message.guild.name} for reason "${reason}".` - ); - } catch (e) { - await message.util.send('Error messaging user, warning still saved.'); - return; - } - await message.util.send( - `${member.user.tag} was warned for reason "${reason}".` - ); - } -} diff --git a/src/commands/moderation/warnCommand.ts b/src/commands/moderation/warnCommand.ts new file mode 100644 index 0000000..ea2d7ee --- /dev/null +++ b/src/commands/moderation/warnCommand.ts @@ -0,0 +1,68 @@ +import { GuildMember, Message } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Guild, Modlog, ModlogType } from '../../lib/models'; + +export default class WarnCommand extends BushCommand { + public constructor() { + super('warn', { + aliases: ['warn'], + category: "moderation", + userPermissions: ['MANAGE_MESSAGES'], + args: [ + { + id: 'member', + type: 'member' + }, + { + id: 'reason', + match: 'rest' + } + ], + description: { + content: 'Warn a member and log it in modlogs', + usage: 'warn ', + examples: ['warn @Tyman being cool'] + } + }); + } + public async exec( + message: Message, + { member, reason }: { member: GuildMember; reason: string } + ): Promise { + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + const entry = Modlog.build({ + user: member.id, + guild: message.guild.id, + moderator: message.author.id, + type: ModlogType.WARN, + reason + }); + await entry.save(); + } catch (e) { + await message.util.send( + 'Error saving to database, please contact the developers' + ); + return; + } + try { + await member.send( + `You were warned in ${message.guild.name} for reason "${reason}".` + ); + } catch (e) { + await message.util.send('Error messaging user, warning still saved.'); + return; + } + await message.util.send( + `${member.user.tag} was warned for reason "${reason}".` + ); + } +} diff --git a/src/commands/moulberry-bush/capePermissionsCommand.ts b/src/commands/moulberry-bush/capePermissionsCommand.ts new file mode 100644 index 0000000..fdb5caf --- /dev/null +++ b/src/commands/moulberry-bush/capePermissionsCommand.ts @@ -0,0 +1,126 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { MessageEmbed } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; +import { Message } from 'discord.js'; +import got from 'got'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { SlashCommandOption } from '../../lib/extensions/Util'; + +interface Capeperms { + success: boolean; + perms: User[]; +} +interface User { + _id: string; + perms: string[]; +} + +export default class CapePermissionsCommand extends BushCommand { + private nameMap = { + patreon1: 'Patreon Tier 1', + patreon2: 'Patreon Tier 2', + fade: 'Fade', + contrib: 'Contributor', + nullzee: 'Nullzee', + gravy: 'ThatGravyBoat', + space: 'Space', + mcworld: 'Minecraft World', + lava: 'Lava', + packshq: 'PacksHQ', + mbstaff: "Moulberry's Bush staff", + thebakery: "Biscuit's Bakery", + negative: 'Negative', + void: 'Void', + ironmoon: 'IRONM00N', + krusty: 'Krusty', + furf: 'FurfSky Reborn', + soldier: 'Soldier', + dsm: "Danker's Skyblock Mod", + zera: 'Zera', + tunnel: 'Tunnel', + alexxoffi: 'Alexxoffi', + parallax: 'Parallax', + jakethybro: 'Jakethybro', + planets: 'Planets' + }; + public constructor() { + super('capeperms', { + aliases: ['capeperms', 'capeperm', 'capepermissions', 'capepermission'], + category: "Moulberry's Bush", + description: { + content: 'A command to see what capes someone has access to.', + usage: 'capeperms ', + examples: ['capeperms IRONM00N'] + }, + args: [ + { + id: 'user', + type: 'string', + prompt: { + start: 'Who would you like to see the cape permissions of?', + retry: + '<:error:837123021016924261> Choose someone to see the capes their available capes.', + optional: false + } + } + ], + clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], + channel: 'guild', + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.STRING, + name: 'user', + description: + 'The username of the player to see the cape permissions of', + required: true + } + ] + }); + } + private async getResponse(user: string): Promise { + 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.`; + } + + try { + capeperms = await got + .get('http://moulberry.codes/permscapes.json') + .json(); + } catch (error) { + capeperms = null; + } + if (capeperms == null) { + return `<: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.`; + 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; + } else { + return `<:error:837123021016924261> There was an error finding cape perms for ${user}.`; + } + } + } + public async exec( + message: Message, + { user }: { user: string } + ): Promise { + await message.reply(await this.getResponse(user)); + } + + public async execSlash( + message: CommandInteraction, + { user }: { user: SlashCommandOption } + ): Promise { + await message.reply(await this.getResponse(user.value)); + } +} diff --git a/src/commands/moulberry-bush/capeperms.ts b/src/commands/moulberry-bush/capeperms.ts deleted file mode 100644 index 9832083..0000000 --- a/src/commands/moulberry-bush/capeperms.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { MessageEmbed } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; -import { Message } from 'discord.js'; -import got from 'got'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { SlashCommandOption } from '../../lib/extensions/Util'; - -interface Capeperms { - success: boolean; - perms: User[]; -} -interface User { - _id: string; - perms: string[]; -} - -export default class CapePermsCommand extends BushCommand { - private nameMap = { - patreon1: 'Patreon Tier 1', - patreon2: 'Patreon Tier 2', - fade: 'Fade', - contrib: 'Contributor', - nullzee: 'Nullzee', - gravy: 'ThatGravyBoat', - space: 'Space', - mcworld: 'Minecraft World', - lava: 'Lava', - packshq: 'PacksHQ', - mbstaff: "Moulberry's Bush staff", - thebakery: "Biscuit's Bakery", - negative: 'Negative', - void: 'Void', - ironmoon: 'IRONM00N', - krusty: 'Krusty', - furf: 'FurfSky Reborn', - soldier: 'Soldier', - dsm: "Danker's Skyblock Mod", - zera: 'Zera', - tunnel: 'Tunnel', - alexxoffi: 'Alexxoffi', - parallax: 'Parallax', - jakethybro: 'Jakethybro', - planets: 'Planets' - }; - public constructor() { - super('capeperms', { - aliases: ['capeperms', 'capeperm'], - category: "Moulberry's Bush", - description: { - content: 'A command to see what capes someone has access to.', - usage: 'capeperms ', - examples: ['capeperms IRONM00N'] - }, - args: [ - { - id: 'user', - type: 'string', - prompt: { - start: 'Who would you like to see the cape permissions of?', - retry: - '<:error:837123021016924261> Choose someone to see the capes their available capes.', - optional: false - } - } - ], - clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.STRING, - name: 'user', - description: - 'The username of the player to see the cape permissions of', - required: true - } - ] - }); - } - private async getResponse(user: string): Promise { - 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.`; - } - - try { - capeperms = await got - .get('http://moulberry.codes/permscapes.json') - .json(); - } catch (error) { - capeperms = null; - } - if (capeperms == null) { - return `<: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.`; - 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; - } else { - return `<:error:837123021016924261> There was an error finding cape perms for ${user}.`; - } - } - } - public async exec( - message: Message, - { user }: { user: string } - ): Promise { - await message.reply(await this.getResponse(user)); - } - - public async execSlash( - message: CommandInteraction, - { user }: { user: SlashCommandOption } - ): Promise { - await message.reply(await this.getResponse(user.value)); - } -} diff --git a/src/commands/moulberry-bush/giveawayPingCommand.ts b/src/commands/moulberry-bush/giveawayPingCommand.ts new file mode 100644 index 0000000..e6fdd9a --- /dev/null +++ b/src/commands/moulberry-bush/giveawayPingCommand.ts @@ -0,0 +1,68 @@ +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() { + super('giveawayping', { + aliases: ['giveawayping', 'giveawaypong'], + category: "Moulberry's Bush", + description: { + content: 'Pings the giveaway role.', + usage: 'giveawayping', + examples: ['giveawayping'] + }, + clientPermissions: ['MANAGE_MESSAGES'], + userPermissions: [ + 'SEND_MESSAGES', + 'MANAGE_GUILD', + 'MANAGE_MESSAGES', + 'BAN_MEMBERS', + 'KICK_MEMBERS', + 'VIEW_CHANNEL' + ], + channel: 'guild', + ignoreCooldown: [], + ignorePermissions: [], + cooldown: 1.44e7, //4 hours + ratelimit: 1, + editable: false + }); + } + public async exec(message: Message): Promise { + if (message.guild.id !== '516977525906341928') + return message.reply( + "<:error:837123021016924261> This command may only be run in Moulberry's Bush." + ); + if ( + !['767782084981817344', '833855738501267456'].includes(message.channel.id) + ) + return message.reply( + '<:error:837123021016924261> This command may only be run in giveaway channels.' + ); + await message.delete().catch(() => undefined); + const webhooks = await ( + message.channel as TextChannel | NewsChannel + ).fetchWebhooks(); + let webhookClient: WebhookClient; + if (webhooks.size < 1) { + const webhook = await ( + message.channel as TextChannel | NewsChannel + ).createWebhook('Giveaway ping webhook'); + webhookClient = new WebhookClient(webhook.id, webhook.token); + } else { + 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() + } + ); + } +} diff --git a/src/commands/moulberry-bush/giveawayping.ts b/src/commands/moulberry-bush/giveawayping.ts deleted file mode 100644 index e6fdd9a..0000000 --- a/src/commands/moulberry-bush/giveawayping.ts +++ /dev/null @@ -1,68 +0,0 @@ -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() { - super('giveawayping', { - aliases: ['giveawayping', 'giveawaypong'], - category: "Moulberry's Bush", - description: { - content: 'Pings the giveaway role.', - usage: 'giveawayping', - examples: ['giveawayping'] - }, - clientPermissions: ['MANAGE_MESSAGES'], - userPermissions: [ - 'SEND_MESSAGES', - 'MANAGE_GUILD', - 'MANAGE_MESSAGES', - 'BAN_MEMBERS', - 'KICK_MEMBERS', - 'VIEW_CHANNEL' - ], - channel: 'guild', - ignoreCooldown: [], - ignorePermissions: [], - cooldown: 1.44e7, //4 hours - ratelimit: 1, - editable: false - }); - } - public async exec(message: Message): Promise { - if (message.guild.id !== '516977525906341928') - return message.reply( - "<:error:837123021016924261> This command may only be run in Moulberry's Bush." - ); - if ( - !['767782084981817344', '833855738501267456'].includes(message.channel.id) - ) - return message.reply( - '<:error:837123021016924261> This command may only be run in giveaway channels.' - ); - await message.delete().catch(() => undefined); - const webhooks = await ( - message.channel as TextChannel | NewsChannel - ).fetchWebhooks(); - let webhookClient: WebhookClient; - if (webhooks.size < 1) { - const webhook = await ( - message.channel as TextChannel | NewsChannel - ).createWebhook('Giveaway ping webhook'); - webhookClient = new WebhookClient(webhook.id, webhook.token); - } else { - 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() - } - ); - } -} diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/moulberry-bush/level.ts deleted file mode 100644 index decac8a..0000000 --- a/src/commands/moulberry-bush/level.ts +++ /dev/null @@ -1,62 +0,0 @@ -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 { BushCommand } from '../../lib/extensions/BushCommand'; -import { Level } from '../../lib/models'; - -export default class LevelCommand extends BushCommand { - constructor() { - super('level', { - aliases: ['level', 'rank'], - category: "Moulberry's Bush", - description: { - content: 'Shows the level of a user', - usage: 'level [user]', - examples: ['level', 'level @Tyman'] - }, - args: [ - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to see the level of?', - retry: - 'Invalid user. What user would you like to see the level of?', - optional: true - } - } - ], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to get the level of', - required: false - } - ] - }); - } - - private async getResponse(user: User): Promise { - const userLevelRow = await Level.findByPk(user.id); - if (userLevelRow) { - return `${user ? `${user.tag}'s` : 'Your'} level is ${ - userLevelRow.level - } (${userLevelRow.xp} XP)`; - } else { - return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; - } - } - - async exec(message: Message, { user }: { user?: User }): Promise { - await message.reply(await this.getResponse(user || message.author)); - } - async execSlash( - message: CommandInteraction, - { user }: { user?: CommandInteractionOption } - ): Promise { - await message.reply(await this.getResponse(user?.user || message.user)); - } -} diff --git a/src/commands/moulberry-bush/levelCommand.ts b/src/commands/moulberry-bush/levelCommand.ts new file mode 100644 index 0000000..decac8a --- /dev/null +++ b/src/commands/moulberry-bush/levelCommand.ts @@ -0,0 +1,62 @@ +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 { BushCommand } from '../../lib/extensions/BushCommand'; +import { Level } from '../../lib/models'; + +export default class LevelCommand extends BushCommand { + constructor() { + super('level', { + aliases: ['level', 'rank'], + category: "Moulberry's Bush", + description: { + content: 'Shows the level of a user', + usage: 'level [user]', + examples: ['level', 'level @Tyman'] + }, + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to see the level of?', + retry: + 'Invalid user. What user would you like to see the level of?', + optional: true + } + } + ], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to get the level of', + required: false + } + ] + }); + } + + private async getResponse(user: User): Promise { + const userLevelRow = await Level.findByPk(user.id); + if (userLevelRow) { + return `${user ? `${user.tag}'s` : 'Your'} level is ${ + userLevelRow.level + } (${userLevelRow.xp} XP)`; + } else { + return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; + } + } + + async exec(message: Message, { user }: { user?: User }): Promise { + await message.reply(await this.getResponse(user || message.author)); + } + async execSlash( + message: CommandInteraction, + { user }: { user?: CommandInteractionOption } + ): Promise { + await message.reply(await this.getResponse(user?.user || message.user)); + } +} diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts deleted file mode 100644 index 69dcb6e..0000000 --- a/src/commands/moulberry-bush/rule.ts +++ /dev/null @@ -1,188 +0,0 @@ -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 { SlashCommandOption } from '../../lib/extensions/Util'; - -export default class RuleCommand extends BushCommand { - private rules = [ - { - title: "Follow Discord's TOS", - description: - "Be sure to follow discord's TOS found at , you must be 13 to use discord so if you admit to being under 13 you will be banned from the server." - }, - { - title: 'Be Respectful', - description: - 'Racist, sexist, homophobic, xenophobic, transphobic, ableist, hate speech, slurs, or any other derogatory, toxic, or discriminatory behavior will not be tolerated.' - }, - { - title: 'No Spamming', - description: - 'Including but not limited to: any messages that do not contribute to the conversation, repeated messages, randomly tagging users, and chat flood.' - }, - { - title: 'English', - description: - 'The primary language of the server is English, please keep all discussions in English.' - }, - { - title: 'Safe for Work', - description: - 'Please keep NSFW and NSFL content out of this server, avoid borderline images as well as keeping your status and profile picture SFW.' - }, - { - title: 'No Advertising', - description: - 'Do not promote anything without prior approval from a staff member, this includes DM advertising.' - }, - { - title: 'Impersonation', - description: - 'Do not try to impersonate others for the express intent of being deceitful, defamation , and/or personal gain.' - }, - { - title: 'Swearing', - description: 'Swearing is allowed only when not used as an insult.' - }, - { - title: 'Only ping @emergency in emergencies', - description: - 'Pinging <@&833802660209229854> for no reason will result in severe punishment. <@&833802660209229854> is only to be pinged in true emergencies.' - }, - { - title: 'No Backseat Moderating', - description: - 'If you see a rule being broken be broken, please report it using: `-report [evidence]`.' - }, - { - title: 'Staff may moderate at their discretion', - description: - 'If there are loopholes in our rules, the staff team may moderate based on what they deem appropriate. The staff team holds final discretion.' - }, - { - title: "Sending media that are able to crash a user's Discord", - description: - "Sending videos, GIFs, emojis, etc. that are able to crash someone's discord will result in a **permanent** mute that cannot be appealed." - } - ]; - - public constructor() { - super('rule', { - aliases: ['rule', 'rules'], - category: "Moulberry's Bush", - description: { - content: 'A command to state a rule.', - usage: 'rule [user]', - examples: ['rule 1 IRONM00N', 'rule 2', 'rules'] - }, - args: [ - { - id: 'rule', - type: Argument.range('number', 1, 12, true), - prompt: { - start: 'What rule would you like to have cited?', - retry: '<:no:787549684196704257> Choose a valid rule.', - optional: true - }, - default: undefined - }, - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to mention?', - retry: '<:no:787549684196704257> Choose a valid user to mention.', - optional: true - }, - default: undefined - } - ], - clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.INTEGER, - name: 'rule', - description: 'The rule to show', - required: false - }, - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to ping', - required: false - } - ] - }); - } - private getResponse( - message: Message | CommandInteraction, - rule?: number, - user?: User - ): string | 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."; - } - let rulesEmbed = new MessageEmbed().setColor('ef3929'); - if (message instanceof Message) { - rulesEmbed = rulesEmbed.setFooter( - `Triggered by ${message.author.tag}`, - message.author.avatarURL({ dynamic: true }) - ); - } - if (rule) { - const foundRule = this.rules[rule - 1]; - rulesEmbed.addField(`${rule}) ${foundRule.title}`, foundRule.description); - } else { - for (const curRule of this.rules) { - rulesEmbed.addField( - `${this.rules.indexOf(curRule) + 1}) ${curRule.title}`, - curRule.description - ); - } - } - if (!user) { - return rulesEmbed; - } else { - return [`<@!${user.id}>`, rulesEmbed]; - } - } - public async exec( - message: Message, - { rule, user }: { rule?: number; user?: User } - ): Promise { - const response = this.getResponse(message, rule, user); - if (Array.isArray(response)) { - await message.util.send(response[0], { - embed: response[1] - }); - } else { - await message.util.send(response); - } - await message.delete().catch(() => undefined); - } - - public async execSlash( - message: CommandInteraction, - { - rule, - user - }: { rule?: SlashCommandOption; user?: SlashCommandOption } - ): Promise { - const response = this.getResponse(message, rule?.value, user?.user); - if (Array.isArray(response)) { - await message.reply(response[0], { - embeds: [response[1]] - }); - } else { - await message.reply(response); - } - } -} diff --git a/src/commands/moulberry-bush/ruleCommand.ts b/src/commands/moulberry-bush/ruleCommand.ts new file mode 100644 index 0000000..69dcb6e --- /dev/null +++ b/src/commands/moulberry-bush/ruleCommand.ts @@ -0,0 +1,188 @@ +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 { SlashCommandOption } from '../../lib/extensions/Util'; + +export default class RuleCommand extends BushCommand { + private rules = [ + { + title: "Follow Discord's TOS", + description: + "Be sure to follow discord's TOS found at , you must be 13 to use discord so if you admit to being under 13 you will be banned from the server." + }, + { + title: 'Be Respectful', + description: + 'Racist, sexist, homophobic, xenophobic, transphobic, ableist, hate speech, slurs, or any other derogatory, toxic, or discriminatory behavior will not be tolerated.' + }, + { + title: 'No Spamming', + description: + 'Including but not limited to: any messages that do not contribute to the conversation, repeated messages, randomly tagging users, and chat flood.' + }, + { + title: 'English', + description: + 'The primary language of the server is English, please keep all discussions in English.' + }, + { + title: 'Safe for Work', + description: + 'Please keep NSFW and NSFL content out of this server, avoid borderline images as well as keeping your status and profile picture SFW.' + }, + { + title: 'No Advertising', + description: + 'Do not promote anything without prior approval from a staff member, this includes DM advertising.' + }, + { + title: 'Impersonation', + description: + 'Do not try to impersonate others for the express intent of being deceitful, defamation , and/or personal gain.' + }, + { + title: 'Swearing', + description: 'Swearing is allowed only when not used as an insult.' + }, + { + title: 'Only ping @emergency in emergencies', + description: + 'Pinging <@&833802660209229854> for no reason will result in severe punishment. <@&833802660209229854> is only to be pinged in true emergencies.' + }, + { + title: 'No Backseat Moderating', + description: + 'If you see a rule being broken be broken, please report it using: `-report [evidence]`.' + }, + { + title: 'Staff may moderate at their discretion', + description: + 'If there are loopholes in our rules, the staff team may moderate based on what they deem appropriate. The staff team holds final discretion.' + }, + { + title: "Sending media that are able to crash a user's Discord", + description: + "Sending videos, GIFs, emojis, etc. that are able to crash someone's discord will result in a **permanent** mute that cannot be appealed." + } + ]; + + public constructor() { + super('rule', { + aliases: ['rule', 'rules'], + category: "Moulberry's Bush", + description: { + content: 'A command to state a rule.', + usage: 'rule [user]', + examples: ['rule 1 IRONM00N', 'rule 2', 'rules'] + }, + args: [ + { + id: 'rule', + type: Argument.range('number', 1, 12, true), + prompt: { + start: 'What rule would you like to have cited?', + retry: '<:no:787549684196704257> Choose a valid rule.', + optional: true + }, + default: undefined + }, + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to mention?', + retry: '<:no:787549684196704257> Choose a valid user to mention.', + optional: true + }, + default: undefined + } + ], + clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], + channel: 'guild', + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.INTEGER, + name: 'rule', + description: 'The rule to show', + required: false + }, + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to ping', + required: false + } + ] + }); + } + private getResponse( + message: Message | CommandInteraction, + rule?: number, + user?: User + ): string | 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."; + } + let rulesEmbed = new MessageEmbed().setColor('ef3929'); + if (message instanceof Message) { + rulesEmbed = rulesEmbed.setFooter( + `Triggered by ${message.author.tag}`, + message.author.avatarURL({ dynamic: true }) + ); + } + if (rule) { + const foundRule = this.rules[rule - 1]; + rulesEmbed.addField(`${rule}) ${foundRule.title}`, foundRule.description); + } else { + for (const curRule of this.rules) { + rulesEmbed.addField( + `${this.rules.indexOf(curRule) + 1}) ${curRule.title}`, + curRule.description + ); + } + } + if (!user) { + return rulesEmbed; + } else { + return [`<@!${user.id}>`, rulesEmbed]; + } + } + public async exec( + message: Message, + { rule, user }: { rule?: number; user?: User } + ): Promise { + const response = this.getResponse(message, rule, user); + if (Array.isArray(response)) { + await message.util.send(response[0], { + embed: response[1] + }); + } else { + await message.util.send(response); + } + await message.delete().catch(() => undefined); + } + + public async execSlash( + message: CommandInteraction, + { + rule, + user + }: { rule?: SlashCommandOption; user?: SlashCommandOption } + ): Promise { + const response = this.getResponse(message, rule?.value, user?.user); + if (Array.isArray(response)) { + await message.reply(response[0], { + embeds: [response[1]] + }); + } else { + await message.reply(response); + } + } +} diff --git a/src/commands/server-config/prefix.ts b/src/commands/server-config/prefix.ts deleted file mode 100644 index ac85922..0000000 --- a/src/commands/server-config/prefix.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction, Message, Guild as DiscordGuild } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { SlashCommandOption } from '../../lib/extensions/Util'; -import { Guild } from '../../lib/models'; - -export default class PrefixCommand extends BushCommand { - constructor() { - super('prefix', { - aliases: ['prefix'], - category: "server config", - args: [ - { - id: 'prefix' - } - ], - userPermissions: ['MANAGE_GUILD'], - description: { - content: - 'Set the prefix of the current server (resets to default if prefix is not given)', - usage: 'prefix [prefix]', - examples: ['prefix', 'prefix +'] - }, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.STRING, - name: 'prefix', - description: 'The prefix to set for this server', - required: false - } - ] - }); - } - - async changePrefix(guild: DiscordGuild, prefix?: string): Promise { - if (prefix) { - const row = await Guild.findByPk(guild.id); - row.prefix = prefix; - await row.save(); - } else { - const row = await Guild.findByPk(guild.id); - row.prefix = this.client.config.prefix; - await row.save(); - } - } - - async exec(message: Message, { prefix }: { prefix?: string }): Promise { - await this.changePrefix(message.guild, prefix); - if (prefix) { - await message.util.send(`Sucessfully set prefix to \`${prefix}\``); - } else { - await message.util.send( - `Sucessfully reset prefix to \`${this.client.config.prefix}\`` - ); - } - } - - async execSlash( - message: CommandInteraction, - { prefix }: { prefix?: SlashCommandOption } - ): Promise { - await this.changePrefix(message.guild, prefix?.value); - if (prefix) { - await message.reply(`Sucessfully set prefix to \`${prefix.value}\``); - } else { - await message.reply( - `Sucessfully reset prefix to \`${this.client.config.prefix}\`` - ); - } - } -} diff --git a/src/commands/server-config/prefixCommand.ts b/src/commands/server-config/prefixCommand.ts new file mode 100644 index 0000000..ac85922 --- /dev/null +++ b/src/commands/server-config/prefixCommand.ts @@ -0,0 +1,71 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction, Message, Guild as DiscordGuild } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { Guild } from '../../lib/models'; + +export default class PrefixCommand extends BushCommand { + constructor() { + super('prefix', { + aliases: ['prefix'], + category: "server config", + args: [ + { + id: 'prefix' + } + ], + userPermissions: ['MANAGE_GUILD'], + description: { + content: + 'Set the prefix of the current server (resets to default if prefix is not given)', + usage: 'prefix [prefix]', + examples: ['prefix', 'prefix +'] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.STRING, + name: 'prefix', + description: 'The prefix to set for this server', + required: false + } + ] + }); + } + + async changePrefix(guild: DiscordGuild, prefix?: string): Promise { + if (prefix) { + const row = await Guild.findByPk(guild.id); + row.prefix = prefix; + await row.save(); + } else { + const row = await Guild.findByPk(guild.id); + row.prefix = this.client.config.prefix; + await row.save(); + } + } + + async exec(message: Message, { prefix }: { prefix?: string }): Promise { + await this.changePrefix(message.guild, prefix); + if (prefix) { + await message.util.send(`Sucessfully set prefix to \`${prefix}\``); + } else { + await message.util.send( + `Sucessfully reset prefix to \`${this.client.config.prefix}\`` + ); + } + } + + async execSlash( + message: CommandInteraction, + { prefix }: { prefix?: SlashCommandOption } + ): Promise { + await this.changePrefix(message.guild, prefix?.value); + if (prefix) { + await message.reply(`Sucessfully set prefix to \`${prefix.value}\``); + } else { + await message.reply( + `Sucessfully reset prefix to \`${this.client.config.prefix}\`` + ); + } + } +} diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts deleted file mode 100644 index b13dd36..0000000 --- a/src/listeners/client/ready.ts +++ /dev/null @@ -1,18 +0,0 @@ -import chalk from 'chalk'; -import { BushListener } from '../../lib/extensions/BushListener'; - -export default class CommandBlockedListener extends BushListener { - public constructor() { - super('ready', { - emitter: 'client', - event: 'ready' - }); - } - - public async exec(): Promise { - await this.client.logger.log( - chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, - true - ); - } -} diff --git a/src/listeners/client/readyListener.ts b/src/listeners/client/readyListener.ts new file mode 100644 index 0000000..8d4ac6a --- /dev/null +++ b/src/listeners/client/readyListener.ts @@ -0,0 +1,18 @@ +import chalk from 'chalk'; +import { BushListener } from '../../lib/extensions/BushListener'; + +export default class ReadyListener extends BushListener { + public constructor() { + super('ready', { + emitter: 'client', + event: 'ready' + }); + } + + public async exec(): Promise { + await this.client.logger.log( + chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, + true + ); + } +} diff --git a/src/listeners/client/syncSlashCommandsListener.ts b/src/listeners/client/syncSlashCommandsListener.ts new file mode 100644 index 0000000..7835136 --- /dev/null +++ b/src/listeners/client/syncSlashCommandsListener.ts @@ -0,0 +1,22 @@ +import { BushListener } from '../../lib/extensions/BushListener'; + +export default class SyncSlashCommandsListener extends BushListener { + constructor() { + super('syncslashcommands', { + emitter: 'client', + event: 'ready' + }); + } + async exec(): Promise { + if (this.client.config.dev && this.client.config.devGuild) { + // Use guild slash commands for instant registration in dev + await this.client.util.syncSlashCommands( + false, + this.client.config.devGuild + ); + } else { + // Use global in production + await this.client.util.syncSlashCommands(); + } + } +} diff --git a/src/listeners/client/syncslashcommands.ts b/src/listeners/client/syncslashcommands.ts deleted file mode 100644 index d734507..0000000 --- a/src/listeners/client/syncslashcommands.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { BushListener } from '../../lib/extensions/BushListener'; - -export default class CreateSlashCommands extends BushListener { - constructor() { - super('createslashcommands', { - emitter: 'client', - event: 'ready' - }); - } - async exec(): Promise { - if (this.client.config.dev && this.client.config.devGuild) { - // Use guild slash commands for instant registration in dev - await this.client.util.syncSlashCommands( - false, - this.client.config.devGuild - ); - } else { - // Use global in production - await this.client.util.syncSlashCommands(); - } - } -} diff --git a/src/listeners/commands/commandBlockedListener.ts b/src/listeners/commands/commandBlockedListener.ts new file mode 100644 index 0000000..dee1319 --- /dev/null +++ b/src/listeners/commands/commandBlockedListener.ts @@ -0,0 +1,34 @@ +import { BushListener } from '../../lib/extensions/BushListener'; +import { Command } from 'discord-akairo'; +import { Message } from 'discord.js'; + +export default class CommandBlockedListener extends BushListener { + public constructor() { + super('commandBlocked', { + emitter: 'commandHandler', + event: 'commandBlocked' + }); + } + + public async exec( + message: Message, + command: Command, + reason: string + ): Promise { + switch (reason) { + case 'owner': { + await message.util.send( + `You must be an owner to run command \`${message.util.parsed.command}\`` + ); + break; + } + case 'blacklist': { + // pass + break; + } + default: { + await message.util.send(`Command blocked with reason \`${reason}\``); + } + } + } +} diff --git a/src/listeners/commands/commandErrorListener.ts b/src/listeners/commands/commandErrorListener.ts new file mode 100644 index 0000000..518545a --- /dev/null +++ b/src/listeners/commands/commandErrorListener.ts @@ -0,0 +1,48 @@ +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() { + super('error', { + emitter: 'commandHandler', + event: 'error' + }); + } + async exec( + error: Error, + message: Message, + command?: BushCommand + ): Promise { + const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers + const errorDevEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle(`Error # \`${errorNumber}\`: An error occurred`) + .setDescription( + stripIndents`**User:** ${message.author} (${message.author.tag}) + **Command:** ${command} + **Channel:** ${message.channel} (${message.channel.id}) + **Message:** [link](${message.url})` + ) + .addField('Error', `${await this.client.util.haste(error.stack)}`); + let errorUserEmbed: MessageEmbed; + if (command) { + errorUserEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle('An error occurred') + .setDescription( + stripIndents`Whoops! It appears like something broke. + The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. + ` + ); + } + const channel = (await this.client.channels.fetch( + this.client.config.channels.log + )) as TextChannel; + await channel.send(errorDevEmbed); + if (errorUserEmbed) await message.reply(errorUserEmbed); + } +} diff --git a/src/listeners/commands/commandStartedListener.ts b/src/listeners/commands/commandStartedListener.ts new file mode 100644 index 0000000..d81ed59 --- /dev/null +++ b/src/listeners/commands/commandStartedListener.ts @@ -0,0 +1,24 @@ +import chalk from 'chalk'; +import { Message, DMChannel } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushListener } from '../../lib/extensions/BushListener'; + +export default class CommandStartedListener extends BushListener { + constructor() { + super('logCommands', { + emitter: 'commandHandler', + event: 'commandStarted' + }); + } + exec(message: Message, command: BushCommand): void { + this.client.logger.verbose( + chalk`{cyan {green ${message.author.tag}} is running {green ${ + command.aliases[0] + }} in {green ${ + message.channel instanceof DMChannel + ? 'DMs' + : `#${message.channel.name} (Server: ${message.guild.name})` + }}.}` + ); + } +} diff --git a/src/listeners/commands/commandblocked.ts b/src/listeners/commands/commandblocked.ts deleted file mode 100644 index dee1319..0000000 --- a/src/listeners/commands/commandblocked.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { BushListener } from '../../lib/extensions/BushListener'; -import { Command } from 'discord-akairo'; -import { Message } from 'discord.js'; - -export default class CommandBlockedListener extends BushListener { - public constructor() { - super('commandBlocked', { - emitter: 'commandHandler', - event: 'commandBlocked' - }); - } - - public async exec( - message: Message, - command: Command, - reason: string - ): Promise { - switch (reason) { - case 'owner': { - await message.util.send( - `You must be an owner to run command \`${message.util.parsed.command}\`` - ); - break; - } - case 'blacklist': { - // pass - break; - } - default: { - await message.util.send(`Command blocked with reason \`${reason}\``); - } - } - } -} diff --git a/src/listeners/commands/commandstarted.ts b/src/listeners/commands/commandstarted.ts deleted file mode 100644 index d81ed59..0000000 --- a/src/listeners/commands/commandstarted.ts +++ /dev/null @@ -1,24 +0,0 @@ -import chalk from 'chalk'; -import { Message, DMChannel } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { BushListener } from '../../lib/extensions/BushListener'; - -export default class CommandStartedListener extends BushListener { - constructor() { - super('logCommands', { - emitter: 'commandHandler', - event: 'commandStarted' - }); - } - exec(message: Message, command: BushCommand): void { - this.client.logger.verbose( - chalk`{cyan {green ${message.author.tag}} is running {green ${ - command.aliases[0] - }} in {green ${ - message.channel instanceof DMChannel - ? 'DMs' - : `#${message.channel.name} (Server: ${message.guild.name})` - }}.}` - ); - } -} diff --git a/src/listeners/commands/error.ts b/src/listeners/commands/error.ts deleted file mode 100644 index 518545a..0000000 --- a/src/listeners/commands/error.ts +++ /dev/null @@ -1,48 +0,0 @@ -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() { - super('error', { - emitter: 'commandHandler', - event: 'error' - }); - } - async exec( - error: Error, - message: Message, - command?: BushCommand - ): Promise { - const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers - const errorDevEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle(`Error # \`${errorNumber}\`: An error occurred`) - .setDescription( - stripIndents`**User:** ${message.author} (${message.author.tag}) - **Command:** ${command} - **Channel:** ${message.channel} (${message.channel.id}) - **Message:** [link](${message.url})` - ) - .addField('Error', `${await this.client.util.haste(error.stack)}`); - let errorUserEmbed: MessageEmbed; - if (command) { - errorUserEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle('An error occurred') - .setDescription( - stripIndents`Whoops! It appears like something broke. - The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. - ` - ); - } - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; - await channel.send(errorDevEmbed); - if (errorUserEmbed) await message.reply(errorUserEmbed); - } -} diff --git a/src/listeners/commands/slashCommandErrorListener.ts b/src/listeners/commands/slashCommandErrorListener.ts new file mode 100644 index 0000000..a2b7bb7 --- /dev/null +++ b/src/listeners/commands/slashCommandErrorListener.ts @@ -0,0 +1,48 @@ +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() { + super('slashError', { + emitter: 'commandHandler', + event: 'slashError' + }); + } + async exec( + error: Error, + message: CommandInteraction, + command: BushCommand + ): Promise { + const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers + const errorDevEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle(`Slash Error # \`${errorNumber}\`: An error occurred`) + .setDescription( + stripIndents`**User:** <@${message.user.id}> (${message.user.tag}) + **Slash Command:** ${command} + **Channel:** <#${message.channelID}> (${message.channelID}) + **Message:** [link](https://discord.com/${message.guildID}/${message.channelID}/${message.id})` + ) + .addField('Error', `${await this.client.util.haste(error.stack)}`); + let errorUserEmbed: MessageEmbed; + if (command) { + errorUserEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle('An error occurred') + .setDescription( + stripIndents`Whoops! It appears like something broke. + The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. + ` + ); + } + const channel = (await this.client.channels.fetch( + this.client.config.channels.log + )) as TextChannel; + await channel.send(errorDevEmbed); + if (errorUserEmbed) await message.reply(errorUserEmbed); + } +} diff --git a/src/listeners/commands/slashError.ts b/src/listeners/commands/slashError.ts deleted file mode 100644 index 3f2260d..0000000 --- a/src/listeners/commands/slashError.ts +++ /dev/null @@ -1,48 +0,0 @@ -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 CommandErrorListener extends BushListener { - constructor() { - super('slashError', { - emitter: 'commandHandler', - event: 'slashError' - }); - } - async exec( - error: Error, - message: CommandInteraction, - command: BushCommand - ): Promise { - const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers - const errorDevEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle(`Slash Error # \`${errorNumber}\`: An error occurred`) - .setDescription( - stripIndents`**User:** <@${message.user.id}> (${message.user.tag}) - **Slash Command:** ${command} - **Channel:** <#${message.channelID}> (${message.channelID}) - **Message:** [link](https://discord.com/${message.guildID}/${message.channelID}/${message.id})` - ) - .addField('Error', `${await this.client.util.haste(error.stack)}`); - let errorUserEmbed: MessageEmbed; - if (command) { - errorUserEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle('An error occurred') - .setDescription( - stripIndents`Whoops! It appears like something broke. - The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. - ` - ); - } - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; - await channel.send(errorDevEmbed); - if (errorUserEmbed) await message.reply(errorUserEmbed); - } -} diff --git a/src/listeners/guild/syncUnbanListener.ts b/src/listeners/guild/syncUnbanListener.ts new file mode 100644 index 0000000..81a08f8 --- /dev/null +++ b/src/listeners/guild/syncUnbanListener.ts @@ -0,0 +1,24 @@ +import { User, Guild } from 'discord.js'; +import { BushListener } from '../../lib/extensions/BushListener'; +import { Ban } from '../../lib/models'; + +export default class SyncUnbanListener extends BushListener { + public constructor() { + super('guildBanRemove', { + emitter: 'client', + event: 'guildBanRemove' + }); + } + + public async exec(guild: Guild, user: User): Promise { + const bans = await Ban.findAll({ + where: { + user: user.id, + guild: guild.id + } + }); + for (const dbBan of bans) { + await dbBan.destroy(); + } + } +} diff --git a/src/listeners/guild/syncunban.ts b/src/listeners/guild/syncunban.ts deleted file mode 100644 index b8a1419..0000000 --- a/src/listeners/guild/syncunban.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { User, Guild } from 'discord.js'; -import { BushListener } from '../../lib/extensions/BushListener'; -import { Ban } from '../../lib/models'; - -export default class CommandBlockedListener extends BushListener { - public constructor() { - super('guildBanRemove', { - emitter: 'client', - event: 'guildBanRemove' - }); - } - - public async exec(guild: Guild, user: User): Promise { - const bans = await Ban.findAll({ - where: { - user: user.id, - guild: guild.id - } - }); - for (const dbBan of bans) { - await dbBan.destroy(); - } - } -} diff --git a/src/listeners/message/levelListener.ts b/src/listeners/message/levelListener.ts new file mode 100644 index 0000000..3b23f36 --- /dev/null +++ b/src/listeners/message/levelListener.ts @@ -0,0 +1,40 @@ +import chalk from 'chalk'; +import { Message } from 'discord.js'; +import { BushListener } from '../../lib/extensions/BushListener'; +import { Level } from '../../lib/models'; + +export default class LevelListener extends BushListener { + private levelCooldowns: Set = new Set(); + private blacklistedChannels = ['702456294874808330']; + constructor() { + super('level', { + emitter: 'commandHandler', + event: 'messageInvalid' // Using messageInvalid here so commands don't give xp + }); + } + async exec(message: Message): Promise { + if (message.author.bot) return; + if (message.util?.parsed?.command) return; + if (this.levelCooldowns.has(message.author.id)) return; + if (!this.client.config.dev && message.guild.id != '516977525906341928') + return; + if (this.blacklistedChannels.includes(message.channel.id)) return; + if (!['DEFAULT', 'REPLY'].includes(message.type)) return; //checks for join messages, slash commands, booster messages etc + const [user] = await Level.findOrBuild({ + where: { + id: message.author.id + }, + defaults: { + id: message.author.id + } + }); + const xpToGive = Level.genRandomizedXp(); + user.xp += xpToGive; + await user.save(); + await this.client.logger.verbose( + chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}` + ); + this.levelCooldowns.add(message.author.id); + setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000); + } +} diff --git a/src/listeners/message/levels.ts b/src/listeners/message/levels.ts deleted file mode 100644 index 3503584..0000000 --- a/src/listeners/message/levels.ts +++ /dev/null @@ -1,39 +0,0 @@ -import chalk from 'chalk'; -import { Message } from 'discord.js'; -import { BushListener } from '../../lib/extensions/BushListener'; -import { Level } from '../../lib/models'; - -export default class LevelListener extends BushListener { - private levelCooldowns: Set = new Set(); - private blacklistedChannels = ['702456294874808330']; - constructor() { - super('level', { - emitter: 'commandHandler', - event: 'messageInvalid' // Using messageInvalid here so commands don't give xp - }); - } - async exec(message: Message): Promise { - if (message.author.bot) return; - if (message.util?.parsed?.command) return; - if (this.levelCooldowns.has(message.author.id)) return; - if (!this.client.config.dev && message.guild.id != '516977525906341928') - return; - if (this.blacklistedChannels.includes(message.channel.id)) return; - const [user] = await Level.findOrBuild({ - where: { - id: message.author.id - }, - defaults: { - id: message.author.id - } - }); - const xpToGive = Level.genRandomizedXp(); - user.xp += xpToGive; - await user.save(); - await this.client.logger.verbose( - chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}` - ); - this.levelCooldowns.add(message.author.id); - setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000); - } -} diff --git a/src/tasks/Unban.ts b/src/tasks/Unban.ts deleted file mode 100644 index 6b9d82e..0000000 --- a/src/tasks/Unban.ts +++ /dev/null @@ -1,49 +0,0 @@ -import chalk from 'chalk'; -import { DiscordAPIError } from 'discord.js'; -import { Op } from 'sequelize'; -import { BushTask } from '../lib/extensions/BushTask'; -import { Ban } from '../lib/models'; - -export default class UnbanTask extends BushTask { - constructor() { - super('unban', { - delay: 30_000, // 1/2 min - runOnStart: true - }); - } - async exec(): Promise { - const rows = await Ban.findAll({ - where: { - [Op.and]: [ - { - expires: { - [Op.lt]: new Date() // Find all rows with an expiry date before now - } - } - ] - } - }); - this.client.logger.verbose( - chalk.cyan(`Queried bans, found ${rows.length} expired bans.`) - ); - for (const row of rows) { - const guild = this.client.guilds.cache.get(row.guild); - if (!guild) { - await row.destroy(); - continue; - } - try { - await guild.members.unban( - row.user, - `Unbanning user because tempban expired` - ); - } catch (e) { - if (e instanceof DiscordAPIError) { - // Member not banned, ignore - } else throw e; - } - await row.destroy(); - this.client.logger.verbose(chalk.cyan('Unbanned user')); - } - } -} diff --git a/src/tasks/UnbanTask.ts b/src/tasks/UnbanTask.ts new file mode 100644 index 0000000..6b9d82e --- /dev/null +++ b/src/tasks/UnbanTask.ts @@ -0,0 +1,49 @@ +import chalk from 'chalk'; +import { DiscordAPIError } from 'discord.js'; +import { Op } from 'sequelize'; +import { BushTask } from '../lib/extensions/BushTask'; +import { Ban } from '../lib/models'; + +export default class UnbanTask extends BushTask { + constructor() { + super('unban', { + delay: 30_000, // 1/2 min + runOnStart: true + }); + } + async exec(): Promise { + const rows = await Ban.findAll({ + where: { + [Op.and]: [ + { + expires: { + [Op.lt]: new Date() // Find all rows with an expiry date before now + } + } + ] + } + }); + this.client.logger.verbose( + chalk.cyan(`Queried bans, found ${rows.length} expired bans.`) + ); + for (const row of rows) { + const guild = this.client.guilds.cache.get(row.guild); + if (!guild) { + await row.destroy(); + continue; + } + try { + await guild.members.unban( + row.user, + `Unbanning user because tempban expired` + ); + } catch (e) { + if (e instanceof DiscordAPIError) { + // Member not banned, ignore + } else throw e; + } + await row.destroy(); + this.client.logger.verbose(chalk.cyan('Unbanned user')); + } + } +} -- cgit From 1546da359646b89f13d17784eb7653a52ca61efd Mon Sep 17 00:00:00 2001 From: TymanWasTaken Date: Thu, 27 May 2021 14:58:21 -0600 Subject: Fix file naming --- src/commands/dev/eval.ts | 138 +++++++++++++ src/commands/dev/evalCommand.ts | 138 ------------- src/commands/dev/reload.ts | 44 ++++ src/commands/dev/reloadCommand.ts | 44 ---- src/commands/dev/setLevel.ts | 91 +++++++++ src/commands/dev/setLevelCommand.ts | 91 --------- src/commands/info/botInfo.ts | 66 ++++++ src/commands/info/botInfoCommand.ts | 66 ------ src/commands/info/help.ts | 108 ++++++++++ src/commands/info/helpCommand.ts | 108 ---------- src/commands/info/ping.ts | 69 +++++++ src/commands/info/pingCommand.ts | 69 ------- src/commands/info/pronouns.ts | 121 +++++++++++ src/commands/info/pronounsCommand.ts | 121 ----------- src/commands/moderation/ban.ts | 224 +++++++++++++++++++++ src/commands/moderation/banCommand.ts | 224 --------------------- src/commands/moderation/kick.ts | 121 +++++++++++ src/commands/moderation/kickCommand.ts | 121 ----------- src/commands/moderation/modlog.ts | 149 ++++++++++++++ src/commands/moderation/modlogCommand.ts | 149 -------------- src/commands/moderation/role.ts | 181 +++++++++++++++++ src/commands/moderation/roleCommand.ts | 181 ----------------- src/commands/moderation/warn.ts | 68 +++++++ src/commands/moderation/warnCommand.ts | 68 ------- .../moulberry-bush/capePermissionsCommand.ts | 126 ------------ src/commands/moulberry-bush/capePerms.ts | 126 ++++++++++++ src/commands/moulberry-bush/giveawayPing.ts | 68 +++++++ src/commands/moulberry-bush/giveawayPingCommand.ts | 68 ------- src/commands/moulberry-bush/level.ts | 62 ++++++ src/commands/moulberry-bush/levelCommand.ts | 62 ------ src/commands/moulberry-bush/rule.ts | 188 +++++++++++++++++ src/commands/moulberry-bush/ruleCommand.ts | 188 ----------------- src/commands/server-config/prefix.ts | 71 +++++++ src/commands/server-config/prefixCommand.ts | 71 ------- src/inhibitors/blacklist/BlacklistInhibitor.ts | 14 -- src/inhibitors/blacklist/blacklist.ts | 14 ++ src/listeners/client/ready.ts | 18 ++ src/listeners/client/readyListener.ts | 18 -- src/listeners/client/syncSlashCommands.ts | 22 ++ src/listeners/client/syncSlashCommandsListener.ts | 22 -- src/listeners/commands/commandBlocked.ts | 34 ++++ src/listeners/commands/commandBlockedListener.ts | 34 ---- src/listeners/commands/commandError.ts | 48 +++++ src/listeners/commands/commandErrorListener.ts | 48 ----- src/listeners/commands/commandStarted.ts | 24 +++ src/listeners/commands/commandStartedListener.ts | 24 --- src/listeners/commands/slashCommandError.ts | 48 +++++ .../commands/slashCommandErrorListener.ts | 48 ----- src/listeners/guild/syncUnban.ts | 24 +++ src/listeners/guild/syncUnbanListener.ts | 24 --- src/listeners/message/level.ts | 40 ++++ src/listeners/message/levelListener.ts | 40 ---- src/tasks/UnbanTask.ts | 49 ----- src/tasks/unban.ts | 49 +++++ 54 files changed, 2216 insertions(+), 2216 deletions(-) create mode 100644 src/commands/dev/eval.ts delete mode 100644 src/commands/dev/evalCommand.ts create mode 100644 src/commands/dev/reload.ts delete mode 100644 src/commands/dev/reloadCommand.ts create mode 100644 src/commands/dev/setLevel.ts delete mode 100644 src/commands/dev/setLevelCommand.ts create mode 100644 src/commands/info/botInfo.ts delete mode 100644 src/commands/info/botInfoCommand.ts create mode 100644 src/commands/info/help.ts delete mode 100644 src/commands/info/helpCommand.ts create mode 100644 src/commands/info/ping.ts delete mode 100644 src/commands/info/pingCommand.ts create mode 100644 src/commands/info/pronouns.ts delete mode 100644 src/commands/info/pronounsCommand.ts create mode 100644 src/commands/moderation/ban.ts delete mode 100644 src/commands/moderation/banCommand.ts create mode 100644 src/commands/moderation/kick.ts delete mode 100644 src/commands/moderation/kickCommand.ts create mode 100644 src/commands/moderation/modlog.ts delete mode 100644 src/commands/moderation/modlogCommand.ts create mode 100644 src/commands/moderation/role.ts delete mode 100644 src/commands/moderation/roleCommand.ts create mode 100644 src/commands/moderation/warn.ts delete mode 100644 src/commands/moderation/warnCommand.ts delete mode 100644 src/commands/moulberry-bush/capePermissionsCommand.ts create mode 100644 src/commands/moulberry-bush/capePerms.ts create mode 100644 src/commands/moulberry-bush/giveawayPing.ts delete mode 100644 src/commands/moulberry-bush/giveawayPingCommand.ts create mode 100644 src/commands/moulberry-bush/level.ts delete mode 100644 src/commands/moulberry-bush/levelCommand.ts create mode 100644 src/commands/moulberry-bush/rule.ts delete mode 100644 src/commands/moulberry-bush/ruleCommand.ts create mode 100644 src/commands/server-config/prefix.ts delete mode 100644 src/commands/server-config/prefixCommand.ts delete mode 100644 src/inhibitors/blacklist/BlacklistInhibitor.ts create mode 100644 src/inhibitors/blacklist/blacklist.ts create mode 100644 src/listeners/client/ready.ts delete mode 100644 src/listeners/client/readyListener.ts create mode 100644 src/listeners/client/syncSlashCommands.ts delete mode 100644 src/listeners/client/syncSlashCommandsListener.ts create mode 100644 src/listeners/commands/commandBlocked.ts delete mode 100644 src/listeners/commands/commandBlockedListener.ts create mode 100644 src/listeners/commands/commandError.ts delete mode 100644 src/listeners/commands/commandErrorListener.ts create mode 100644 src/listeners/commands/commandStarted.ts delete mode 100644 src/listeners/commands/commandStartedListener.ts create mode 100644 src/listeners/commands/slashCommandError.ts delete mode 100644 src/listeners/commands/slashCommandErrorListener.ts create mode 100644 src/listeners/guild/syncUnban.ts delete mode 100644 src/listeners/guild/syncUnbanListener.ts create mode 100644 src/listeners/message/level.ts delete mode 100644 src/listeners/message/levelListener.ts delete mode 100644 src/tasks/UnbanTask.ts create mode 100644 src/tasks/unban.ts (limited to 'src/commands/moulberry-bush/rule.ts') diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts new file mode 100644 index 0000000..83e63f6 --- /dev/null +++ b/src/commands/dev/eval.ts @@ -0,0 +1,138 @@ +/* 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'; + +const clean = (text) => { + if (typeof text === 'string') + return text + .replace(/`/g, '`' + String.fromCharCode(8203)) + .replace(/@/g, '@' + String.fromCharCode(8203)); + else return text; +}; + +export default class EvalCommand extends BushCommand { + public constructor() { + super('eval', { + aliases: ['eval', 'ev'], + category: 'dev', + description: { + content: 'Use the command to eval stuff in the bot.', + usage: 'eval [--silent] [--depth #]', + examples: ['eval message.guild.name', 'eval this.client.ownerID'] + }, + args: [ + { + id: 'depth', + match: 'option', + type: 'number', + flag: '--depth', + default: 0 + }, + { + id: 'silent', + match: 'flag', + flag: '--silent' + }, + { + id: 'code', + match: 'rest', + type: 'string', + prompt: { + start: 'What would you like to eval?', + retry: 'Invalid code to eval. What would you like to eval?' + } + } + ], + ownerOnly: true, + clientPermissions: ['EMBED_LINKS'] + }); + } + + public async exec( + message: Message, + { depth, code, silent }: { depth: number; code: string; silent: boolean } + ): Promise { + const embed: MessageEmbed = new MessageEmbed(); + + try { + let output; + const me = message.member, + member = message.member, + bot = this.client, + guild = message.guild, + channel = message.channel, + config = this.client.config, + 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]' + ); + output = clean(output); + 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 }) + ) + .setTimestamp(); + } catch (e) { + 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 }) + ) + .setTimestamp(); + } + if (!silent) { + await message.util.send(embed); + } else { + try { + await message.author.send(embed); + await message.react(''); + } catch (e) { + await message.react('❌'); + } + } + } +} diff --git a/src/commands/dev/evalCommand.ts b/src/commands/dev/evalCommand.ts deleted file mode 100644 index 83e63f6..0000000 --- a/src/commands/dev/evalCommand.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* 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'; - -const clean = (text) => { - if (typeof text === 'string') - return text - .replace(/`/g, '`' + String.fromCharCode(8203)) - .replace(/@/g, '@' + String.fromCharCode(8203)); - else return text; -}; - -export default class EvalCommand extends BushCommand { - public constructor() { - super('eval', { - aliases: ['eval', 'ev'], - category: 'dev', - description: { - content: 'Use the command to eval stuff in the bot.', - usage: 'eval [--silent] [--depth #]', - examples: ['eval message.guild.name', 'eval this.client.ownerID'] - }, - args: [ - { - id: 'depth', - match: 'option', - type: 'number', - flag: '--depth', - default: 0 - }, - { - id: 'silent', - match: 'flag', - flag: '--silent' - }, - { - id: 'code', - match: 'rest', - type: 'string', - prompt: { - start: 'What would you like to eval?', - retry: 'Invalid code to eval. What would you like to eval?' - } - } - ], - ownerOnly: true, - clientPermissions: ['EMBED_LINKS'] - }); - } - - public async exec( - message: Message, - { depth, code, silent }: { depth: number; code: string; silent: boolean } - ): Promise { - const embed: MessageEmbed = new MessageEmbed(); - - try { - let output; - const me = message.member, - member = message.member, - bot = this.client, - guild = message.guild, - channel = message.channel, - config = this.client.config, - 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]' - ); - output = clean(output); - 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 }) - ) - .setTimestamp(); - } catch (e) { - 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 }) - ) - .setTimestamp(); - } - if (!silent) { - await message.util.send(embed); - } else { - try { - await message.author.send(embed); - await message.react(''); - } catch (e) { - await message.react('❌'); - } - } - } -} diff --git a/src/commands/dev/reload.ts b/src/commands/dev/reload.ts new file mode 100644 index 0000000..64e5745 --- /dev/null +++ b/src/commands/dev/reload.ts @@ -0,0 +1,44 @@ +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { stripIndent } from 'common-tags'; +import { Message } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; + +export default class ReloadCommand extends BushCommand { + constructor() { + super('reload', { + aliases: ['reload'], + category: 'dev', + description: { + content: 'Reloads the bot', + usage: 'reload', + examples: ['reload'] + }, + ownerOnly: true, + typing: true + }); + } + + private async getResponse(): Promise { + try { + await this.client.util.shell('yarn rimraf dist/'); + await this.client.util.shell('yarn tsc'); + this.client.commandHandler.reloadAll(); + this.client.listenerHandler.reloadAll(); + this.client.inhibitorHandler.reloadAll(); + return '🔁 Successfully reloaded!'; + } catch (e) { + return stripIndent` + An error occured while reloading: + ${await this.client.util.haste(e.stack)} + `; + } + } + + public async exec(message: Message): Promise { + await message.util.send(await this.getResponse()); + } + + public async execSlash(message: CommandInteraction): Promise { + await message.reply(await this.getResponse()); + } +} diff --git a/src/commands/dev/reloadCommand.ts b/src/commands/dev/reloadCommand.ts deleted file mode 100644 index 64e5745..0000000 --- a/src/commands/dev/reloadCommand.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { stripIndent } from 'common-tags'; -import { Message } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; - -export default class ReloadCommand extends BushCommand { - constructor() { - super('reload', { - aliases: ['reload'], - category: 'dev', - description: { - content: 'Reloads the bot', - usage: 'reload', - examples: ['reload'] - }, - ownerOnly: true, - typing: true - }); - } - - private async getResponse(): Promise { - try { - await this.client.util.shell('yarn rimraf dist/'); - await this.client.util.shell('yarn tsc'); - this.client.commandHandler.reloadAll(); - this.client.listenerHandler.reloadAll(); - this.client.inhibitorHandler.reloadAll(); - return '🔁 Successfully reloaded!'; - } catch (e) { - return stripIndent` - An error occured while reloading: - ${await this.client.util.haste(e.stack)} - `; - } - } - - public async exec(message: Message): Promise { - await message.util.send(await this.getResponse()); - } - - public async execSlash(message: CommandInteraction): Promise { - await message.reply(await this.getResponse()); - } -} diff --git a/src/commands/dev/setLevel.ts b/src/commands/dev/setLevel.ts new file mode 100644 index 0000000..d1701c5 --- /dev/null +++ b/src/commands/dev/setLevel.ts @@ -0,0 +1,91 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction } from 'discord.js'; +import { User } from 'discord.js'; +import { Message } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { Level } from '../../lib/models'; +import AllowedMentions from '../../lib/utils/AllowedMentions'; + +export default class SetLevelCommand extends BushCommand { + constructor() { + super('setlevel', { + aliases: ['setlevel'], + category: 'dev', + description: { + content: 'Sets the level of a user', + usage: 'setlevel ', + examples: ['setlevel @Moulberry 69'] //nice + }, + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to change the level of?', + retry: + 'Invalid user. What user would you like to change the level of?' + } + }, + { + id: 'level', + type: 'number', + prompt: { + start: 'What level would you like to set?', + retry: 'Invalid user. What level would you like to set?' + } + } + ], + ownerOnly: true, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to change the level of', + required: true + }, + { + type: ApplicationCommandOptionType.INTEGER, + name: 'level', + description: 'The level to set the user to', + required: true + } + ] + }); + } + + private async setLevel(user: User, level: number): Promise { + const [levelEntry] = await Level.findOrBuild({ + where: { + id: user.id + }, + defaults: { + id: user.id + } + }); + levelEntry.xp = Level.convertLevelToXp(level); + await levelEntry.save(); + return `Successfully set level of <@${user.id}> to \`${level}\` (\`${levelEntry.xp}\` XP)`; + } + + async exec( + message: Message, + { user, level }: { user: User; level: number } + ): Promise { + await message.util.send(await this.setLevel(user, level), { + allowedMentions: AllowedMentions.none() + }); + } + + async execSlash( + message: CommandInteraction, + { + user, + level + }: { user: SlashCommandOption; level: SlashCommandOption } + ): Promise { + await message.reply(await this.setLevel(user.user, level.value), { + allowedMentions: AllowedMentions.none() + }); + } +} diff --git a/src/commands/dev/setLevelCommand.ts b/src/commands/dev/setLevelCommand.ts deleted file mode 100644 index d1701c5..0000000 --- a/src/commands/dev/setLevelCommand.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction } from 'discord.js'; -import { User } from 'discord.js'; -import { Message } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { SlashCommandOption } from '../../lib/extensions/Util'; -import { Level } from '../../lib/models'; -import AllowedMentions from '../../lib/utils/AllowedMentions'; - -export default class SetLevelCommand extends BushCommand { - constructor() { - super('setlevel', { - aliases: ['setlevel'], - category: 'dev', - description: { - content: 'Sets the level of a user', - usage: 'setlevel ', - examples: ['setlevel @Moulberry 69'] //nice - }, - args: [ - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to change the level of?', - retry: - 'Invalid user. What user would you like to change the level of?' - } - }, - { - id: 'level', - type: 'number', - prompt: { - start: 'What level would you like to set?', - retry: 'Invalid user. What level would you like to set?' - } - } - ], - ownerOnly: true, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to change the level of', - required: true - }, - { - type: ApplicationCommandOptionType.INTEGER, - name: 'level', - description: 'The level to set the user to', - required: true - } - ] - }); - } - - private async setLevel(user: User, level: number): Promise { - const [levelEntry] = await Level.findOrBuild({ - where: { - id: user.id - }, - defaults: { - id: user.id - } - }); - levelEntry.xp = Level.convertLevelToXp(level); - await levelEntry.save(); - return `Successfully set level of <@${user.id}> to \`${level}\` (\`${levelEntry.xp}\` XP)`; - } - - async exec( - message: Message, - { user, level }: { user: User; level: number } - ): Promise { - await message.util.send(await this.setLevel(user, level), { - allowedMentions: AllowedMentions.none() - }); - } - - async execSlash( - message: CommandInteraction, - { - user, - level - }: { user: SlashCommandOption; level: SlashCommandOption } - ): Promise { - await message.reply(await this.setLevel(user.user, level.value), { - allowedMentions: AllowedMentions.none() - }); - } -} diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts new file mode 100644 index 0000000..779b318 --- /dev/null +++ b/src/commands/info/botInfo.ts @@ -0,0 +1,66 @@ +import { MessageEmbed, Message, CommandInteraction } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { duration } from 'moment'; + +export default class BotInfoCommand extends BushCommand { + constructor() { + super('botinfo', { + aliases: ['botinfo'], + category: 'info', + description: { + content: 'Shows information about the bot', + usage: 'botinfo', + examples: ['botinfo'] + } + }); + } + + private async generateEmbed(): Promise { + const owners = (await this.client.util.mapIDs(this.client.ownerID)) + .map((u) => u.tag) + .join('\n'); + const currentCommit = ( + await this.client.util.shell('git rev-parse HEAD') + ).stdout.replace('\n', ''); + const repoUrl = ( + await this.client.util.shell('git remote get-url origin') + ).stdout.replace('\n', ''); + const embed = new MessageEmbed() + .setTitle('Bot Info:') + .addFields([ + { + name: 'Owners', + value: owners, + inline: true + }, + { + name: 'Uptime', + value: this.client.util.capitalize( + duration(this.client.uptime, 'milliseconds').humanize() + ) + }, + { + name: 'User count', + value: this.client.users.cache.size, + inline: true + }, + { + name: 'Current commit', + value: `[${currentCommit.substring( + 0, + 7 + )}](${repoUrl}/commit/${currentCommit})` + } + ]) + .setTimestamp(); + return embed; + } + + public async exec(message: Message): Promise { + await message.util.send(await this.generateEmbed()); + } + + public async execSlash(message: CommandInteraction): Promise { + await message.reply(await this.generateEmbed()); + } +} diff --git a/src/commands/info/botInfoCommand.ts b/src/commands/info/botInfoCommand.ts deleted file mode 100644 index 779b318..0000000 --- a/src/commands/info/botInfoCommand.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { MessageEmbed, Message, CommandInteraction } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { duration } from 'moment'; - -export default class BotInfoCommand extends BushCommand { - constructor() { - super('botinfo', { - aliases: ['botinfo'], - category: 'info', - description: { - content: 'Shows information about the bot', - usage: 'botinfo', - examples: ['botinfo'] - } - }); - } - - private async generateEmbed(): Promise { - const owners = (await this.client.util.mapIDs(this.client.ownerID)) - .map((u) => u.tag) - .join('\n'); - const currentCommit = ( - await this.client.util.shell('git rev-parse HEAD') - ).stdout.replace('\n', ''); - const repoUrl = ( - await this.client.util.shell('git remote get-url origin') - ).stdout.replace('\n', ''); - const embed = new MessageEmbed() - .setTitle('Bot Info:') - .addFields([ - { - name: 'Owners', - value: owners, - inline: true - }, - { - name: 'Uptime', - value: this.client.util.capitalize( - duration(this.client.uptime, 'milliseconds').humanize() - ) - }, - { - name: 'User count', - value: this.client.users.cache.size, - inline: true - }, - { - name: 'Current commit', - value: `[${currentCommit.substring( - 0, - 7 - )}](${repoUrl}/commit/${currentCommit})` - } - ]) - .setTimestamp(); - return embed; - } - - public async exec(message: Message): Promise { - await message.util.send(await this.generateEmbed()); - } - - public async execSlash(message: CommandInteraction): Promise { - await message.reply(await this.generateEmbed()); - } -} diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts new file mode 100644 index 0000000..3b5e538 --- /dev/null +++ b/src/commands/info/help.ts @@ -0,0 +1,108 @@ +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 { SlashCommandOption } from '../../lib/extensions/Util'; + +export default class HelpCommand extends BushCommand { + constructor() { + super('help', { + aliases: ['help'], + category: 'info', + description: { + content: 'Shows the commands of the bot', + usage: 'help', + examples: ['help'] + }, + clientPermissions: ['EMBED_LINKS'], + args: [ + { + id: 'command', + type: 'commandAlias' + } + ], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.STRING, + name: 'command', + description: 'The command to get help for', + required: false + } + ] + }); + } + + private generateEmbed(command?: BushCommand): MessageEmbed { + const prefix = this.handler.prefix; + if (!command) { + const embed = new MessageEmbed() + .addField( + 'Commands', + stripIndent`A list of available commands. + For additional info on a command, type \`${prefix}help \` + ` + ) + .setFooter( + `For more information about a command use "${this.client.config.prefix}help "` + ) + .setTimestamp(); + for (const category of this.handler.categories.values()) { + embed.addField( + `${category.id.replace(/(\b\w)/gi, (lc): string => + lc.toUpperCase() + )}`, + `${category + .filter((cmd): boolean => cmd.aliases.length > 0) + .map((cmd): string => `\`${cmd.aliases[0]}\``) + .join(' ')}` + ); + } + return embed; + } else { + const embed = new MessageEmbed() + .setColor([155, 200, 200]) + .setTitle( + `\`${command.description.usage ? command.description.usage : ''}\`` + ) + .addField( + 'Description', + `${command.description.content ? command.description.content : ''} ${ + command.ownerOnly ? '\n__Owner Only__' : '' + }` + ); + + if (command.aliases.length > 1) + embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true); + if (command.description.examples && command.description.examples.length) + embed.addField( + 'Examples', + `\`${command.description.examples.join('`\n`')}\``, + true + ); + return embed; + } + } + + public async exec( + message: Message, + { command }: { command: BushCommand } + ): Promise { + await message.util.send(this.generateEmbed(command)); + } + + public async execSlash( + message: CommandInteraction, + { command }: { command: SlashCommandOption } + ): Promise { + if (command) { + await message.reply( + this.generateEmbed( + this.handler.findCommand(command.value) as BushCommand + ) + ); + } else { + await message.reply(this.generateEmbed()); + } + } +} diff --git a/src/commands/info/helpCommand.ts b/src/commands/info/helpCommand.ts deleted file mode 100644 index 3b5e538..0000000 --- a/src/commands/info/helpCommand.ts +++ /dev/null @@ -1,108 +0,0 @@ -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 { SlashCommandOption } from '../../lib/extensions/Util'; - -export default class HelpCommand extends BushCommand { - constructor() { - super('help', { - aliases: ['help'], - category: 'info', - description: { - content: 'Shows the commands of the bot', - usage: 'help', - examples: ['help'] - }, - clientPermissions: ['EMBED_LINKS'], - args: [ - { - id: 'command', - type: 'commandAlias' - } - ], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.STRING, - name: 'command', - description: 'The command to get help for', - required: false - } - ] - }); - } - - private generateEmbed(command?: BushCommand): MessageEmbed { - const prefix = this.handler.prefix; - if (!command) { - const embed = new MessageEmbed() - .addField( - 'Commands', - stripIndent`A list of available commands. - For additional info on a command, type \`${prefix}help \` - ` - ) - .setFooter( - `For more information about a command use "${this.client.config.prefix}help "` - ) - .setTimestamp(); - for (const category of this.handler.categories.values()) { - embed.addField( - `${category.id.replace(/(\b\w)/gi, (lc): string => - lc.toUpperCase() - )}`, - `${category - .filter((cmd): boolean => cmd.aliases.length > 0) - .map((cmd): string => `\`${cmd.aliases[0]}\``) - .join(' ')}` - ); - } - return embed; - } else { - const embed = new MessageEmbed() - .setColor([155, 200, 200]) - .setTitle( - `\`${command.description.usage ? command.description.usage : ''}\`` - ) - .addField( - 'Description', - `${command.description.content ? command.description.content : ''} ${ - command.ownerOnly ? '\n__Owner Only__' : '' - }` - ); - - if (command.aliases.length > 1) - embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true); - if (command.description.examples && command.description.examples.length) - embed.addField( - 'Examples', - `\`${command.description.examples.join('`\n`')}\``, - true - ); - return embed; - } - } - - public async exec( - message: Message, - { command }: { command: BushCommand } - ): Promise { - await message.util.send(this.generateEmbed(command)); - } - - public async execSlash( - message: CommandInteraction, - { command }: { command: SlashCommandOption } - ): Promise { - if (command) { - await message.reply( - this.generateEmbed( - this.handler.findCommand(command.value) as BushCommand - ) - ); - } else { - await message.reply(this.generateEmbed()); - } - } -} diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts new file mode 100644 index 0000000..e0bbfc7 --- /dev/null +++ b/src/commands/info/ping.ts @@ -0,0 +1,69 @@ +import { CommandInteraction } from 'discord.js'; +import { Message } from 'discord.js'; +import { MessageEmbed } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; + +export default class PingCommand extends BushCommand { + constructor() { + super('ping', { + aliases: ['ping'], + category: 'info', + description: { + content: 'Gets the latency of the bot', + usage: 'ping', + examples: ['ping'] + } + }); + } + + public async exec(message: Message): Promise { + const sentMessage = await message.util.send('Pong!'); + const timestamp: number = message.editedTimestamp + ? message.editedTimestamp + : message.createdTimestamp; + const botLatency = `\`\`\`\n ${Math.floor( + sentMessage.createdTimestamp - timestamp + )}ms \`\`\``; + const apiLatency = `\`\`\`\n ${Math.round( + message.client.ws.ping + )}ms \`\`\``; + const embed = new MessageEmbed() + .setTitle('Pong! 🏓') + .addField('Bot Latency', botLatency, true) + .addField('API Latency', apiLatency, true) + .setFooter( + message.author.username, + message.author.displayAvatarURL({ dynamic: true }) + ) + .setTimestamp(); + await sentMessage.edit({ + content: null, + embed + }); + } + + public async execSlash(message: CommandInteraction): Promise { + const timestamp1 = message.createdTimestamp; + await message.reply('Pong!'); + const timestamp2 = await message + .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 }) + ) + .setTimestamp(); + await message.editReply({ + content: null, + embeds: [embed] + }); + } +} diff --git a/src/commands/info/pingCommand.ts b/src/commands/info/pingCommand.ts deleted file mode 100644 index e0bbfc7..0000000 --- a/src/commands/info/pingCommand.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { CommandInteraction } from 'discord.js'; -import { Message } from 'discord.js'; -import { MessageEmbed } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; - -export default class PingCommand extends BushCommand { - constructor() { - super('ping', { - aliases: ['ping'], - category: 'info', - description: { - content: 'Gets the latency of the bot', - usage: 'ping', - examples: ['ping'] - } - }); - } - - public async exec(message: Message): Promise { - const sentMessage = await message.util.send('Pong!'); - const timestamp: number = message.editedTimestamp - ? message.editedTimestamp - : message.createdTimestamp; - const botLatency = `\`\`\`\n ${Math.floor( - sentMessage.createdTimestamp - timestamp - )}ms \`\`\``; - const apiLatency = `\`\`\`\n ${Math.round( - message.client.ws.ping - )}ms \`\`\``; - const embed = new MessageEmbed() - .setTitle('Pong! 🏓') - .addField('Bot Latency', botLatency, true) - .addField('API Latency', apiLatency, true) - .setFooter( - message.author.username, - message.author.displayAvatarURL({ dynamic: true }) - ) - .setTimestamp(); - await sentMessage.edit({ - content: null, - embed - }); - } - - public async execSlash(message: CommandInteraction): Promise { - const timestamp1 = message.createdTimestamp; - await message.reply('Pong!'); - const timestamp2 = await message - .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 }) - ) - .setTimestamp(); - await message.editReply({ - content: null, - embeds: [embed] - }); - } -} diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts new file mode 100644 index 0000000..2c1d5f2 --- /dev/null +++ b/src/commands/info/pronouns.ts @@ -0,0 +1,121 @@ +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 { SlashCommandOption } from '../../lib/extensions/Util'; + +export const pronounMapping = { + unspecified: 'Unspecified', + hh: 'He/Him', + hi: 'He/It', + hs: 'He/She', + ht: 'He/They', + ih: 'It/Him', + ii: 'It/Its', + is: 'It/She', + it: 'It/They', + shh: 'She/He', + sh: 'She/Her', + si: 'She/It', + st: 'She/They', + th: 'They/He', + ti: 'They/It', + ts: 'They/She', + tt: 'They/Them', + any: 'Any pronouns', + other: 'Other pronouns', + ask: 'Ask me my pronouns', + avoid: 'Avoid pronouns, use my name' +}; +export type pronounsType = keyof typeof pronounMapping; + +export default class PronounsCommand extends BushCommand { + constructor() { + super('pronouns', { + aliases: ['pronouns', 'pronoun'], + category: 'info', + description: { + usage: 'pronouns ', + examples: ['pronouns IRONM00N'], + content: 'Finds the pronouns of a user using https://pronoundb.org.' + }, + args: [ + { + id: 'user', + type: 'user', + default: null + } + ], + clientPermissions: ['SEND_MESSAGES'], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to get pronouns for', + required: false + } + ], + slashEmphemeral: true // I'll add dynamic checking to this later + }); + } + async sendResponse( + message: Message | CommandInteraction, + user: User, + author: boolean + ): Promise { + try { + const apiRes: { pronouns: pronounsType } = await got + .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/' + } + }) + ); + } else { + message.reply({ + embeds: [ + new MessageEmbed({ + title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`, + description: pronounMapping[apiRes.pronouns], + footer: { + text: 'Data provided by https://pronoundb.org/' + } + }) + ] + }); + } + } catch (e) { + if (e instanceof HTTPError && e.response.statusCode === 404) { + if (author) { + await message.reply( + 'You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.' + ); + } else { + await message.reply( + `${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.` + ); + } + } else throw e; + } + } + async exec(message: Message, { user }: { user?: User }): Promise { + const u = user || message.author; + await this.sendResponse(message, u, u.id === message.author.id); + } + async execSlash( + message: CommandInteraction, + { user }: { user?: SlashCommandOption } + ): Promise { + const u = user?.user || message.user; + await this.sendResponse(message, u, u.id === message.user.id); + } +} diff --git a/src/commands/info/pronounsCommand.ts b/src/commands/info/pronounsCommand.ts deleted file mode 100644 index 2c1d5f2..0000000 --- a/src/commands/info/pronounsCommand.ts +++ /dev/null @@ -1,121 +0,0 @@ -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 { SlashCommandOption } from '../../lib/extensions/Util'; - -export const pronounMapping = { - unspecified: 'Unspecified', - hh: 'He/Him', - hi: 'He/It', - hs: 'He/She', - ht: 'He/They', - ih: 'It/Him', - ii: 'It/Its', - is: 'It/She', - it: 'It/They', - shh: 'She/He', - sh: 'She/Her', - si: 'She/It', - st: 'She/They', - th: 'They/He', - ti: 'They/It', - ts: 'They/She', - tt: 'They/Them', - any: 'Any pronouns', - other: 'Other pronouns', - ask: 'Ask me my pronouns', - avoid: 'Avoid pronouns, use my name' -}; -export type pronounsType = keyof typeof pronounMapping; - -export default class PronounsCommand extends BushCommand { - constructor() { - super('pronouns', { - aliases: ['pronouns', 'pronoun'], - category: 'info', - description: { - usage: 'pronouns ', - examples: ['pronouns IRONM00N'], - content: 'Finds the pronouns of a user using https://pronoundb.org.' - }, - args: [ - { - id: 'user', - type: 'user', - default: null - } - ], - clientPermissions: ['SEND_MESSAGES'], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to get pronouns for', - required: false - } - ], - slashEmphemeral: true // I'll add dynamic checking to this later - }); - } - async sendResponse( - message: Message | CommandInteraction, - user: User, - author: boolean - ): Promise { - try { - const apiRes: { pronouns: pronounsType } = await got - .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/' - } - }) - ); - } else { - message.reply({ - embeds: [ - new MessageEmbed({ - title: `${author ? 'Your' : `${user.tag}'s`} pronouns:`, - description: pronounMapping[apiRes.pronouns], - footer: { - text: 'Data provided by https://pronoundb.org/' - } - }) - ] - }); - } - } catch (e) { - if (e instanceof HTTPError && e.response.statusCode === 404) { - if (author) { - await message.reply( - 'You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.' - ); - } else { - await message.reply( - `${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.` - ); - } - } else throw e; - } - } - async exec(message: Message, { user }: { user?: User }): Promise { - const u = user || message.author; - await this.sendResponse(message, u, u.id === message.author.id); - } - async execSlash( - message: CommandInteraction, - { user }: { user?: SlashCommandOption } - ): Promise { - const u = user?.user || message.user; - await this.sendResponse(message, u, u.id === message.user.id); - } -} diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts new file mode 100644 index 0000000..feb020b --- /dev/null +++ b/src/commands/moderation/ban.ts @@ -0,0 +1,224 @@ +import { User } from 'discord.js'; +import { Guild } from '../../lib/models'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Ban, Modlog, ModlogType } from '../../lib/models'; +import moment from 'moment'; +import { Message } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { ApplicationCommandOptionType } from 'discord-api-types'; + +const durationAliases: Record = { + weeks: ['w', 'weeks', 'week', 'wk', 'wks'], + days: ['d', 'days', 'day'], + hours: ['h', 'hours', 'hour', 'hr', 'hrs'], + minutes: ['m', 'min', 'mins', 'minutes', 'minute'], + months: ['mo', 'month', 'months'] +}; +const durationRegex = + /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; + +export default class BanCommand extends BushCommand { + constructor() { + super('ban', { + aliases: ['ban'], + category: 'moderation', + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to ban?', + retry: 'Invalid response. What user would you like to ban?' + } + }, + { + id: 'reason', + match: 'rest' + }, + { + id: 'time', + match: 'option', + flag: '--time' + } + ], + clientPermissions: ['BAN_MEMBERS'], + userPermissions: ['BAN_MEMBERS'], + description: { + content: + 'Ban a member and log it in modlogs (with optional time to unban)', + usage: 'ban [--time]', + examples: [ + 'ban @Tyman being cool', + 'ban @Tyman being cool --time 7days' + ] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to ban', + required: true + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'reason', + description: 'The reason to show in modlogs and audit log', + required: false + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'time', + description: + 'The time the user should be banned for (default permanent)', + required: false + } + ] + }); + } + async *genResponses( + message: Message | CommandInteraction, + user: User, + reason?: string, + time?: string + ): AsyncIterable { + const duration = moment.duration(); + let modlogEnry: Modlog; + let banEntry: Ban; + const translatedTime: string[] = []; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + try { + if (time) { + const parsed = [...time.matchAll(durationRegex)]; + if (parsed.length < 1) { + yield 'Invalid time.'; + return; + } + for (const part of parsed) { + const translated = Object.keys(durationAliases).find((k) => + durationAliases[k].includes(part[2]) + ); + translatedTime.push(part[1] + ' ' + translated); + duration.add( + Number(part[1]), + translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes' + ); + } + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + reason, + type: ModlogType.TEMPBAN, + duration: duration.asMilliseconds(), + moderator: + message instanceof CommandInteraction + ? message.user.id + : message.author.id + }); + banEntry = Ban.build({ + user: user.id, + guild: message.guild.id, + reason, + expires: new Date(new Date().getTime() + duration.asMilliseconds()), + modlog: modlogEnry.id + }); + } else { + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + reason, + type: ModlogType.BAN, + moderator: + message instanceof CommandInteraction + ? message.user.id + : message.author.id + }); + banEntry = Ban.build({ + user: user.id, + guild: message.guild.id, + reason, + modlog: modlogEnry.id + }); + } + await modlogEnry.save(); + await banEntry.save(); + } catch (e) { + console.error(e); + yield 'Error saving to database. Please report this to a developer.'; + return; + } + try { + await user.send( + `You were banned in ${message.guild.name} ${ + translatedTime.length >= 1 + ? `for ${translatedTime.join(', ')}` + : 'permanently' + } with reason \`${reason || 'No reason given'}\`` + ); + } catch (e) { + yield 'Error sending message to user'; + } + await message.guild.members.ban(user, { + reason: `Banned by ${ + message instanceof CommandInteraction + ? message.user.tag + : message.author.tag + } with ${reason ? `reason ${reason}` : 'no reason'}` + }); + yield `Banned <@!${user.id}> ${ + translatedTime.length >= 1 + ? `for ${translatedTime.join(', ')}` + : 'permanently' + } with reason \`${reason || 'No reason given'}\``; + } catch { + yield 'Error banning :/'; + await banEntry.destroy(); + await modlogEnry.destroy(); + return; + } + } + async exec( + message: Message, + { user, reason, time }: { user: User; reason?: string; time?: string } + ): Promise { + for await (const response of this.genResponses( + message, + user, + reason, + time + )) { + await message.util.send(response); + } + } + + async execSlash( + message: CommandInteraction, + { + user, + reason, + time + }: { + user: SlashCommandOption; + reason: SlashCommandOption; + time: SlashCommandOption; + } + ): Promise { + for await (const response of this.genResponses( + message, + user.user, + reason?.value, + time?.value + )) { + await message.reply(response); + } + } +} diff --git a/src/commands/moderation/banCommand.ts b/src/commands/moderation/banCommand.ts deleted file mode 100644 index feb020b..0000000 --- a/src/commands/moderation/banCommand.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { User } from 'discord.js'; -import { Guild } from '../../lib/models'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { Ban, Modlog, ModlogType } from '../../lib/models'; -import moment from 'moment'; -import { Message } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; -import { SlashCommandOption } from '../../lib/extensions/Util'; -import { ApplicationCommandOptionType } from 'discord-api-types'; - -const durationAliases: Record = { - weeks: ['w', 'weeks', 'week', 'wk', 'wks'], - days: ['d', 'days', 'day'], - hours: ['h', 'hours', 'hour', 'hr', 'hrs'], - minutes: ['m', 'min', 'mins', 'minutes', 'minute'], - months: ['mo', 'month', 'months'] -}; -const durationRegex = - /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; - -export default class BanCommand extends BushCommand { - constructor() { - super('ban', { - aliases: ['ban'], - category: 'moderation', - args: [ - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to ban?', - retry: 'Invalid response. What user would you like to ban?' - } - }, - { - id: 'reason', - match: 'rest' - }, - { - id: 'time', - match: 'option', - flag: '--time' - } - ], - clientPermissions: ['BAN_MEMBERS'], - userPermissions: ['BAN_MEMBERS'], - description: { - content: - 'Ban a member and log it in modlogs (with optional time to unban)', - usage: 'ban [--time]', - examples: [ - 'ban @Tyman being cool', - 'ban @Tyman being cool --time 7days' - ] - }, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to ban', - required: true - }, - { - type: ApplicationCommandOptionType.STRING, - name: 'reason', - description: 'The reason to show in modlogs and audit log', - required: false - }, - { - type: ApplicationCommandOptionType.STRING, - name: 'time', - description: - 'The time the user should be banned for (default permanent)', - required: false - } - ] - }); - } - async *genResponses( - message: Message | CommandInteraction, - user: User, - reason?: string, - time?: string - ): AsyncIterable { - const duration = moment.duration(); - let modlogEnry: Modlog; - let banEntry: Ban; - const translatedTime: string[] = []; - // Create guild entry so postgres doesn't get mad when I try and add a modlog entry - await Guild.findOrCreate({ - where: { - id: message.guild.id - }, - defaults: { - id: message.guild.id - } - }); - try { - try { - if (time) { - const parsed = [...time.matchAll(durationRegex)]; - if (parsed.length < 1) { - yield 'Invalid time.'; - return; - } - for (const part of parsed) { - const translated = Object.keys(durationAliases).find((k) => - durationAliases[k].includes(part[2]) - ); - translatedTime.push(part[1] + ' ' + translated); - duration.add( - Number(part[1]), - translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes' - ); - } - modlogEnry = Modlog.build({ - user: user.id, - guild: message.guild.id, - reason, - type: ModlogType.TEMPBAN, - duration: duration.asMilliseconds(), - moderator: - message instanceof CommandInteraction - ? message.user.id - : message.author.id - }); - banEntry = Ban.build({ - user: user.id, - guild: message.guild.id, - reason, - expires: new Date(new Date().getTime() + duration.asMilliseconds()), - modlog: modlogEnry.id - }); - } else { - modlogEnry = Modlog.build({ - user: user.id, - guild: message.guild.id, - reason, - type: ModlogType.BAN, - moderator: - message instanceof CommandInteraction - ? message.user.id - : message.author.id - }); - banEntry = Ban.build({ - user: user.id, - guild: message.guild.id, - reason, - modlog: modlogEnry.id - }); - } - await modlogEnry.save(); - await banEntry.save(); - } catch (e) { - console.error(e); - yield 'Error saving to database. Please report this to a developer.'; - return; - } - try { - await user.send( - `You were banned in ${message.guild.name} ${ - translatedTime.length >= 1 - ? `for ${translatedTime.join(', ')}` - : 'permanently' - } with reason \`${reason || 'No reason given'}\`` - ); - } catch (e) { - yield 'Error sending message to user'; - } - await message.guild.members.ban(user, { - reason: `Banned by ${ - message instanceof CommandInteraction - ? message.user.tag - : message.author.tag - } with ${reason ? `reason ${reason}` : 'no reason'}` - }); - yield `Banned <@!${user.id}> ${ - translatedTime.length >= 1 - ? `for ${translatedTime.join(', ')}` - : 'permanently' - } with reason \`${reason || 'No reason given'}\``; - } catch { - yield 'Error banning :/'; - await banEntry.destroy(); - await modlogEnry.destroy(); - return; - } - } - async exec( - message: Message, - { user, reason, time }: { user: User; reason?: string; time?: string } - ): Promise { - for await (const response of this.genResponses( - message, - user, - reason, - time - )) { - await message.util.send(response); - } - } - - async execSlash( - message: CommandInteraction, - { - user, - reason, - time - }: { - user: SlashCommandOption; - reason: SlashCommandOption; - time: SlashCommandOption; - } - ): Promise { - for await (const response of this.genResponses( - message, - user.user, - reason?.value, - time?.value - )) { - await message.reply(response); - } - } -} diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts new file mode 100644 index 0000000..58cf52e --- /dev/null +++ b/src/commands/moderation/kick.ts @@ -0,0 +1,121 @@ +import { BushCommand } from '../../lib/extensions/BushCommand'; +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() { + super('kick', { + aliases: ['kick'], + category: 'moderation', + args: [ + { + id: 'user', + type: 'member', + prompt: { + start: 'What user would you like to kick?', + retry: 'Invalid response. What user would you like to kick?' + } + }, + { + id: 'reason' + } + ], + clientPermissions: ['KICK_MEMBERS'], + userPermissions: ['KICK_MEMBERS'], + description: { + content: 'Kick a member and log it in modlogs', + usage: 'kick ', + examples: ['kick @Tyman being cool'] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to kick', + required: true + }, + { + type: ApplicationCommandOptionType.STRING, + name: 'reason', + description: 'The reason to show in modlogs and audit log', + required: false + } + ] + }); + } + + private async *genResponses( + message: Message | CommandInteraction, + user: GuildMember, + reason?: string + ): AsyncIterable { + let modlogEnry: Modlog; + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + modlogEnry = Modlog.build({ + user: user.id, + guild: message.guild.id, + moderator: + message instanceof Message ? message.author.id : message.user.id, + type: ModlogType.KICK, + reason + }); + await modlogEnry.save(); + } catch (e) { + console.error(e); + yield 'Error saving to database. Please report this to a developer.'; + return; + } + try { + await user.send( + `You were kicked in ${message.guild.name} with reason \`${ + reason || 'No reason given' + }\`` + ); + } catch (e) { + yield 'Error sending message to user'; + } + try { + await user.kick( + `Kicked by ${ + message instanceof Message ? message.author.tag : message.user.tag + } with ${reason ? `reason ${reason}` : 'no reason'}` + ); + } catch { + yield 'Error kicking :/'; + await modlogEnry.destroy(); + return; + } + yield `Kicked <@!${user.id}> with reason \`${ + reason || 'No reason given' + }\``; + } + + async exec( + message: Message, + { user, reason }: { user: GuildMember; reason?: string } + ): Promise { + for await (const response of this.genResponses(message, user, reason)) { + await message.util.send(response); + } + } + + async execSlash( + message: CommandInteraction, + { user, reason }: { user: GuildMember; reason?: string } + ): Promise { + for await (const response of this.genResponses(message, user, reason)) { + await message.reply(response); + } + } +} diff --git a/src/commands/moderation/kickCommand.ts b/src/commands/moderation/kickCommand.ts deleted file mode 100644 index 58cf52e..0000000 --- a/src/commands/moderation/kickCommand.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { BushCommand } from '../../lib/extensions/BushCommand'; -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() { - super('kick', { - aliases: ['kick'], - category: 'moderation', - args: [ - { - id: 'user', - type: 'member', - prompt: { - start: 'What user would you like to kick?', - retry: 'Invalid response. What user would you like to kick?' - } - }, - { - id: 'reason' - } - ], - clientPermissions: ['KICK_MEMBERS'], - userPermissions: ['KICK_MEMBERS'], - description: { - content: 'Kick a member and log it in modlogs', - usage: 'kick ', - examples: ['kick @Tyman being cool'] - }, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to kick', - required: true - }, - { - type: ApplicationCommandOptionType.STRING, - name: 'reason', - description: 'The reason to show in modlogs and audit log', - required: false - } - ] - }); - } - - private async *genResponses( - message: Message | CommandInteraction, - user: GuildMember, - reason?: string - ): AsyncIterable { - let modlogEnry: Modlog; - // Create guild entry so postgres doesn't get mad when I try and add a modlog entry - await Guild.findOrCreate({ - where: { - id: message.guild.id - }, - defaults: { - id: message.guild.id - } - }); - try { - modlogEnry = Modlog.build({ - user: user.id, - guild: message.guild.id, - moderator: - message instanceof Message ? message.author.id : message.user.id, - type: ModlogType.KICK, - reason - }); - await modlogEnry.save(); - } catch (e) { - console.error(e); - yield 'Error saving to database. Please report this to a developer.'; - return; - } - try { - await user.send( - `You were kicked in ${message.guild.name} with reason \`${ - reason || 'No reason given' - }\`` - ); - } catch (e) { - yield 'Error sending message to user'; - } - try { - await user.kick( - `Kicked by ${ - message instanceof Message ? message.author.tag : message.user.tag - } with ${reason ? `reason ${reason}` : 'no reason'}` - ); - } catch { - yield 'Error kicking :/'; - await modlogEnry.destroy(); - return; - } - yield `Kicked <@!${user.id}> with reason \`${ - reason || 'No reason given' - }\``; - } - - async exec( - message: Message, - { user, reason }: { user: GuildMember; reason?: string } - ): Promise { - for await (const response of this.genResponses(message, user, reason)) { - await message.util.send(response); - } - } - - async execSlash( - message: CommandInteraction, - { user, reason }: { user: GuildMember; reason?: string } - ): Promise { - for await (const response of this.genResponses(message, user, reason)) { - await message.reply(response); - } - } -} diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts new file mode 100644 index 0000000..2df4745 --- /dev/null +++ b/src/commands/moderation/modlog.ts @@ -0,0 +1,149 @@ +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'; + +export default class ModlogCommand extends BushCommand { + constructor() { + super('modlog', { + aliases: ['modlog', 'modlogs'], + category: 'moderation', + args: [ + { + id: 'search', + prompt: { + start: 'What modlog id or user would you like to see?' + } + }, + { + id: 'page', + type: 'number' + } + ], + userPermissions: ['MANAGE_MESSAGES'], + description: { + content: "View a user's modlogs, or view a specific modlog entry", + usage: 'warn [page]', + examples: ['modlogs @Tyman', 'modlogs @Tyman 3'] + } + }); + } + *args(): unknown { + const search = yield { + id: 'search', + type: Argument.union('user', 'string'), + prompt: { + start: 'What modlog id or user would you like to see?' + } + }; + if (typeof search === 'string') return { search, page: null }; + else { + const page = yield { + id: 'page', + type: 'number', + prompt: { + start: 'What page?', + retry: 'Not a number. What page?', + optional: true + } + }; + return { search, page }; + } + } + async exec( + message: Message, + { search, page }: { search: string; page: number } + ): Promise { + const foundUser = await this.client.util.resolveUserAsync(search); + if (foundUser) { + const logs = await Modlog.findAll({ + where: { + guild: message.guild.id, + user: foundUser.id + }, + order: [['createdAt', 'ASC']] + }); + const niceLogs: string[] = []; + for (const log of logs) { + niceLogs.push(stripIndent` + ID: ${log.id} + Type: ${log.type.toLowerCase()} + User: <@!${log.user}> (${log.user}) + Moderator: <@!${log.moderator}> (${log.moderator}) + Duration: ${ + log.duration + ? moment.duration(log.duration, 'milliseconds').humanize() + : 'N/A' + } + Reason: ${log.reason || 'None given'} + ${this.client.util.ordinal(logs.indexOf(log) + 1)} action + `); + } + const chunked: string[][] = this.client.util.chunk(niceLogs, 3); + const embedPages = chunked.map( + (e, i) => + new MessageEmbed({ + title: `Modlogs page ${i + 1}`, + description: e.join( + '\n-------------------------------------------------------\n' + ), + footer: { + text: `Page ${i + 1}/${chunked.length}` + } + }) + ); + if (page) { + await message.util.send(embedPages[page - 1]); + return; + } else { + await message.util.send(embedPages[0]); + return; + } + } else if (search) { + const entry = await Modlog.findByPk(search); + if (!entry) { + 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 + } + ] + }) + ); + } + } +} diff --git a/src/commands/moderation/modlogCommand.ts b/src/commands/moderation/modlogCommand.ts deleted file mode 100644 index 2df4745..0000000 --- a/src/commands/moderation/modlogCommand.ts +++ /dev/null @@ -1,149 +0,0 @@ -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'; - -export default class ModlogCommand extends BushCommand { - constructor() { - super('modlog', { - aliases: ['modlog', 'modlogs'], - category: 'moderation', - args: [ - { - id: 'search', - prompt: { - start: 'What modlog id or user would you like to see?' - } - }, - { - id: 'page', - type: 'number' - } - ], - userPermissions: ['MANAGE_MESSAGES'], - description: { - content: "View a user's modlogs, or view a specific modlog entry", - usage: 'warn [page]', - examples: ['modlogs @Tyman', 'modlogs @Tyman 3'] - } - }); - } - *args(): unknown { - const search = yield { - id: 'search', - type: Argument.union('user', 'string'), - prompt: { - start: 'What modlog id or user would you like to see?' - } - }; - if (typeof search === 'string') return { search, page: null }; - else { - const page = yield { - id: 'page', - type: 'number', - prompt: { - start: 'What page?', - retry: 'Not a number. What page?', - optional: true - } - }; - return { search, page }; - } - } - async exec( - message: Message, - { search, page }: { search: string; page: number } - ): Promise { - const foundUser = await this.client.util.resolveUserAsync(search); - if (foundUser) { - const logs = await Modlog.findAll({ - where: { - guild: message.guild.id, - user: foundUser.id - }, - order: [['createdAt', 'ASC']] - }); - const niceLogs: string[] = []; - for (const log of logs) { - niceLogs.push(stripIndent` - ID: ${log.id} - Type: ${log.type.toLowerCase()} - User: <@!${log.user}> (${log.user}) - Moderator: <@!${log.moderator}> (${log.moderator}) - Duration: ${ - log.duration - ? moment.duration(log.duration, 'milliseconds').humanize() - : 'N/A' - } - Reason: ${log.reason || 'None given'} - ${this.client.util.ordinal(logs.indexOf(log) + 1)} action - `); - } - const chunked: string[][] = this.client.util.chunk(niceLogs, 3); - const embedPages = chunked.map( - (e, i) => - new MessageEmbed({ - title: `Modlogs page ${i + 1}`, - description: e.join( - '\n-------------------------------------------------------\n' - ), - footer: { - text: `Page ${i + 1}/${chunked.length}` - } - }) - ); - if (page) { - await message.util.send(embedPages[page - 1]); - return; - } else { - await message.util.send(embedPages[0]); - return; - } - } else if (search) { - const entry = await Modlog.findByPk(search); - if (!entry) { - 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 - } - ] - }) - ); - } - } -} diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts new file mode 100644 index 0000000..d5948c3 --- /dev/null +++ b/src/commands/moderation/role.ts @@ -0,0 +1,181 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +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 = { + 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Suggester': [ + '*', + 'Admin Perms', + 'Sr. Moderator', + 'Moderator', + 'Helper', + 'Trial Helper', + 'Contributor' + ], + 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'No Giveaways': [ + '*', + 'Admin Perms', + 'Sr. Moderator', + 'Moderator', + 'Helper' + ], + 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (100m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (50m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (25m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (10m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (5m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], + 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] + }; + constructor() { + super('role', { + aliases: ['role', 'addrole', 'removerole'], + category: 'moderation', + description: { + content: "Manages users' roles.", + usage: 'role ', + examples: ['role add tyman adminperms'] + }, + clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'], + channel: 'guild', + typing: true, + args: [ + { + id: 'user', + type: 'member', + prompt: { + start: `What user do you want to add/remove the role on?`, + retry: `<:error:837123021016924261> Choose a valid user to add/remove the role on.` + } + }, + { + id: 'role', + type: 'role', + match: 'restContent', + prompt: { + start: `What role do you want to add/remove?`, + retry: `<:error:837123021016924261> Choose a valid role to add/remove.` + } + } + ], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to add/remove the role on', + required: true + }, + { + type: ApplicationCommandOptionType.ROLE, + name: 'role', + description: 'The role to add/remove', + required: true + } + ] + }); + } + + public async exec( + message: Message, + { user, role }: { user: GuildMember; role: Role } + ): Promise { + 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() + } + ); + } + 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() + } + ); + } + } + 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.`, + { + 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.`, + { + 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() + } + ); + } + } + // No checks if the user has MANAGE_ROLES + if (user.roles.cache.has(role.id)) { + try { + await user.roles.remove(role.id); + } catch { + return message.util.reply( + `<: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}>!`, + { 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}>.`, + { allowedMentions: AllowedMentions.none() } + ); + } + return message.util.reply( + `<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, + { allowedMentions: AllowedMentions.none() } + ); + } + } +} diff --git a/src/commands/moderation/roleCommand.ts b/src/commands/moderation/roleCommand.ts deleted file mode 100644 index d5948c3..0000000 --- a/src/commands/moderation/roleCommand.ts +++ /dev/null @@ -1,181 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -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 = { - 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Suggester': [ - '*', - 'Admin Perms', - 'Sr. Moderator', - 'Moderator', - 'Helper', - 'Trial Helper', - 'Contributor' - ], - 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Giveaways': [ - '*', - 'Admin Perms', - 'Sr. Moderator', - 'Moderator', - 'Helper' - ], - 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (100m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (50m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (25m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (10m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (5m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Giveaway (1m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'] - }; - constructor() { - super('role', { - aliases: ['role', 'addrole', 'removerole'], - category: 'moderation', - description: { - content: "Manages users' roles.", - usage: 'role ', - examples: ['role add tyman adminperms'] - }, - clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - typing: true, - args: [ - { - id: 'user', - type: 'member', - prompt: { - start: `What user do you want to add/remove the role on?`, - retry: `<:error:837123021016924261> Choose a valid user to add/remove the role on.` - } - }, - { - id: 'role', - type: 'role', - match: 'restContent', - prompt: { - start: `What role do you want to add/remove?`, - retry: `<:error:837123021016924261> Choose a valid role to add/remove.` - } - } - ], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to add/remove the role on', - required: true - }, - { - type: ApplicationCommandOptionType.ROLE, - name: 'role', - description: 'The role to add/remove', - required: true - } - ] - }); - } - - public async exec( - message: Message, - { user, role }: { user: GuildMember; role: Role } - ): Promise { - 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() - } - ); - } - 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() - } - ); - } - } - 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.`, - { - 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.`, - { - 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() - } - ); - } - } - // No checks if the user has MANAGE_ROLES - if (user.roles.cache.has(role.id)) { - try { - await user.roles.remove(role.id); - } catch { - return message.util.reply( - `<: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}>!`, - { 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}>.`, - { allowedMentions: AllowedMentions.none() } - ); - } - return message.util.reply( - `<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, - { allowedMentions: AllowedMentions.none() } - ); - } - } -} diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts new file mode 100644 index 0000000..a61eef3 --- /dev/null +++ b/src/commands/moderation/warn.ts @@ -0,0 +1,68 @@ +import { GuildMember, Message } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { Guild, Modlog, ModlogType } from '../../lib/models'; + +export default class WarnCommand extends BushCommand { + public constructor() { + super('warn', { + aliases: ['warn'], + category: 'moderation', + userPermissions: ['MANAGE_MESSAGES'], + args: [ + { + id: 'member', + type: 'member' + }, + { + id: 'reason', + match: 'rest' + } + ], + description: { + content: 'Warn a member and log it in modlogs', + usage: 'warn ', + examples: ['warn @Tyman being cool'] + } + }); + } + public async exec( + message: Message, + { member, reason }: { member: GuildMember; reason: string } + ): Promise { + // Create guild entry so postgres doesn't get mad when I try and add a modlog entry + await Guild.findOrCreate({ + where: { + id: message.guild.id + }, + defaults: { + id: message.guild.id + } + }); + try { + const entry = Modlog.build({ + user: member.id, + guild: message.guild.id, + moderator: message.author.id, + type: ModlogType.WARN, + reason + }); + await entry.save(); + } catch (e) { + await message.util.send( + 'Error saving to database, please contact the developers' + ); + return; + } + try { + await member.send( + `You were warned in ${message.guild.name} for reason "${reason}".` + ); + } catch (e) { + await message.util.send('Error messaging user, warning still saved.'); + return; + } + await message.util.send( + `${member.user.tag} was warned for reason "${reason}".` + ); + } +} diff --git a/src/commands/moderation/warnCommand.ts b/src/commands/moderation/warnCommand.ts deleted file mode 100644 index a61eef3..0000000 --- a/src/commands/moderation/warnCommand.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { GuildMember, Message } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { Guild, Modlog, ModlogType } from '../../lib/models'; - -export default class WarnCommand extends BushCommand { - public constructor() { - super('warn', { - aliases: ['warn'], - category: 'moderation', - userPermissions: ['MANAGE_MESSAGES'], - args: [ - { - id: 'member', - type: 'member' - }, - { - id: 'reason', - match: 'rest' - } - ], - description: { - content: 'Warn a member and log it in modlogs', - usage: 'warn ', - examples: ['warn @Tyman being cool'] - } - }); - } - public async exec( - message: Message, - { member, reason }: { member: GuildMember; reason: string } - ): Promise { - // Create guild entry so postgres doesn't get mad when I try and add a modlog entry - await Guild.findOrCreate({ - where: { - id: message.guild.id - }, - defaults: { - id: message.guild.id - } - }); - try { - const entry = Modlog.build({ - user: member.id, - guild: message.guild.id, - moderator: message.author.id, - type: ModlogType.WARN, - reason - }); - await entry.save(); - } catch (e) { - await message.util.send( - 'Error saving to database, please contact the developers' - ); - return; - } - try { - await member.send( - `You were warned in ${message.guild.name} for reason "${reason}".` - ); - } catch (e) { - await message.util.send('Error messaging user, warning still saved.'); - return; - } - await message.util.send( - `${member.user.tag} was warned for reason "${reason}".` - ); - } -} diff --git a/src/commands/moulberry-bush/capePermissionsCommand.ts b/src/commands/moulberry-bush/capePermissionsCommand.ts deleted file mode 100644 index fdb5caf..0000000 --- a/src/commands/moulberry-bush/capePermissionsCommand.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { MessageEmbed } from 'discord.js'; -import { CommandInteraction } from 'discord.js'; -import { Message } from 'discord.js'; -import got from 'got'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { SlashCommandOption } from '../../lib/extensions/Util'; - -interface Capeperms { - success: boolean; - perms: User[]; -} -interface User { - _id: string; - perms: string[]; -} - -export default class CapePermissionsCommand extends BushCommand { - private nameMap = { - patreon1: 'Patreon Tier 1', - patreon2: 'Patreon Tier 2', - fade: 'Fade', - contrib: 'Contributor', - nullzee: 'Nullzee', - gravy: 'ThatGravyBoat', - space: 'Space', - mcworld: 'Minecraft World', - lava: 'Lava', - packshq: 'PacksHQ', - mbstaff: "Moulberry's Bush staff", - thebakery: "Biscuit's Bakery", - negative: 'Negative', - void: 'Void', - ironmoon: 'IRONM00N', - krusty: 'Krusty', - furf: 'FurfSky Reborn', - soldier: 'Soldier', - dsm: "Danker's Skyblock Mod", - zera: 'Zera', - tunnel: 'Tunnel', - alexxoffi: 'Alexxoffi', - parallax: 'Parallax', - jakethybro: 'Jakethybro', - planets: 'Planets' - }; - public constructor() { - super('capeperms', { - aliases: ['capeperms', 'capeperm', 'capepermissions', 'capepermission'], - category: "Moulberry's Bush", - description: { - content: 'A command to see what capes someone has access to.', - usage: 'capeperms ', - examples: ['capeperms IRONM00N'] - }, - args: [ - { - id: 'user', - type: 'string', - prompt: { - start: 'Who would you like to see the cape permissions of?', - retry: - '<:error:837123021016924261> Choose someone to see the capes their available capes.', - optional: false - } - } - ], - clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.STRING, - name: 'user', - description: - 'The username of the player to see the cape permissions of', - required: true - } - ] - }); - } - private async getResponse(user: string): Promise { - 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.`; - } - - try { - capeperms = await got - .get('http://moulberry.codes/permscapes.json') - .json(); - } catch (error) { - capeperms = null; - } - if (capeperms == null) { - return `<: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.`; - 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; - } else { - return `<:error:837123021016924261> There was an error finding cape perms for ${user}.`; - } - } - } - public async exec( - message: Message, - { user }: { user: string } - ): Promise { - await message.reply(await this.getResponse(user)); - } - - public async execSlash( - message: CommandInteraction, - { user }: { user: SlashCommandOption } - ): Promise { - await message.reply(await this.getResponse(user.value)); - } -} diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts new file mode 100644 index 0000000..fdb5caf --- /dev/null +++ b/src/commands/moulberry-bush/capePerms.ts @@ -0,0 +1,126 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { MessageEmbed } from 'discord.js'; +import { CommandInteraction } from 'discord.js'; +import { Message } from 'discord.js'; +import got from 'got'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { SlashCommandOption } from '../../lib/extensions/Util'; + +interface Capeperms { + success: boolean; + perms: User[]; +} +interface User { + _id: string; + perms: string[]; +} + +export default class CapePermissionsCommand extends BushCommand { + private nameMap = { + patreon1: 'Patreon Tier 1', + patreon2: 'Patreon Tier 2', + fade: 'Fade', + contrib: 'Contributor', + nullzee: 'Nullzee', + gravy: 'ThatGravyBoat', + space: 'Space', + mcworld: 'Minecraft World', + lava: 'Lava', + packshq: 'PacksHQ', + mbstaff: "Moulberry's Bush staff", + thebakery: "Biscuit's Bakery", + negative: 'Negative', + void: 'Void', + ironmoon: 'IRONM00N', + krusty: 'Krusty', + furf: 'FurfSky Reborn', + soldier: 'Soldier', + dsm: "Danker's Skyblock Mod", + zera: 'Zera', + tunnel: 'Tunnel', + alexxoffi: 'Alexxoffi', + parallax: 'Parallax', + jakethybro: 'Jakethybro', + planets: 'Planets' + }; + public constructor() { + super('capeperms', { + aliases: ['capeperms', 'capeperm', 'capepermissions', 'capepermission'], + category: "Moulberry's Bush", + description: { + content: 'A command to see what capes someone has access to.', + usage: 'capeperms ', + examples: ['capeperms IRONM00N'] + }, + args: [ + { + id: 'user', + type: 'string', + prompt: { + start: 'Who would you like to see the cape permissions of?', + retry: + '<:error:837123021016924261> Choose someone to see the capes their available capes.', + optional: false + } + } + ], + clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], + channel: 'guild', + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.STRING, + name: 'user', + description: + 'The username of the player to see the cape permissions of', + required: true + } + ] + }); + } + private async getResponse(user: string): Promise { + 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.`; + } + + try { + capeperms = await got + .get('http://moulberry.codes/permscapes.json') + .json(); + } catch (error) { + capeperms = null; + } + if (capeperms == null) { + return `<: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.`; + 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; + } else { + return `<:error:837123021016924261> There was an error finding cape perms for ${user}.`; + } + } + } + public async exec( + message: Message, + { user }: { user: string } + ): Promise { + await message.reply(await this.getResponse(user)); + } + + public async execSlash( + message: CommandInteraction, + { user }: { user: SlashCommandOption } + ): Promise { + await message.reply(await this.getResponse(user.value)); + } +} diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts new file mode 100644 index 0000000..e6fdd9a --- /dev/null +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -0,0 +1,68 @@ +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() { + super('giveawayping', { + aliases: ['giveawayping', 'giveawaypong'], + category: "Moulberry's Bush", + description: { + content: 'Pings the giveaway role.', + usage: 'giveawayping', + examples: ['giveawayping'] + }, + clientPermissions: ['MANAGE_MESSAGES'], + userPermissions: [ + 'SEND_MESSAGES', + 'MANAGE_GUILD', + 'MANAGE_MESSAGES', + 'BAN_MEMBERS', + 'KICK_MEMBERS', + 'VIEW_CHANNEL' + ], + channel: 'guild', + ignoreCooldown: [], + ignorePermissions: [], + cooldown: 1.44e7, //4 hours + ratelimit: 1, + editable: false + }); + } + public async exec(message: Message): Promise { + if (message.guild.id !== '516977525906341928') + return message.reply( + "<:error:837123021016924261> This command may only be run in Moulberry's Bush." + ); + if ( + !['767782084981817344', '833855738501267456'].includes(message.channel.id) + ) + return message.reply( + '<:error:837123021016924261> This command may only be run in giveaway channels.' + ); + await message.delete().catch(() => undefined); + const webhooks = await ( + message.channel as TextChannel | NewsChannel + ).fetchWebhooks(); + let webhookClient: WebhookClient; + if (webhooks.size < 1) { + const webhook = await ( + message.channel as TextChannel | NewsChannel + ).createWebhook('Giveaway ping webhook'); + webhookClient = new WebhookClient(webhook.id, webhook.token); + } else { + 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() + } + ); + } +} diff --git a/src/commands/moulberry-bush/giveawayPingCommand.ts b/src/commands/moulberry-bush/giveawayPingCommand.ts deleted file mode 100644 index e6fdd9a..0000000 --- a/src/commands/moulberry-bush/giveawayPingCommand.ts +++ /dev/null @@ -1,68 +0,0 @@ -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() { - super('giveawayping', { - aliases: ['giveawayping', 'giveawaypong'], - category: "Moulberry's Bush", - description: { - content: 'Pings the giveaway role.', - usage: 'giveawayping', - examples: ['giveawayping'] - }, - clientPermissions: ['MANAGE_MESSAGES'], - userPermissions: [ - 'SEND_MESSAGES', - 'MANAGE_GUILD', - 'MANAGE_MESSAGES', - 'BAN_MEMBERS', - 'KICK_MEMBERS', - 'VIEW_CHANNEL' - ], - channel: 'guild', - ignoreCooldown: [], - ignorePermissions: [], - cooldown: 1.44e7, //4 hours - ratelimit: 1, - editable: false - }); - } - public async exec(message: Message): Promise { - if (message.guild.id !== '516977525906341928') - return message.reply( - "<:error:837123021016924261> This command may only be run in Moulberry's Bush." - ); - if ( - !['767782084981817344', '833855738501267456'].includes(message.channel.id) - ) - return message.reply( - '<:error:837123021016924261> This command may only be run in giveaway channels.' - ); - await message.delete().catch(() => undefined); - const webhooks = await ( - message.channel as TextChannel | NewsChannel - ).fetchWebhooks(); - let webhookClient: WebhookClient; - if (webhooks.size < 1) { - const webhook = await ( - message.channel as TextChannel | NewsChannel - ).createWebhook('Giveaway ping webhook'); - webhookClient = new WebhookClient(webhook.id, webhook.token); - } else { - 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() - } - ); - } -} diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/moulberry-bush/level.ts new file mode 100644 index 0000000..decac8a --- /dev/null +++ b/src/commands/moulberry-bush/level.ts @@ -0,0 +1,62 @@ +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 { BushCommand } from '../../lib/extensions/BushCommand'; +import { Level } from '../../lib/models'; + +export default class LevelCommand extends BushCommand { + constructor() { + super('level', { + aliases: ['level', 'rank'], + category: "Moulberry's Bush", + description: { + content: 'Shows the level of a user', + usage: 'level [user]', + examples: ['level', 'level @Tyman'] + }, + args: [ + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to see the level of?', + retry: + 'Invalid user. What user would you like to see the level of?', + optional: true + } + } + ], + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to get the level of', + required: false + } + ] + }); + } + + private async getResponse(user: User): Promise { + const userLevelRow = await Level.findByPk(user.id); + if (userLevelRow) { + return `${user ? `${user.tag}'s` : 'Your'} level is ${ + userLevelRow.level + } (${userLevelRow.xp} XP)`; + } else { + return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; + } + } + + async exec(message: Message, { user }: { user?: User }): Promise { + await message.reply(await this.getResponse(user || message.author)); + } + async execSlash( + message: CommandInteraction, + { user }: { user?: CommandInteractionOption } + ): Promise { + await message.reply(await this.getResponse(user?.user || message.user)); + } +} diff --git a/src/commands/moulberry-bush/levelCommand.ts b/src/commands/moulberry-bush/levelCommand.ts deleted file mode 100644 index decac8a..0000000 --- a/src/commands/moulberry-bush/levelCommand.ts +++ /dev/null @@ -1,62 +0,0 @@ -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 { BushCommand } from '../../lib/extensions/BushCommand'; -import { Level } from '../../lib/models'; - -export default class LevelCommand extends BushCommand { - constructor() { - super('level', { - aliases: ['level', 'rank'], - category: "Moulberry's Bush", - description: { - content: 'Shows the level of a user', - usage: 'level [user]', - examples: ['level', 'level @Tyman'] - }, - args: [ - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to see the level of?', - retry: - 'Invalid user. What user would you like to see the level of?', - optional: true - } - } - ], - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to get the level of', - required: false - } - ] - }); - } - - private async getResponse(user: User): Promise { - const userLevelRow = await Level.findByPk(user.id); - if (userLevelRow) { - return `${user ? `${user.tag}'s` : 'Your'} level is ${ - userLevelRow.level - } (${userLevelRow.xp} XP)`; - } else { - return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; - } - } - - async exec(message: Message, { user }: { user?: User }): Promise { - await message.reply(await this.getResponse(user || message.author)); - } - async execSlash( - message: CommandInteraction, - { user }: { user?: CommandInteractionOption } - ): Promise { - await message.reply(await this.getResponse(user?.user || message.user)); - } -} diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts new file mode 100644 index 0000000..69dcb6e --- /dev/null +++ b/src/commands/moulberry-bush/rule.ts @@ -0,0 +1,188 @@ +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 { SlashCommandOption } from '../../lib/extensions/Util'; + +export default class RuleCommand extends BushCommand { + private rules = [ + { + title: "Follow Discord's TOS", + description: + "Be sure to follow discord's TOS found at , you must be 13 to use discord so if you admit to being under 13 you will be banned from the server." + }, + { + title: 'Be Respectful', + description: + 'Racist, sexist, homophobic, xenophobic, transphobic, ableist, hate speech, slurs, or any other derogatory, toxic, or discriminatory behavior will not be tolerated.' + }, + { + title: 'No Spamming', + description: + 'Including but not limited to: any messages that do not contribute to the conversation, repeated messages, randomly tagging users, and chat flood.' + }, + { + title: 'English', + description: + 'The primary language of the server is English, please keep all discussions in English.' + }, + { + title: 'Safe for Work', + description: + 'Please keep NSFW and NSFL content out of this server, avoid borderline images as well as keeping your status and profile picture SFW.' + }, + { + title: 'No Advertising', + description: + 'Do not promote anything without prior approval from a staff member, this includes DM advertising.' + }, + { + title: 'Impersonation', + description: + 'Do not try to impersonate others for the express intent of being deceitful, defamation , and/or personal gain.' + }, + { + title: 'Swearing', + description: 'Swearing is allowed only when not used as an insult.' + }, + { + title: 'Only ping @emergency in emergencies', + description: + 'Pinging <@&833802660209229854> for no reason will result in severe punishment. <@&833802660209229854> is only to be pinged in true emergencies.' + }, + { + title: 'No Backseat Moderating', + description: + 'If you see a rule being broken be broken, please report it using: `-report [evidence]`.' + }, + { + title: 'Staff may moderate at their discretion', + description: + 'If there are loopholes in our rules, the staff team may moderate based on what they deem appropriate. The staff team holds final discretion.' + }, + { + title: "Sending media that are able to crash a user's Discord", + description: + "Sending videos, GIFs, emojis, etc. that are able to crash someone's discord will result in a **permanent** mute that cannot be appealed." + } + ]; + + public constructor() { + super('rule', { + aliases: ['rule', 'rules'], + category: "Moulberry's Bush", + description: { + content: 'A command to state a rule.', + usage: 'rule [user]', + examples: ['rule 1 IRONM00N', 'rule 2', 'rules'] + }, + args: [ + { + id: 'rule', + type: Argument.range('number', 1, 12, true), + prompt: { + start: 'What rule would you like to have cited?', + retry: '<:no:787549684196704257> Choose a valid rule.', + optional: true + }, + default: undefined + }, + { + id: 'user', + type: 'user', + prompt: { + start: 'What user would you like to mention?', + retry: '<:no:787549684196704257> Choose a valid user to mention.', + optional: true + }, + default: undefined + } + ], + clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], + channel: 'guild', + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.INTEGER, + name: 'rule', + description: 'The rule to show', + required: false + }, + { + type: ApplicationCommandOptionType.USER, + name: 'user', + description: 'The user to ping', + required: false + } + ] + }); + } + private getResponse( + message: Message | CommandInteraction, + rule?: number, + user?: User + ): string | 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."; + } + let rulesEmbed = new MessageEmbed().setColor('ef3929'); + if (message instanceof Message) { + rulesEmbed = rulesEmbed.setFooter( + `Triggered by ${message.author.tag}`, + message.author.avatarURL({ dynamic: true }) + ); + } + if (rule) { + const foundRule = this.rules[rule - 1]; + rulesEmbed.addField(`${rule}) ${foundRule.title}`, foundRule.description); + } else { + for (const curRule of this.rules) { + rulesEmbed.addField( + `${this.rules.indexOf(curRule) + 1}) ${curRule.title}`, + curRule.description + ); + } + } + if (!user) { + return rulesEmbed; + } else { + return [`<@!${user.id}>`, rulesEmbed]; + } + } + public async exec( + message: Message, + { rule, user }: { rule?: number; user?: User } + ): Promise { + const response = this.getResponse(message, rule, user); + if (Array.isArray(response)) { + await message.util.send(response[0], { + embed: response[1] + }); + } else { + await message.util.send(response); + } + await message.delete().catch(() => undefined); + } + + public async execSlash( + message: CommandInteraction, + { + rule, + user + }: { rule?: SlashCommandOption; user?: SlashCommandOption } + ): Promise { + const response = this.getResponse(message, rule?.value, user?.user); + if (Array.isArray(response)) { + await message.reply(response[0], { + embeds: [response[1]] + }); + } else { + await message.reply(response); + } + } +} diff --git a/src/commands/moulberry-bush/ruleCommand.ts b/src/commands/moulberry-bush/ruleCommand.ts deleted file mode 100644 index 69dcb6e..0000000 --- a/src/commands/moulberry-bush/ruleCommand.ts +++ /dev/null @@ -1,188 +0,0 @@ -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 { SlashCommandOption } from '../../lib/extensions/Util'; - -export default class RuleCommand extends BushCommand { - private rules = [ - { - title: "Follow Discord's TOS", - description: - "Be sure to follow discord's TOS found at , you must be 13 to use discord so if you admit to being under 13 you will be banned from the server." - }, - { - title: 'Be Respectful', - description: - 'Racist, sexist, homophobic, xenophobic, transphobic, ableist, hate speech, slurs, or any other derogatory, toxic, or discriminatory behavior will not be tolerated.' - }, - { - title: 'No Spamming', - description: - 'Including but not limited to: any messages that do not contribute to the conversation, repeated messages, randomly tagging users, and chat flood.' - }, - { - title: 'English', - description: - 'The primary language of the server is English, please keep all discussions in English.' - }, - { - title: 'Safe for Work', - description: - 'Please keep NSFW and NSFL content out of this server, avoid borderline images as well as keeping your status and profile picture SFW.' - }, - { - title: 'No Advertising', - description: - 'Do not promote anything without prior approval from a staff member, this includes DM advertising.' - }, - { - title: 'Impersonation', - description: - 'Do not try to impersonate others for the express intent of being deceitful, defamation , and/or personal gain.' - }, - { - title: 'Swearing', - description: 'Swearing is allowed only when not used as an insult.' - }, - { - title: 'Only ping @emergency in emergencies', - description: - 'Pinging <@&833802660209229854> for no reason will result in severe punishment. <@&833802660209229854> is only to be pinged in true emergencies.' - }, - { - title: 'No Backseat Moderating', - description: - 'If you see a rule being broken be broken, please report it using: `-report [evidence]`.' - }, - { - title: 'Staff may moderate at their discretion', - description: - 'If there are loopholes in our rules, the staff team may moderate based on what they deem appropriate. The staff team holds final discretion.' - }, - { - title: "Sending media that are able to crash a user's Discord", - description: - "Sending videos, GIFs, emojis, etc. that are able to crash someone's discord will result in a **permanent** mute that cannot be appealed." - } - ]; - - public constructor() { - super('rule', { - aliases: ['rule', 'rules'], - category: "Moulberry's Bush", - description: { - content: 'A command to state a rule.', - usage: 'rule [user]', - examples: ['rule 1 IRONM00N', 'rule 2', 'rules'] - }, - args: [ - { - id: 'rule', - type: Argument.range('number', 1, 12, true), - prompt: { - start: 'What rule would you like to have cited?', - retry: '<:no:787549684196704257> Choose a valid rule.', - optional: true - }, - default: undefined - }, - { - id: 'user', - type: 'user', - prompt: { - start: 'What user would you like to mention?', - retry: '<:no:787549684196704257> Choose a valid user to mention.', - optional: true - }, - default: undefined - } - ], - clientPermissions: ['EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.INTEGER, - name: 'rule', - description: 'The rule to show', - required: false - }, - { - type: ApplicationCommandOptionType.USER, - name: 'user', - description: 'The user to ping', - required: false - } - ] - }); - } - private getResponse( - message: Message | CommandInteraction, - rule?: number, - user?: User - ): string | 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."; - } - let rulesEmbed = new MessageEmbed().setColor('ef3929'); - if (message instanceof Message) { - rulesEmbed = rulesEmbed.setFooter( - `Triggered by ${message.author.tag}`, - message.author.avatarURL({ dynamic: true }) - ); - } - if (rule) { - const foundRule = this.rules[rule - 1]; - rulesEmbed.addField(`${rule}) ${foundRule.title}`, foundRule.description); - } else { - for (const curRule of this.rules) { - rulesEmbed.addField( - `${this.rules.indexOf(curRule) + 1}) ${curRule.title}`, - curRule.description - ); - } - } - if (!user) { - return rulesEmbed; - } else { - return [`<@!${user.id}>`, rulesEmbed]; - } - } - public async exec( - message: Message, - { rule, user }: { rule?: number; user?: User } - ): Promise { - const response = this.getResponse(message, rule, user); - if (Array.isArray(response)) { - await message.util.send(response[0], { - embed: response[1] - }); - } else { - await message.util.send(response); - } - await message.delete().catch(() => undefined); - } - - public async execSlash( - message: CommandInteraction, - { - rule, - user - }: { rule?: SlashCommandOption; user?: SlashCommandOption } - ): Promise { - const response = this.getResponse(message, rule?.value, user?.user); - if (Array.isArray(response)) { - await message.reply(response[0], { - embeds: [response[1]] - }); - } else { - await message.reply(response); - } - } -} diff --git a/src/commands/server-config/prefix.ts b/src/commands/server-config/prefix.ts new file mode 100644 index 0000000..cf70bd2 --- /dev/null +++ b/src/commands/server-config/prefix.ts @@ -0,0 +1,71 @@ +import { ApplicationCommandOptionType } from 'discord-api-types'; +import { CommandInteraction, Message, Guild as DiscordGuild } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { SlashCommandOption } from '../../lib/extensions/Util'; +import { Guild } from '../../lib/models'; + +export default class PrefixCommand extends BushCommand { + constructor() { + super('prefix', { + aliases: ['prefix'], + category: 'server config', + args: [ + { + id: 'prefix' + } + ], + userPermissions: ['MANAGE_GUILD'], + description: { + content: + 'Set the prefix of the current server (resets to default if prefix is not given)', + usage: 'prefix [prefix]', + examples: ['prefix', 'prefix +'] + }, + slashCommandOptions: [ + { + type: ApplicationCommandOptionType.STRING, + name: 'prefix', + description: 'The prefix to set for this server', + required: false + } + ] + }); + } + + async changePrefix(guild: DiscordGuild, prefix?: string): Promise { + if (prefix) { + const row = await Guild.findByPk(guild.id); + row.prefix = prefix; + await row.save(); + } else { + const row = await Guild.findByPk(guild.id); + row.prefix = this.client.config.prefix; + await row.save(); + } + } + + async exec(message: Message, { prefix }: { prefix?: string }): Promise { + await this.changePrefix(message.guild, prefix); + if (prefix) { + await message.util.send(`Sucessfully set prefix to \`${prefix}\``); + } else { + await message.util.send( + `Sucessfully reset prefix to \`${this.client.config.prefix}\`` + ); + } + } + + async execSlash( + message: CommandInteraction, + { prefix }: { prefix?: SlashCommandOption } + ): Promise { + await this.changePrefix(message.guild, prefix?.value); + if (prefix) { + await message.reply(`Sucessfully set prefix to \`${prefix.value}\``); + } else { + await message.reply( + `Sucessfully reset prefix to \`${this.client.config.prefix}\`` + ); + } + } +} diff --git a/src/commands/server-config/prefixCommand.ts b/src/commands/server-config/prefixCommand.ts deleted file mode 100644 index cf70bd2..0000000 --- a/src/commands/server-config/prefixCommand.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { ApplicationCommandOptionType } from 'discord-api-types'; -import { CommandInteraction, Message, Guild as DiscordGuild } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { SlashCommandOption } from '../../lib/extensions/Util'; -import { Guild } from '../../lib/models'; - -export default class PrefixCommand extends BushCommand { - constructor() { - super('prefix', { - aliases: ['prefix'], - category: 'server config', - args: [ - { - id: 'prefix' - } - ], - userPermissions: ['MANAGE_GUILD'], - description: { - content: - 'Set the prefix of the current server (resets to default if prefix is not given)', - usage: 'prefix [prefix]', - examples: ['prefix', 'prefix +'] - }, - slashCommandOptions: [ - { - type: ApplicationCommandOptionType.STRING, - name: 'prefix', - description: 'The prefix to set for this server', - required: false - } - ] - }); - } - - async changePrefix(guild: DiscordGuild, prefix?: string): Promise { - if (prefix) { - const row = await Guild.findByPk(guild.id); - row.prefix = prefix; - await row.save(); - } else { - const row = await Guild.findByPk(guild.id); - row.prefix = this.client.config.prefix; - await row.save(); - } - } - - async exec(message: Message, { prefix }: { prefix?: string }): Promise { - await this.changePrefix(message.guild, prefix); - if (prefix) { - await message.util.send(`Sucessfully set prefix to \`${prefix}\``); - } else { - await message.util.send( - `Sucessfully reset prefix to \`${this.client.config.prefix}\`` - ); - } - } - - async execSlash( - message: CommandInteraction, - { prefix }: { prefix?: SlashCommandOption } - ): Promise { - await this.changePrefix(message.guild, prefix?.value); - if (prefix) { - await message.reply(`Sucessfully set prefix to \`${prefix.value}\``); - } else { - await message.reply( - `Sucessfully reset prefix to \`${this.client.config.prefix}\`` - ); - } - } -} diff --git a/src/inhibitors/blacklist/BlacklistInhibitor.ts b/src/inhibitors/blacklist/BlacklistInhibitor.ts deleted file mode 100644 index 309815f..0000000 --- a/src/inhibitors/blacklist/BlacklistInhibitor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BushInhibitor } from '../../lib/extensions/BushInhibitor'; - -export default class BlacklistInhibitor extends BushInhibitor { - constructor() { - super('blacklist', { - reason: 'blacklist' - }); - } - - public exec(): boolean | Promise { - // This is just a placeholder for now - return false; - } -} diff --git a/src/inhibitors/blacklist/blacklist.ts b/src/inhibitors/blacklist/blacklist.ts new file mode 100644 index 0000000..309815f --- /dev/null +++ b/src/inhibitors/blacklist/blacklist.ts @@ -0,0 +1,14 @@ +import { BushInhibitor } from '../../lib/extensions/BushInhibitor'; + +export default class BlacklistInhibitor extends BushInhibitor { + constructor() { + super('blacklist', { + reason: 'blacklist' + }); + } + + public exec(): boolean | Promise { + // This is just a placeholder for now + return false; + } +} diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts new file mode 100644 index 0000000..8d4ac6a --- /dev/null +++ b/src/listeners/client/ready.ts @@ -0,0 +1,18 @@ +import chalk from 'chalk'; +import { BushListener } from '../../lib/extensions/BushListener'; + +export default class ReadyListener extends BushListener { + public constructor() { + super('ready', { + emitter: 'client', + event: 'ready' + }); + } + + public async exec(): Promise { + await this.client.logger.log( + chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, + true + ); + } +} diff --git a/src/listeners/client/readyListener.ts b/src/listeners/client/readyListener.ts deleted file mode 100644 index 8d4ac6a..0000000 --- a/src/listeners/client/readyListener.ts +++ /dev/null @@ -1,18 +0,0 @@ -import chalk from 'chalk'; -import { BushListener } from '../../lib/extensions/BushListener'; - -export default class ReadyListener extends BushListener { - public constructor() { - super('ready', { - emitter: 'client', - event: 'ready' - }); - } - - public async exec(): Promise { - await this.client.logger.log( - chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, - true - ); - } -} diff --git a/src/listeners/client/syncSlashCommands.ts b/src/listeners/client/syncSlashCommands.ts new file mode 100644 index 0000000..7835136 --- /dev/null +++ b/src/listeners/client/syncSlashCommands.ts @@ -0,0 +1,22 @@ +import { BushListener } from '../../lib/extensions/BushListener'; + +export default class SyncSlashCommandsListener extends BushListener { + constructor() { + super('syncslashcommands', { + emitter: 'client', + event: 'ready' + }); + } + async exec(): Promise { + if (this.client.config.dev && this.client.config.devGuild) { + // Use guild slash commands for instant registration in dev + await this.client.util.syncSlashCommands( + false, + this.client.config.devGuild + ); + } else { + // Use global in production + await this.client.util.syncSlashCommands(); + } + } +} diff --git a/src/listeners/client/syncSlashCommandsListener.ts b/src/listeners/client/syncSlashCommandsListener.ts deleted file mode 100644 index 7835136..0000000 --- a/src/listeners/client/syncSlashCommandsListener.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { BushListener } from '../../lib/extensions/BushListener'; - -export default class SyncSlashCommandsListener extends BushListener { - constructor() { - super('syncslashcommands', { - emitter: 'client', - event: 'ready' - }); - } - async exec(): Promise { - if (this.client.config.dev && this.client.config.devGuild) { - // Use guild slash commands for instant registration in dev - await this.client.util.syncSlashCommands( - false, - this.client.config.devGuild - ); - } else { - // Use global in production - await this.client.util.syncSlashCommands(); - } - } -} diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts new file mode 100644 index 0000000..dee1319 --- /dev/null +++ b/src/listeners/commands/commandBlocked.ts @@ -0,0 +1,34 @@ +import { BushListener } from '../../lib/extensions/BushListener'; +import { Command } from 'discord-akairo'; +import { Message } from 'discord.js'; + +export default class CommandBlockedListener extends BushListener { + public constructor() { + super('commandBlocked', { + emitter: 'commandHandler', + event: 'commandBlocked' + }); + } + + public async exec( + message: Message, + command: Command, + reason: string + ): Promise { + switch (reason) { + case 'owner': { + await message.util.send( + `You must be an owner to run command \`${message.util.parsed.command}\`` + ); + break; + } + case 'blacklist': { + // pass + break; + } + default: { + await message.util.send(`Command blocked with reason \`${reason}\``); + } + } + } +} diff --git a/src/listeners/commands/commandBlockedListener.ts b/src/listeners/commands/commandBlockedListener.ts deleted file mode 100644 index dee1319..0000000 --- a/src/listeners/commands/commandBlockedListener.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { BushListener } from '../../lib/extensions/BushListener'; -import { Command } from 'discord-akairo'; -import { Message } from 'discord.js'; - -export default class CommandBlockedListener extends BushListener { - public constructor() { - super('commandBlocked', { - emitter: 'commandHandler', - event: 'commandBlocked' - }); - } - - public async exec( - message: Message, - command: Command, - reason: string - ): Promise { - switch (reason) { - case 'owner': { - await message.util.send( - `You must be an owner to run command \`${message.util.parsed.command}\`` - ); - break; - } - case 'blacklist': { - // pass - break; - } - default: { - await message.util.send(`Command blocked with reason \`${reason}\``); - } - } - } -} diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts new file mode 100644 index 0000000..518545a --- /dev/null +++ b/src/listeners/commands/commandError.ts @@ -0,0 +1,48 @@ +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() { + super('error', { + emitter: 'commandHandler', + event: 'error' + }); + } + async exec( + error: Error, + message: Message, + command?: BushCommand + ): Promise { + const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers + const errorDevEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle(`Error # \`${errorNumber}\`: An error occurred`) + .setDescription( + stripIndents`**User:** ${message.author} (${message.author.tag}) + **Command:** ${command} + **Channel:** ${message.channel} (${message.channel.id}) + **Message:** [link](${message.url})` + ) + .addField('Error', `${await this.client.util.haste(error.stack)}`); + let errorUserEmbed: MessageEmbed; + if (command) { + errorUserEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle('An error occurred') + .setDescription( + stripIndents`Whoops! It appears like something broke. + The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. + ` + ); + } + const channel = (await this.client.channels.fetch( + this.client.config.channels.log + )) as TextChannel; + await channel.send(errorDevEmbed); + if (errorUserEmbed) await message.reply(errorUserEmbed); + } +} diff --git a/src/listeners/commands/commandErrorListener.ts b/src/listeners/commands/commandErrorListener.ts deleted file mode 100644 index 518545a..0000000 --- a/src/listeners/commands/commandErrorListener.ts +++ /dev/null @@ -1,48 +0,0 @@ -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() { - super('error', { - emitter: 'commandHandler', - event: 'error' - }); - } - async exec( - error: Error, - message: Message, - command?: BushCommand - ): Promise { - const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers - const errorDevEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle(`Error # \`${errorNumber}\`: An error occurred`) - .setDescription( - stripIndents`**User:** ${message.author} (${message.author.tag}) - **Command:** ${command} - **Channel:** ${message.channel} (${message.channel.id}) - **Message:** [link](${message.url})` - ) - .addField('Error', `${await this.client.util.haste(error.stack)}`); - let errorUserEmbed: MessageEmbed; - if (command) { - errorUserEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle('An error occurred') - .setDescription( - stripIndents`Whoops! It appears like something broke. - The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. - ` - ); - } - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; - await channel.send(errorDevEmbed); - if (errorUserEmbed) await message.reply(errorUserEmbed); - } -} diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts new file mode 100644 index 0000000..d81ed59 --- /dev/null +++ b/src/listeners/commands/commandStarted.ts @@ -0,0 +1,24 @@ +import chalk from 'chalk'; +import { Message, DMChannel } from 'discord.js'; +import { BushCommand } from '../../lib/extensions/BushCommand'; +import { BushListener } from '../../lib/extensions/BushListener'; + +export default class CommandStartedListener extends BushListener { + constructor() { + super('logCommands', { + emitter: 'commandHandler', + event: 'commandStarted' + }); + } + exec(message: Message, command: BushCommand): void { + this.client.logger.verbose( + chalk`{cyan {green ${message.author.tag}} is running {green ${ + command.aliases[0] + }} in {green ${ + message.channel instanceof DMChannel + ? 'DMs' + : `#${message.channel.name} (Server: ${message.guild.name})` + }}.}` + ); + } +} diff --git a/src/listeners/commands/commandStartedListener.ts b/src/listeners/commands/commandStartedListener.ts deleted file mode 100644 index d81ed59..0000000 --- a/src/listeners/commands/commandStartedListener.ts +++ /dev/null @@ -1,24 +0,0 @@ -import chalk from 'chalk'; -import { Message, DMChannel } from 'discord.js'; -import { BushCommand } from '../../lib/extensions/BushCommand'; -import { BushListener } from '../../lib/extensions/BushListener'; - -export default class CommandStartedListener extends BushListener { - constructor() { - super('logCommands', { - emitter: 'commandHandler', - event: 'commandStarted' - }); - } - exec(message: Message, command: BushCommand): void { - this.client.logger.verbose( - chalk`{cyan {green ${message.author.tag}} is running {green ${ - command.aliases[0] - }} in {green ${ - message.channel instanceof DMChannel - ? 'DMs' - : `#${message.channel.name} (Server: ${message.guild.name})` - }}.}` - ); - } -} diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts new file mode 100644 index 0000000..a2b7bb7 --- /dev/null +++ b/src/listeners/commands/slashCommandError.ts @@ -0,0 +1,48 @@ +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() { + super('slashError', { + emitter: 'commandHandler', + event: 'slashError' + }); + } + async exec( + error: Error, + message: CommandInteraction, + command: BushCommand + ): Promise { + const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers + const errorDevEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle(`Slash Error # \`${errorNumber}\`: An error occurred`) + .setDescription( + stripIndents`**User:** <@${message.user.id}> (${message.user.tag}) + **Slash Command:** ${command} + **Channel:** <#${message.channelID}> (${message.channelID}) + **Message:** [link](https://discord.com/${message.guildID}/${message.channelID}/${message.id})` + ) + .addField('Error', `${await this.client.util.haste(error.stack)}`); + let errorUserEmbed: MessageEmbed; + if (command) { + errorUserEmbed = this.client.util + .createEmbed(this.client.util.colors.error) + .setTitle('An error occurred') + .setDescription( + stripIndents`Whoops! It appears like something broke. + The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. + ` + ); + } + const channel = (await this.client.channels.fetch( + this.client.config.channels.log + )) as TextChannel; + await channel.send(errorDevEmbed); + if (errorUserEmbed) await message.reply(errorUserEmbed); + } +} diff --git a/src/listeners/commands/slashCommandErrorListener.ts b/src/listeners/commands/slashCommandErrorListener.ts deleted file mode 100644 index a2b7bb7..0000000 --- a/src/listeners/commands/slashCommandErrorListener.ts +++ /dev/null @@ -1,48 +0,0 @@ -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() { - super('slashError', { - emitter: 'commandHandler', - event: 'slashError' - }); - } - async exec( - error: Error, - message: CommandInteraction, - command: BushCommand - ): Promise { - const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers - const errorDevEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle(`Slash Error # \`${errorNumber}\`: An error occurred`) - .setDescription( - stripIndents`**User:** <@${message.user.id}> (${message.user.tag}) - **Slash Command:** ${command} - **Channel:** <#${message.channelID}> (${message.channelID}) - **Message:** [link](https://discord.com/${message.guildID}/${message.channelID}/${message.id})` - ) - .addField('Error', `${await this.client.util.haste(error.stack)}`); - let errorUserEmbed: MessageEmbed; - if (command) { - errorUserEmbed = this.client.util - .createEmbed(this.client.util.colors.error) - .setTitle('An error occurred') - .setDescription( - stripIndents`Whoops! It appears like something broke. - The developers have been notified of this. If you contact them, give them code \`${errorNumber}\`. - ` - ); - } - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; - await channel.send(errorDevEmbed); - if (errorUserEmbed) await message.reply(errorUserEmbed); - } -} diff --git a/src/listeners/guild/syncUnban.ts b/src/listeners/guild/syncUnban.ts new file mode 100644 index 0000000..81a08f8 --- /dev/null +++ b/src/listeners/guild/syncUnban.ts @@ -0,0 +1,24 @@ +import { User, Guild } from 'discord.js'; +import { BushListener } from '../../lib/extensions/BushListener'; +import { Ban } from '../../lib/models'; + +export default class SyncUnbanListener extends BushListener { + public constructor() { + super('guildBanRemove', { + emitter: 'client', + event: 'guildBanRemove' + }); + } + + public async exec(guild: Guild, user: User): Promise { + const bans = await Ban.findAll({ + where: { + user: user.id, + guild: guild.id + } + }); + for (const dbBan of bans) { + await dbBan.destroy(); + } + } +} diff --git a/src/listeners/guild/syncUnbanListener.ts b/src/listeners/guild/syncUnbanListener.ts deleted file mode 100644 index 81a08f8..0000000 --- a/src/listeners/guild/syncUnbanListener.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { User, Guild } from 'discord.js'; -import { BushListener } from '../../lib/extensions/BushListener'; -import { Ban } from '../../lib/models'; - -export default class SyncUnbanListener extends BushListener { - public constructor() { - super('guildBanRemove', { - emitter: 'client', - event: 'guildBanRemove' - }); - } - - public async exec(guild: Guild, user: User): Promise { - const bans = await Ban.findAll({ - where: { - user: user.id, - guild: guild.id - } - }); - for (const dbBan of bans) { - await dbBan.destroy(); - } - } -} diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts new file mode 100644 index 0000000..3b23f36 --- /dev/null +++ b/src/listeners/message/level.ts @@ -0,0 +1,40 @@ +import chalk from 'chalk'; +import { Message } from 'discord.js'; +import { BushListener } from '../../lib/extensions/BushListener'; +import { Level } from '../../lib/models'; + +export default class LevelListener extends BushListener { + private levelCooldowns: Set = new Set(); + private blacklistedChannels = ['702456294874808330']; + constructor() { + super('level', { + emitter: 'commandHandler', + event: 'messageInvalid' // Using messageInvalid here so commands don't give xp + }); + } + async exec(message: Message): Promise { + if (message.author.bot) return; + if (message.util?.parsed?.command) return; + if (this.levelCooldowns.has(message.author.id)) return; + if (!this.client.config.dev && message.guild.id != '516977525906341928') + return; + if (this.blacklistedChannels.includes(message.channel.id)) return; + if (!['DEFAULT', 'REPLY'].includes(message.type)) return; //checks for join messages, slash commands, booster messages etc + const [user] = await Level.findOrBuild({ + where: { + id: message.author.id + }, + defaults: { + id: message.author.id + } + }); + const xpToGive = Level.genRandomizedXp(); + user.xp += xpToGive; + await user.save(); + await this.client.logger.verbose( + chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}` + ); + this.levelCooldowns.add(message.author.id); + setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000); + } +} diff --git a/src/listeners/message/levelListener.ts b/src/listeners/message/levelListener.ts deleted file mode 100644 index 3b23f36..0000000 --- a/src/listeners/message/levelListener.ts +++ /dev/null @@ -1,40 +0,0 @@ -import chalk from 'chalk'; -import { Message } from 'discord.js'; -import { BushListener } from '../../lib/extensions/BushListener'; -import { Level } from '../../lib/models'; - -export default class LevelListener extends BushListener { - private levelCooldowns: Set = new Set(); - private blacklistedChannels = ['702456294874808330']; - constructor() { - super('level', { - emitter: 'commandHandler', - event: 'messageInvalid' // Using messageInvalid here so commands don't give xp - }); - } - async exec(message: Message): Promise { - if (message.author.bot) return; - if (message.util?.parsed?.command) return; - if (this.levelCooldowns.has(message.author.id)) return; - if (!this.client.config.dev && message.guild.id != '516977525906341928') - return; - if (this.blacklistedChannels.includes(message.channel.id)) return; - if (!['DEFAULT', 'REPLY'].includes(message.type)) return; //checks for join messages, slash commands, booster messages etc - const [user] = await Level.findOrBuild({ - where: { - id: message.author.id - }, - defaults: { - id: message.author.id - } - }); - const xpToGive = Level.genRandomizedXp(); - user.xp += xpToGive; - await user.save(); - await this.client.logger.verbose( - chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}` - ); - this.levelCooldowns.add(message.author.id); - setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000); - } -} diff --git a/src/tasks/UnbanTask.ts b/src/tasks/UnbanTask.ts deleted file mode 100644 index 6b9d82e..0000000 --- a/src/tasks/UnbanTask.ts +++ /dev/null @@ -1,49 +0,0 @@ -import chalk from 'chalk'; -import { DiscordAPIError } from 'discord.js'; -import { Op } from 'sequelize'; -import { BushTask } from '../lib/extensions/BushTask'; -import { Ban } from '../lib/models'; - -export default class UnbanTask extends BushTask { - constructor() { - super('unban', { - delay: 30_000, // 1/2 min - runOnStart: true - }); - } - async exec(): Promise { - const rows = await Ban.findAll({ - where: { - [Op.and]: [ - { - expires: { - [Op.lt]: new Date() // Find all rows with an expiry date before now - } - } - ] - } - }); - this.client.logger.verbose( - chalk.cyan(`Queried bans, found ${rows.length} expired bans.`) - ); - for (const row of rows) { - const guild = this.client.guilds.cache.get(row.guild); - if (!guild) { - await row.destroy(); - continue; - } - try { - await guild.members.unban( - row.user, - `Unbanning user because tempban expired` - ); - } catch (e) { - if (e instanceof DiscordAPIError) { - // Member not banned, ignore - } else throw e; - } - await row.destroy(); - this.client.logger.verbose(chalk.cyan('Unbanned user')); - } - } -} diff --git a/src/tasks/unban.ts b/src/tasks/unban.ts new file mode 100644 index 0000000..6b9d82e --- /dev/null +++ b/src/tasks/unban.ts @@ -0,0 +1,49 @@ +import chalk from 'chalk'; +import { DiscordAPIError } from 'discord.js'; +import { Op } from 'sequelize'; +import { BushTask } from '../lib/extensions/BushTask'; +import { Ban } from '../lib/models'; + +export default class UnbanTask extends BushTask { + constructor() { + super('unban', { + delay: 30_000, // 1/2 min + runOnStart: true + }); + } + async exec(): Promise { + const rows = await Ban.findAll({ + where: { + [Op.and]: [ + { + expires: { + [Op.lt]: new Date() // Find all rows with an expiry date before now + } + } + ] + } + }); + this.client.logger.verbose( + chalk.cyan(`Queried bans, found ${rows.length} expired bans.`) + ); + for (const row of rows) { + const guild = this.client.guilds.cache.get(row.guild); + if (!guild) { + await row.destroy(); + continue; + } + try { + await guild.members.unban( + row.user, + `Unbanning user because tempban expired` + ); + } catch (e) { + if (e instanceof DiscordAPIError) { + // Member not banned, ignore + } else throw e; + } + await row.destroy(); + this.client.logger.verbose(chalk.cyan('Unbanned user')); + } + } +} -- cgit From f1cdbd260c6c2249fc0d765949d44d889bcaedc8 Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Fri, 28 May 2021 17:37:04 -0400 Subject: I can't read anything --- .vscode/extensions.json | 6 +---- package.json | 1 + src/commands/dev/eval.ts | 32 ++++++----------------- src/commands/dev/setLevel.ts | 13 +++------- src/commands/info/botInfo.ts | 19 +++++++------- src/commands/info/ping.ts | 26 +++++-------------- src/commands/info/pronouns.ts | 4 +-- src/commands/moderation/ban.ts | 40 +++++++---------------------- src/commands/moderation/kick.ts | 17 +++++------- src/commands/moderation/modlog.ts | 19 +++----------- src/commands/moderation/role.ts | 21 +++------------ src/commands/moderation/warn.ts | 12 +++------ src/commands/moulberry-bush/capePerms.ts | 12 +++------ src/commands/moulberry-bush/giveawayPing.ts | 14 ++++------ src/commands/moulberry-bush/level.ts | 9 +++---- src/commands/moulberry-bush/rule.ts | 5 +--- src/commands/server-config/prefix.ts | 11 +++----- src/lib/extensions/BushClient.ts | 8 ++---- src/lib/extensions/Util.ts | 27 +++++-------------- src/lib/models/Ban.ts | 5 +--- src/lib/models/Modlog.ts | 9 +------ src/lib/utils/Logger.ts | 4 +-- src/listeners/client/syncSlashCommands.ts | 5 +--- src/listeners/commands/commandBlocked.ts | 6 +---- src/listeners/commands/commandError.ts | 6 +---- src/listeners/commands/slashCommandError.ts | 6 +---- src/listeners/message/level.ts | 3 +-- src/tasks/unban.ts | 9 ++----- 28 files changed, 90 insertions(+), 259 deletions(-) (limited to 'src/commands/moulberry-bush/rule.ts') diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1fcdf78..5c4d931 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "arcanis.vscode-zipfs", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode" - ] + "recommendations": ["arcanis.vscode-zipfs", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] } diff --git a/package.json b/package.json index 75fd6c2..16d0436 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ ] }, "prettier": { + "printWidth": 100, "useTabs": true, "quoteProps": "consistent", "singleQuote": true, diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index 83e63f6..cb7a95c 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -70,32 +70,24 @@ export default class EvalCommand extends BushCommand { 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]' - ); + output = output.replace(new RegExp(this.client.token, 'g'), '[token omitted]'); output = clean(output); embed .setTitle('✅ Evaled code successfully') .addField( '📥 Input', code.length > 1012 - ? 'Too large to display. Hastebin: ' + - (await this.client.util.haste(code)) + ? '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)) + ? '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 }) - ) + .setFooter(message.author.username, message.author.displayAvatarURL({ dynamic: true })) .setTimestamp(); } catch (e) { embed @@ -103,25 +95,17 @@ export default class EvalCommand extends BushCommand { .addField( '📥 Input', code.length > 1012 - ? 'Too large to display. Hastebin: ' + - (await this.client.util.haste(code)) + ? '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)) + ? '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 }) - ) + .setFooter(message.author.username, message.author.displayAvatarURL({ dynamic: true })) .setTimestamp(); } if (!silent) { diff --git a/src/commands/dev/setLevel.ts b/src/commands/dev/setLevel.ts index d1701c5..e57e2f8 100644 --- a/src/commands/dev/setLevel.ts +++ b/src/commands/dev/setLevel.ts @@ -23,8 +23,7 @@ export default class SetLevelCommand extends BushCommand { type: 'user', prompt: { start: 'What user would you like to change the level of?', - retry: - 'Invalid user. What user would you like to change the level of?' + retry: 'Invalid user. What user would you like to change the level of?' } }, { @@ -68,10 +67,7 @@ export default class SetLevelCommand extends BushCommand { return `Successfully set level of <@${user.id}> to \`${level}\` (\`${levelEntry.xp}\` XP)`; } - async exec( - message: Message, - { user, level }: { user: User; level: number } - ): Promise { + async exec(message: Message, { user, level }: { user: User; level: number }): Promise { await message.util.send(await this.setLevel(user, level), { allowedMentions: AllowedMentions.none() }); @@ -79,10 +75,7 @@ export default class SetLevelCommand extends BushCommand { async execSlash( message: CommandInteraction, - { - user, - level - }: { user: SlashCommandOption; level: SlashCommandOption } + { user, level }: { user: SlashCommandOption; level: SlashCommandOption } ): Promise { await message.reply(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 779b318..0226edc 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -19,12 +19,14 @@ export default class BotInfoCommand extends BushCommand { const owners = (await this.client.util.mapIDs(this.client.ownerID)) .map((u) => u.tag) .join('\n'); - const currentCommit = ( - await this.client.util.shell('git rev-parse HEAD') - ).stdout.replace('\n', ''); - const repoUrl = ( - await this.client.util.shell('git remote get-url origin') - ).stdout.replace('\n', ''); + const currentCommit = (await this.client.util.shell('git rev-parse HEAD')).stdout.replace( + '\n', + '' + ); + const repoUrl = (await this.client.util.shell('git remote get-url origin')).stdout.replace( + '\n', + '' + ); const embed = new MessageEmbed() .setTitle('Bot Info:') .addFields([ @@ -46,10 +48,7 @@ export default class BotInfoCommand extends BushCommand { }, { name: 'Current commit', - value: `[${currentCommit.substring( - 0, - 7 - )}](${repoUrl}/commit/${currentCommit})` + value: `[${currentCommit.substring(0, 7)}](${repoUrl}/commit/${currentCommit})` } ]) .setTimestamp(); diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index e0bbfc7..9468722 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -21,20 +21,13 @@ export default class PingCommand extends BushCommand { const timestamp: number = message.editedTimestamp ? message.editedTimestamp : message.createdTimestamp; - const botLatency = `\`\`\`\n ${Math.floor( - sentMessage.createdTimestamp - timestamp - )}ms \`\`\``; - const apiLatency = `\`\`\`\n ${Math.round( - message.client.ws.ping - )}ms \`\`\``; + const botLatency = `\`\`\`\n ${Math.floor(sentMessage.createdTimestamp - timestamp)}ms \`\`\``; + const apiLatency = `\`\`\`\n ${Math.round(message.client.ws.ping)}ms \`\`\``; const embed = new MessageEmbed() .setTitle('Pong! 🏓') .addField('Bot Latency', botLatency, true) .addField('API Latency', apiLatency, true) - .setFooter( - message.author.username, - message.author.displayAvatarURL({ dynamic: true }) - ) + .setFooter(message.author.username, message.author.displayAvatarURL({ dynamic: true })) .setTimestamp(); await sentMessage.edit({ content: null, @@ -45,21 +38,14 @@ export default class PingCommand extends BushCommand { public async execSlash(message: CommandInteraction): Promise { const timestamp1 = message.createdTimestamp; await message.reply('Pong!'); - const timestamp2 = await message - .fetchReply() - .then((m) => (m as Message).createdTimestamp); - const botLatency = `\`\`\`\n ${Math.floor( - timestamp2 - timestamp1 - )}ms \`\`\``; + const timestamp2 = await message.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.user.username, message.user.displayAvatarURL({ dynamic: true })) .setTimestamp(); await message.editReply({ content: null, diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index 2c1d5f2..ca70ff0 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -66,9 +66,7 @@ export default class PronounsCommand extends BushCommand { ): Promise { try { const apiRes: { pronouns: pronounsType } = await got - .get( - `https://pronoundb.org/api/v1/lookup?platform=discord&id=${user.id}` - ) + .get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${user.id}`) .json(); if (message instanceof Message) { message.reply( diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index feb020b..05c26e0 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -45,13 +45,9 @@ export default class BanCommand extends BushCommand { clientPermissions: ['BAN_MEMBERS'], userPermissions: ['BAN_MEMBERS'], description: { - content: - 'Ban a member and log it in modlogs (with optional time to unban)', + content: 'Ban a member and log it in modlogs (with optional time to unban)', usage: 'ban [--time]', - examples: [ - 'ban @Tyman being cool', - 'ban @Tyman being cool --time 7days' - ] + examples: ['ban @Tyman being cool', 'ban @Tyman being cool --time 7days'] }, slashCommandOptions: [ { @@ -69,8 +65,7 @@ export default class BanCommand extends BushCommand { { type: ApplicationCommandOptionType.STRING, name: 'time', - description: - 'The time the user should be banned for (default permanent)', + description: 'The time the user should be banned for (default permanent)', required: false } ] @@ -119,10 +114,7 @@ export default class BanCommand extends BushCommand { reason, type: ModlogType.TEMPBAN, duration: duration.asMilliseconds(), - moderator: - message instanceof CommandInteraction - ? message.user.id - : message.author.id + moderator: message instanceof CommandInteraction ? message.user.id : message.author.id }); banEntry = Ban.build({ user: user.id, @@ -137,10 +129,7 @@ export default class BanCommand extends BushCommand { guild: message.guild.id, reason, type: ModlogType.BAN, - moderator: - message instanceof CommandInteraction - ? message.user.id - : message.author.id + moderator: message instanceof CommandInteraction ? message.user.id : message.author.id }); banEntry = Ban.build({ user: user.id, @@ -159,9 +148,7 @@ export default class BanCommand extends BushCommand { try { await user.send( `You were banned in ${message.guild.name} ${ - translatedTime.length >= 1 - ? `for ${translatedTime.join(', ')}` - : 'permanently' + translatedTime.length >= 1 ? `for ${translatedTime.join(', ')}` : 'permanently' } with reason \`${reason || 'No reason given'}\`` ); } catch (e) { @@ -169,15 +156,11 @@ export default class BanCommand extends BushCommand { } await message.guild.members.ban(user, { reason: `Banned by ${ - message instanceof CommandInteraction - ? message.user.tag - : message.author.tag + message instanceof CommandInteraction ? message.user.tag : message.author.tag } with ${reason ? `reason ${reason}` : 'no reason'}` }); yield `Banned <@!${user.id}> ${ - translatedTime.length >= 1 - ? `for ${translatedTime.join(', ')}` - : 'permanently' + translatedTime.length >= 1 ? `for ${translatedTime.join(', ')}` : 'permanently' } with reason \`${reason || 'No reason given'}\``; } catch { yield 'Error banning :/'; @@ -190,12 +173,7 @@ export default class BanCommand extends BushCommand { message: Message, { user, reason, time }: { user: User; reason?: string; time?: string } ): Promise { - for await (const response of this.genResponses( - message, - user, - reason, - time - )) { + for await (const response of this.genResponses(message, user, reason, time)) { await message.util.send(response); } } diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 58cf52e..f3ee44c 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -65,8 +65,7 @@ export default class KickCommand extends BushCommand { modlogEnry = Modlog.build({ user: user.id, guild: message.guild.id, - moderator: - message instanceof Message ? message.author.id : message.user.id, + moderator: message instanceof Message ? message.author.id : message.user.id, type: ModlogType.KICK, reason }); @@ -78,27 +77,23 @@ export default class KickCommand extends BushCommand { } try { await user.send( - `You were kicked in ${message.guild.name} with reason \`${ - reason || 'No reason given' - }\`` + `You were kicked in ${message.guild.name} with reason \`${reason || 'No reason given'}\`` ); } catch (e) { yield 'Error sending message to user'; } try { await user.kick( - `Kicked by ${ - message instanceof Message ? message.author.tag : message.user.tag - } with ${reason ? `reason ${reason}` : 'no reason'}` + `Kicked by ${message instanceof Message ? message.author.tag : message.user.tag} with ${ + reason ? `reason ${reason}` : 'no reason' + }` ); } catch { yield 'Error kicking :/'; await modlogEnry.destroy(); return; } - yield `Kicked <@!${user.id}> with reason \`${ - reason || 'No reason given' - }\``; + yield `Kicked <@!${user.id}> with reason \`${reason || 'No reason given'}\``; } async exec( diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 2df4745..c4eb46a 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -53,10 +53,7 @@ export default class ModlogCommand extends BushCommand { return { search, page }; } } - async exec( - message: Message, - { search, page }: { search: string; page: number } - ): Promise { + async exec(message: Message, { search, page }: { search: string; page: number }): Promise { const foundUser = await this.client.util.resolveUserAsync(search); if (foundUser) { const logs = await Modlog.findAll({ @@ -73,11 +70,7 @@ export default class ModlogCommand extends BushCommand { Type: ${log.type.toLowerCase()} User: <@!${log.user}> (${log.user}) Moderator: <@!${log.moderator}> (${log.moderator}) - Duration: ${ - log.duration - ? moment.duration(log.duration, 'milliseconds').humanize() - : 'N/A' - } + Duration: ${log.duration ? moment.duration(log.duration, 'milliseconds').humanize() : 'N/A'} Reason: ${log.reason || 'None given'} ${this.client.util.ordinal(logs.indexOf(log) + 1)} action `); @@ -87,9 +80,7 @@ export default class ModlogCommand extends BushCommand { (e, i) => new MessageEmbed({ title: `Modlogs page ${i + 1}`, - description: e.join( - '\n-------------------------------------------------------\n' - ), + description: e.join('\n-------------------------------------------------------\n'), footer: { text: `Page ${i + 1}/${chunked.length}` } @@ -120,9 +111,7 @@ export default class ModlogCommand extends BushCommand { { name: 'Duration', value: `${ - entry.duration - ? moment.duration(entry.duration, 'milliseconds').humanize() - : 'N/A' + entry.duration ? moment.duration(entry.duration, 'milliseconds').humanize() : 'N/A' }`, inline: true }, diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index d5948c3..f03e0ad 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -22,13 +22,7 @@ export default class RoleCommand extends BushCommand { 'No Links': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'No Bots': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'No VC': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'No Giveaways': [ - '*', - 'Admin Perms', - 'Sr. Moderator', - 'Moderator', - 'Helper' - ], + 'No Giveaways': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator', 'Helper'], 'No Support': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'Giveaway Donor': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'Giveaway (200m)': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], @@ -95,9 +89,7 @@ export default class RoleCommand extends BushCommand { !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 - ); + 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.`, @@ -107,14 +99,9 @@ export default class RoleCommand extends BushCommand { ); } const allowedRoles = this.roleWhitelist[mappedRole.name].map((r) => { - return this.client.util.moulberryBushRoleMap.find((m) => m.name === r) - .id; + return this.client.util.moulberryBushRoleMap.find((m) => m.name === r).id; }); - if ( - !message.member.roles.cache.some((role) => - allowedRoles.includes(role.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.`, { diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index a61eef3..5651830 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -48,21 +48,15 @@ export default class WarnCommand extends BushCommand { }); await entry.save(); } catch (e) { - await message.util.send( - 'Error saving to database, please contact the developers' - ); + await message.util.send('Error saving to database, please contact the developers'); return; } try { - await member.send( - `You were warned in ${message.guild.name} for reason "${reason}".` - ); + await member.send(`You were warned in ${message.guild.name} for reason "${reason}".`); } catch (e) { await message.util.send('Error messaging user, warning still saved.'); return; } - await message.util.send( - `${member.user.tag} was warned for reason "${reason}".` - ); + await message.util.send(`${member.user.tag} was warned for reason "${reason}".`); } } diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts index fdb5caf..92ccd73 100644 --- a/src/commands/moulberry-bush/capePerms.ts +++ b/src/commands/moulberry-bush/capePerms.ts @@ -70,8 +70,7 @@ export default class CapePermissionsCommand extends BushCommand { { type: ApplicationCommandOptionType.STRING, name: 'user', - description: - 'The username of the player to see the cape permissions of', + description: 'The username of the player to see the cape permissions of', required: true } ] @@ -86,9 +85,7 @@ export default class CapePermissionsCommand extends BushCommand { } try { - capeperms = await got - .get('http://moulberry.codes/permscapes.json') - .json(); + capeperms = await got.get('http://moulberry.codes/permscapes.json').json(); } catch (error) { capeperms = null; } @@ -110,10 +107,7 @@ export default class CapePermissionsCommand extends BushCommand { } } } - public async exec( - message: Message, - { user }: { user: string } - ): Promise { + public async exec(message: Message, { user }: { user: string }): Promise { await message.reply(await this.getResponse(user)); } diff --git a/src/commands/moulberry-bush/giveawayPing.ts b/src/commands/moulberry-bush/giveawayPing.ts index e6fdd9a..06cb254 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -36,21 +36,17 @@ export default class GiveawayPingCommand extends BushCommand { return message.reply( "<:error:837123021016924261> This command may only be run in Moulberry's Bush." ); - if ( - !['767782084981817344', '833855738501267456'].includes(message.channel.id) - ) + if (!['767782084981817344', '833855738501267456'].includes(message.channel.id)) return message.reply( '<:error:837123021016924261> This command may only be run in giveaway channels.' ); await message.delete().catch(() => undefined); - const webhooks = await ( - message.channel as TextChannel | NewsChannel - ).fetchWebhooks(); + const webhooks = await (message.channel as TextChannel | NewsChannel).fetchWebhooks(); let webhookClient: WebhookClient; if (webhooks.size < 1) { - const webhook = await ( - message.channel as TextChannel | NewsChannel - ).createWebhook('Giveaway ping webhook'); + const webhook = await (message.channel as TextChannel | NewsChannel).createWebhook( + 'Giveaway ping webhook' + ); webhookClient = new WebhookClient(webhook.id, webhook.token); } else { const webhook = webhooks.first(); diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/moulberry-bush/level.ts index decac8a..74d6cf4 100644 --- a/src/commands/moulberry-bush/level.ts +++ b/src/commands/moulberry-bush/level.ts @@ -22,8 +22,7 @@ export default class LevelCommand extends BushCommand { type: 'user', prompt: { start: 'What user would you like to see the level of?', - retry: - 'Invalid user. What user would you like to see the level of?', + retry: 'Invalid user. What user would you like to see the level of?', optional: true } } @@ -42,9 +41,9 @@ export default class LevelCommand extends BushCommand { private async getResponse(user: User): Promise { const userLevelRow = await Level.findByPk(user.id); if (userLevelRow) { - return `${user ? `${user.tag}'s` : 'Your'} level is ${ - userLevelRow.level - } (${userLevelRow.xp} XP)`; + return `${user ? `${user.tag}'s` : 'Your'} level is ${userLevelRow.level} (${ + userLevelRow.xp + } XP)`; } else { return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; } diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index 69dcb6e..6984603 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -171,10 +171,7 @@ export default class RuleCommand extends BushCommand { public async execSlash( message: CommandInteraction, - { - rule, - user - }: { rule?: SlashCommandOption; user?: SlashCommandOption } + { rule, user }: { rule?: SlashCommandOption; user?: SlashCommandOption } ): Promise { const response = this.getResponse(message, rule?.value, user?.user); if (Array.isArray(response)) { diff --git a/src/commands/server-config/prefix.ts b/src/commands/server-config/prefix.ts index 899ecc9..f02e693 100644 --- a/src/commands/server-config/prefix.ts +++ b/src/commands/server-config/prefix.ts @@ -16,8 +16,7 @@ export default class PrefixCommand extends BushCommand { ], userPermissions: ['MANAGE_GUILD'], description: { - content: - 'Set the prefix of the current server (resets to default if prefix is not given)', + content: 'Set the prefix of the current server (resets to default if prefix is not given)', usage: 'prefix [prefix]', examples: ['prefix', 'prefix +'] }, @@ -54,9 +53,7 @@ export default class PrefixCommand extends BushCommand { if (prefix) { await message.util.send(`Sucessfully set prefix to \`${prefix}\``); } else { - await message.util.send( - `Sucessfully reset prefix to \`${this.client.config.prefix}\`` - ); + await message.util.send(`Sucessfully reset prefix to \`${this.client.config.prefix}\``); } } @@ -68,9 +65,7 @@ export default class PrefixCommand extends BushCommand { if (prefix) { await message.reply(`Sucessfully set prefix to \`${prefix.value}\``); } else { - await message.reply( - `Sucessfully reset prefix to \`${this.client.config.prefix}\`` - ); + await message.reply(`Sucessfully reset prefix to \`${this.client.config.prefix}\``); } } } diff --git a/src/lib/extensions/BushClient.ts b/src/lib/extensions/BushClient.ts index 8a8ae91..99d0dd3 100644 --- a/src/lib/extensions/BushClient.ts +++ b/src/lib/extensions/BushClient.ts @@ -122,14 +122,10 @@ export class BushClient extends AkairoClient { for (const loader of Object.keys(loaders)) { try { loaders[loader].loadAll(); - this.logger.log( - chalk.green('Successfully loaded ' + chalk.cyan(loader) + '.') - ); + this.logger.log(chalk.green('Successfully loaded ' + chalk.cyan(loader) + '.')); } catch (e) { console.error( - chalk.red( - 'Unable to load loader ' + chalk.cyan(loader) + ' with error ' + e - ) + chalk.red('Unable to load loader ' + chalk.cyan(loader) + ' with error ' + e) ); } } diff --git a/src/lib/extensions/Util.ts b/src/lib/extensions/Util.ts index 7c1d4b2..7bedfc1 100644 --- a/src/lib/extensions/Util.ts +++ b/src/lib/extensions/Util.ts @@ -125,9 +125,7 @@ export class Util extends ClientUtil { public async haste(content: string): Promise { for (const url of this.hasteURLs) { try { - const res: hastebinRes = await got - .post(`${url}/documents`, { body: content }) - .json(); + const res: hastebinRes = await got.post(`${url}/documents`, { body: content }).json(); return `${url}/${res.key}`; } catch (e) { // pass @@ -220,10 +218,7 @@ export class Util extends ClientUtil { /** * A simple utility to create and embed with the needed style for the bot */ - public createEmbed( - color?: string, - author?: User | GuildMember - ): MessageEmbed { + public createEmbed(color?: string, author?: User | GuildMember): MessageEmbed { if (author instanceof GuildMember) { author = author.user; // Convert to User if GuildMember } @@ -255,15 +250,12 @@ export class Util extends ClientUtil { : await fetchedGuild.commands.fetch(); for (const [, registeredCommand] of registered) { if ( - !this.client.commandHandler.modules.find( - (cmd) => cmd.id == registeredCommand.name - )?.execSlash || + !this.client.commandHandler.modules.find((cmd) => cmd.id == registeredCommand.name) + ?.execSlash || force ) { guild === undefined - ? await this.client.application.commands.delete( - registeredCommand.id - ) + ? await this.client.application.commands.delete(registeredCommand.id) : await fetchedGuild.commands.delete(registeredCommand.id); this.client.logger.verbose( chalk`{red Deleted slash command ${registeredCommand.name}${ @@ -286,10 +278,7 @@ export class Util extends ClientUtil { if (found?.id && !force) { if (slashdata.description !== found.description) { guild === undefined - ? await this.client.application.commands.edit( - found.id, - slashdata - ) + ? await this.client.application.commands.edit(found.id, slashdata) : fetchedGuild.commands.edit(found.id, slashdata); this.client.logger.verbose( chalk`{yellow Edited slash command ${BushCommand.id}${ @@ -312,9 +301,7 @@ export class Util extends ClientUtil { return this.client.logger.log( chalk.green( - `Slash commands registered${ - guild !== undefined ? ` in guild ${fetchedGuild.name}` : '' - }` + `Slash commands registered${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}` ) ); } catch (e) { diff --git a/src/lib/models/Ban.ts b/src/lib/models/Ban.ts index 3ce9c06..75e38ab 100644 --- a/src/lib/models/Ban.ts +++ b/src/lib/models/Ban.ts @@ -20,10 +20,7 @@ export interface BanModelCreationAttributes { modlog: string; } -export class Ban - extends BaseModel - implements BanModel -{ +export class Ban extends BaseModel implements BanModel { /** * The ID of this ban (no real use just for a primary key) */ diff --git a/src/lib/models/Modlog.ts b/src/lib/models/Modlog.ts index 7efeeed..52a13b2 100644 --- a/src/lib/models/Modlog.ts +++ b/src/lib/models/Modlog.ts @@ -54,14 +54,7 @@ export class Modlog defaultValue: uuidv4 }, type: { - type: new DataTypes.ENUM( - 'BAN', - 'TEMPBAN', - 'MUTE', - 'TEMPMUTE', - 'KICK', - 'WARN' - ), + type: new DataTypes.ENUM('BAN', 'TEMPBAN', 'MUTE', 'TEMPMUTE', 'KICK', 'WARN'), allowNull: false }, user: { diff --git a/src/lib/utils/Logger.ts b/src/lib/utils/Logger.ts index 604b45e..b0f7da5 100644 --- a/src/lib/utils/Logger.ts +++ b/src/lib/utils/Logger.ts @@ -15,9 +15,7 @@ export class Logger { ); } public getChannel(channel: 'log' | 'error' | 'dm'): Promise { - return this.client.channels.fetch( - this.client.config.channels[channel] - ) as Promise; + return this.client.channels.fetch(this.client.config.channels[channel]) as Promise; } public async log(message: string, sendChannel = false): Promise { console.log(chalk`{bgCyan LOG} ` + message); diff --git a/src/listeners/client/syncSlashCommands.ts b/src/listeners/client/syncSlashCommands.ts index 7835136..69b91bc 100644 --- a/src/listeners/client/syncSlashCommands.ts +++ b/src/listeners/client/syncSlashCommands.ts @@ -10,10 +10,7 @@ export default class SyncSlashCommandsListener extends BushListener { async exec(): Promise { if (this.client.config.dev && this.client.config.devGuild) { // Use guild slash commands for instant registration in dev - await this.client.util.syncSlashCommands( - false, - this.client.config.devGuild - ); + await this.client.util.syncSlashCommands(false, this.client.config.devGuild); } else { // Use global in production await this.client.util.syncSlashCommands(); diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index dee1319..a6a7e7e 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -10,11 +10,7 @@ export default class CommandBlockedListener extends BushListener { }); } - public async exec( - message: Message, - command: Command, - reason: string - ): Promise { + public async exec(message: Message, command: Command, reason: string): Promise { switch (reason) { case 'owner': { await message.util.send( diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index 518545a..c5d6a74 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -12,11 +12,7 @@ export default class CommandErrorListener extends BushListener { event: 'error' }); } - async exec( - error: Error, - message: Message, - command?: BushCommand - ): Promise { + async exec(error: Error, message: Message, command?: BushCommand): Promise { const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers const errorDevEmbed = this.client.util .createEmbed(this.client.util.colors.error) diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts index a2b7bb7..32ccd90 100644 --- a/src/listeners/commands/slashCommandError.ts +++ b/src/listeners/commands/slashCommandError.ts @@ -12,11 +12,7 @@ export default class SlashCommandErrorListener extends BushListener { event: 'slashError' }); } - async exec( - error: Error, - message: CommandInteraction, - command: BushCommand - ): Promise { + async exec(error: Error, message: CommandInteraction, command: BushCommand): Promise { const errorNumber = Math.floor(Math.random() * 6969696969) + 69; // hehe funy numbers const errorDevEmbed = this.client.util .createEmbed(this.client.util.colors.error) diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts index 3b23f36..c9d5522 100644 --- a/src/listeners/message/level.ts +++ b/src/listeners/message/level.ts @@ -16,8 +16,7 @@ export default class LevelListener extends BushListener { if (message.author.bot) return; if (message.util?.parsed?.command) return; if (this.levelCooldowns.has(message.author.id)) return; - if (!this.client.config.dev && message.guild.id != '516977525906341928') - return; + if (!this.client.config.dev && message.guild.id != '516977525906341928') return; if (this.blacklistedChannels.includes(message.channel.id)) return; if (!['DEFAULT', 'REPLY'].includes(message.type)) return; //checks for join messages, slash commands, booster messages etc const [user] = await Level.findOrBuild({ diff --git a/src/tasks/unban.ts b/src/tasks/unban.ts index 6b9d82e..564a2a3 100644 --- a/src/tasks/unban.ts +++ b/src/tasks/unban.ts @@ -23,9 +23,7 @@ export default class UnbanTask extends BushTask { ] } }); - this.client.logger.verbose( - chalk.cyan(`Queried bans, found ${rows.length} expired bans.`) - ); + this.client.logger.verbose(chalk.cyan(`Queried bans, found ${rows.length} expired bans.`)); for (const row of rows) { const guild = this.client.guilds.cache.get(row.guild); if (!guild) { @@ -33,10 +31,7 @@ export default class UnbanTask extends BushTask { continue; } try { - await guild.members.unban( - row.user, - `Unbanning user because tempban expired` - ); + await guild.members.unban(row.user, `Unbanning user because tempban expired`); } catch (e) { if (e instanceof DiscordAPIError) { // Member not banned, ignore -- cgit From 25cf269e2e793de5fefb9aa3f19fb167168e06c6 Mon Sep 17 00:00:00 2001 From: IRONM00N <64110067+IRONM00N@users.noreply.github.com> Date: Fri, 28 May 2021 20:13:49 -0400 Subject: stuff --- package.json | 2 +- src/commands/dev/eval.ts | 31 ++-------- src/commands/dev/setLevel.ts | 5 +- src/commands/info/botInfo.ts | 18 ++---- src/commands/info/help.ts | 26 ++------ src/commands/info/ping.ts | 4 +- src/commands/info/pronouns.ts | 23 ++----- src/commands/moderation/ban.ts | 45 ++++---------- src/commands/moderation/kick.ts | 26 ++------ src/commands/moderation/modlog.ts | 4 +- src/commands/moderation/role.ts | 85 +++++++------------------- src/commands/moderation/warn.ts | 5 +- src/commands/moulberry-bush/capePerms.ts | 16 ++--- src/commands/moulberry-bush/giveawayPing.ts | 22 ++----- src/commands/moulberry-bush/level.ts | 9 +-- src/commands/moulberry-bush/rule.ts | 60 +++++------------- src/commands/server-config/prefix.ts | 5 +- src/config/example-options.ts | 7 ++- src/lib/extensions/BushClient.ts | 29 +++------ src/lib/extensions/Util.ts | 71 ++++------------------ src/lib/models/Guild.ts | 5 +- src/lib/models/Modlog.ts | 5 +- src/lib/utils/AllowedMentions.ts | 6 +- src/lib/utils/Console.ts | 94 +++++++++++++++++++++++++++++ src/listeners/client/ready.ts | 5 +- src/listeners/commands/commandBlocked.ts | 4 +- src/listeners/commands/commandError.ts | 4 +- src/listeners/commands/commandStarted.ts | 8 +-- src/listeners/commands/slashCommandError.ts | 4 +- src/listeners/message/level.ts | 4 +- tsconfig.json | 9 +-- 31 files changed, 221 insertions(+), 420 deletions(-) create mode 100644 src/lib/utils/Console.ts (limited to 'src/commands/moulberry-bush/rule.ts') diff --git a/package.json b/package.json index 16d0436..8e24daf 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ ] }, "prettier": { - "printWidth": 100, + "printWidth": 170, "useTabs": true, "quoteProps": "consistent", "singleQuote": true, diff --git a/src/commands/dev/eval.ts b/src/commands/dev/eval.ts index cb7a95c..cfe75cc 100644 --- a/src/commands/dev/eval.ts +++ b/src/commands/dev/eval.ts @@ -5,10 +5,7 @@ import { inspect, promisify } from 'util'; import { exec } from 'child_process'; const clean = (text) => { - if (typeof text === 'string') - return text - .replace(/`/g, '`' + String.fromCharCode(8203)) - .replace(/@/g, '@' + String.fromCharCode(8203)); + if (typeof text === 'string') return text.replace(/`/g, '`' + String.fromCharCode(8203)).replace(/@/g, '@' + String.fromCharCode(8203)); else return text; }; @@ -50,10 +47,7 @@ export default class EvalCommand extends BushCommand { }); } - public async exec( - message: Message, - { depth, code, silent }: { depth: number; code: string; silent: boolean } - ): Promise { + public async exec(message: Message, { depth, code, silent }: { depth: number; code: string; silent: boolean }): Promise { const embed: MessageEmbed = new MessageEmbed(); try { @@ -74,30 +68,15 @@ export default class EvalCommand extends BushCommand { output = clean(output); 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 + '```' - ) + .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 })) .setTimestamp(); } catch (e) { 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('📥 Input', code.length > 1012 ? 'Too large to display. Hastebin: ' + (await this.client.util.haste(code)) : '```js\n' + code + '```') .addField( '📤 Output', e.length > 1012 diff --git a/src/commands/dev/setLevel.ts b/src/commands/dev/setLevel.ts index e57e2f8..3a1bc85 100644 --- a/src/commands/dev/setLevel.ts +++ b/src/commands/dev/setLevel.ts @@ -73,10 +73,7 @@ export default class SetLevelCommand extends BushCommand { }); } - async execSlash( - message: CommandInteraction, - { user, level }: { user: SlashCommandOption; level: SlashCommandOption } - ): Promise { + async execSlash(message: CommandInteraction, { user, level }: { user: SlashCommandOption; level: SlashCommandOption }): Promise { await message.reply(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 0226edc..ebbd0c9 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -16,17 +16,9 @@ export default class BotInfoCommand extends BushCommand { } private async generateEmbed(): Promise { - const owners = (await this.client.util.mapIDs(this.client.ownerID)) - .map((u) => u.tag) - .join('\n'); - const currentCommit = (await this.client.util.shell('git rev-parse HEAD')).stdout.replace( - '\n', - '' - ); - const repoUrl = (await this.client.util.shell('git remote get-url origin')).stdout.replace( - '\n', - '' - ); + const owners = (await this.client.util.mapIDs(this.client.ownerID)).map((u) => u.tag).join('\n'); + const currentCommit = (await this.client.util.shell('git rev-parse HEAD')).stdout.replace('\n', ''); + const repoUrl = (await this.client.util.shell('git remote get-url origin')).stdout.replace('\n', ''); const embed = new MessageEmbed() .setTitle('Bot Info:') .addFields([ @@ -37,9 +29,7 @@ export default class BotInfoCommand extends BushCommand { }, { name: 'Uptime', - value: this.client.util.capitalize( - duration(this.client.uptime, 'milliseconds').humanize() - ) + value: this.client.util.capitalize(duration(this.client.uptime, 'milliseconds').humanize()) }, { name: 'User count', diff --git a/src/commands/info/help.ts b/src/commands/info/help.ts index 7f91cdf..7f6c8f4 100644 --- a/src/commands/info/help.ts +++ b/src/commands/info/help.ts @@ -43,9 +43,7 @@ export default class HelpCommand extends BushCommand { For additional info on a command, type \`${prefix}help \` ` ) - .setFooter( - `For more information about a command use "${this.client.config.prefix}help "` - ) + .setFooter(`For more information about a command use "${this.client.config.prefix}help "`) .setTimestamp(); for (const category of this.handler.categories.values()) { embed.addField( @@ -61,17 +59,10 @@ export default class HelpCommand extends BushCommand { const embed = new MessageEmbed() .setColor([155, 200, 200]) .setTitle(`\`${command.description.usage ? command.description.usage : ''}\``) - .addField( - 'Description', - `${command.description.content ? command.description.content : ''} ${ - command.ownerOnly ? '\n__Owner Only__' : '' - }` - ); + .addField('Description', `${command.description.content ? command.description.content : ''} ${command.ownerOnly ? '\n__Owner Only__' : ''}`); - if (command.aliases.length > 1) - embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true); - if (command.description.examples && command.description.examples.length) - embed.addField('Examples', `\`${command.description.examples.join('`\n`')}\``, true); + if (command.aliases.length > 1) embed.addField('Aliases', `\`${command.aliases.join('` `')}\``, true); + if (command.description.examples && command.description.examples.length) embed.addField('Examples', `\`${command.description.examples.join('`\n`')}\``, true); return embed; } } @@ -80,14 +71,9 @@ export default class HelpCommand extends BushCommand { await message.util.send(this.generateEmbed(command)); } - public async execSlash( - message: CommandInteraction, - { command }: { command: SlashCommandOption } - ): Promise { + public async execSlash(message: CommandInteraction, { command }: { command: SlashCommandOption }): Promise { if (command) { - await message.reply( - this.generateEmbed(this.handler.findCommand(command.value) as BushCommand) - ); + await message.reply(this.generateEmbed(this.handler.findCommand(command.value) as BushCommand)); } else { await message.reply(this.generateEmbed()); } diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index 9468722..b130e6d 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -18,9 +18,7 @@ export default class PingCommand extends BushCommand { public async exec(message: Message): Promise { const sentMessage = await message.util.send('Pong!'); - const timestamp: number = message.editedTimestamp - ? message.editedTimestamp - : message.createdTimestamp; + const timestamp: number = message.editedTimestamp ? message.editedTimestamp : message.createdTimestamp; const botLatency = `\`\`\`\n ${Math.floor(sentMessage.createdTimestamp - timestamp)}ms \`\`\``; const apiLatency = `\`\`\`\n ${Math.round(message.client.ws.ping)}ms \`\`\``; const embed = new MessageEmbed() diff --git a/src/commands/info/pronouns.ts b/src/commands/info/pronouns.ts index ca70ff0..c53c542 100644 --- a/src/commands/info/pronouns.ts +++ b/src/commands/info/pronouns.ts @@ -59,15 +59,9 @@ export default class PronounsCommand extends BushCommand { slashEmphemeral: true // I'll add dynamic checking to this later }); } - async sendResponse( - message: Message | CommandInteraction, - user: User, - author: boolean - ): Promise { + async sendResponse(message: Message | CommandInteraction, user: User, author: boolean): Promise { try { - const apiRes: { pronouns: pronounsType } = await got - .get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${user.id}`) - .json(); + const apiRes: { pronouns: pronounsType } = await got.get(`https://pronoundb.org/api/v1/lookup?platform=discord&id=${user.id}`).json(); if (message instanceof Message) { message.reply( new MessageEmbed({ @@ -94,13 +88,9 @@ export default class PronounsCommand extends BushCommand { } catch (e) { if (e instanceof HTTPError && e.response.statusCode === 404) { if (author) { - await message.reply( - 'You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.' - ); + await message.reply('You do not appear to have any pronouns set. Please go to https://pronoundb.org/ and set your pronouns.'); } else { - await message.reply( - `${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.` - ); + await message.reply(`${user.tag} does not appear to have any pronouns set. Please tell them to go to https://pronoundb.org/ and set their pronouns.`); } } else throw e; } @@ -109,10 +99,7 @@ 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 } - ): Promise { + async execSlash(message: CommandInteraction, { user }: { user?: SlashCommandOption }): Promise { const u = user?.user || message.user; await this.sendResponse(message, u, u.id === message.user.id); } diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 05c26e0..e59a528 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -15,8 +15,7 @@ const durationAliases: Record = { minutes: ['m', 'min', 'mins', 'minutes', 'minute'], months: ['mo', 'month', 'months'] }; -const durationRegex = - /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; +const durationRegex = /(?:(\d+)(d(?:ays?)?|h(?:ours?|rs?)?|m(?:inutes?|ins?)?|mo(?:nths?)?|w(?:eeks?|ks?)?)(?: |$))/g; export default class BanCommand extends BushCommand { constructor() { @@ -71,12 +70,7 @@ export default class BanCommand extends BushCommand { ] }); } - async *genResponses( - message: Message | CommandInteraction, - user: User, - reason?: string, - time?: string - ): AsyncIterable { + async *genResponses(message: Message | CommandInteraction, user: User, reason?: string, time?: string): AsyncIterable { const duration = moment.duration(); let modlogEnry: Modlog; let banEntry: Ban; @@ -99,14 +93,9 @@ export default class BanCommand extends BushCommand { return; } for (const part of parsed) { - const translated = Object.keys(durationAliases).find((k) => - durationAliases[k].includes(part[2]) - ); + const translated = Object.keys(durationAliases).find((k) => durationAliases[k].includes(part[2])); translatedTime.push(part[1] + ' ' + translated); - duration.add( - Number(part[1]), - translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes' - ); + duration.add(Number(part[1]), translated as 'weeks' | 'days' | 'hours' | 'months' | 'minutes'); } modlogEnry = Modlog.build({ user: user.id, @@ -147,21 +136,17 @@ export default class BanCommand extends BushCommand { } try { await user.send( - `You were banned in ${message.guild.name} ${ - translatedTime.length >= 1 ? `for ${translatedTime.join(', ')}` : 'permanently' - } with reason \`${reason || 'No reason given'}\`` + `You were banned in ${message.guild.name} ${translatedTime.length >= 1 ? `for ${translatedTime.join(', ')}` : 'permanently'} with reason \`${ + reason || 'No reason given' + }\`` ); } catch (e) { yield 'Error sending message to user'; } await message.guild.members.ban(user, { - reason: `Banned by ${ - message instanceof CommandInteraction ? message.user.tag : message.author.tag - } with ${reason ? `reason ${reason}` : 'no reason'}` + reason: `Banned by ${message instanceof CommandInteraction ? message.user.tag : message.author.tag} with ${reason ? `reason ${reason}` : 'no reason'}` }); - yield `Banned <@!${user.id}> ${ - translatedTime.length >= 1 ? `for ${translatedTime.join(', ')}` : 'permanently' - } with reason \`${reason || 'No reason given'}\``; + yield `Banned <@!${user.id}> ${translatedTime.length >= 1 ? `for ${translatedTime.join(', ')}` : 'permanently'} with reason \`${reason || 'No reason given'}\``; } catch { yield 'Error banning :/'; await banEntry.destroy(); @@ -169,10 +154,7 @@ export default class BanCommand extends BushCommand { return; } } - async exec( - message: Message, - { user, reason, time }: { user: User; reason?: string; time?: string } - ): Promise { + async exec(message: Message, { user, reason, time }: { user: User; reason?: string; time?: string }): Promise { for await (const response of this.genResponses(message, user, reason, time)) { await message.util.send(response); } @@ -190,12 +172,7 @@ export default class BanCommand extends BushCommand { time: SlashCommandOption; } ): Promise { - for await (const response of this.genResponses( - message, - user.user, - reason?.value, - time?.value - )) { + for await (const response of this.genResponses(message, 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 f3ee44c..182485a 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -46,11 +46,7 @@ export default class KickCommand extends BushCommand { }); } - private async *genResponses( - message: Message | CommandInteraction, - user: GuildMember, - reason?: string - ): AsyncIterable { + private async *genResponses(message: Message | CommandInteraction, user: GuildMember, reason?: string): AsyncIterable { let modlogEnry: Modlog; // Create guild entry so postgres doesn't get mad when I try and add a modlog entry await Guild.findOrCreate({ @@ -76,18 +72,12 @@ export default class KickCommand extends BushCommand { return; } try { - await user.send( - `You were kicked in ${message.guild.name} with reason \`${reason || 'No reason given'}\`` - ); + await user.send(`You were kicked in ${message.guild.name} with reason \`${reason || 'No reason given'}\``); } catch (e) { yield 'Error sending message to user'; } try { - await user.kick( - `Kicked by ${message instanceof Message ? message.author.tag : message.user.tag} with ${ - reason ? `reason ${reason}` : 'no reason' - }` - ); + await user.kick(`Kicked by ${message instanceof Message ? message.author.tag : message.user.tag} with ${reason ? `reason ${reason}` : 'no reason'}`); } catch { yield 'Error kicking :/'; await modlogEnry.destroy(); @@ -96,19 +86,13 @@ export default class KickCommand extends BushCommand { yield `Kicked <@!${user.id}> with reason \`${reason || 'No reason given'}\``; } - async exec( - message: Message, - { user, reason }: { user: GuildMember; reason?: string } - ): Promise { + async exec(message: Message, { user, reason }: { user: GuildMember; reason?: string }): Promise { for await (const response of this.genResponses(message, user, reason)) { await message.util.send(response); } } - async execSlash( - message: CommandInteraction, - { user, reason }: { user: GuildMember; reason?: string } - ): Promise { + async execSlash(message: CommandInteraction, { user, reason }: { user: GuildMember; reason?: string }): Promise { for await (const response of this.genResponses(message, user, reason)) { await message.reply(response); } diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index c4eb46a..806c00a 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -110,9 +110,7 @@ export default class ModlogCommand extends BushCommand { }, { name: 'Duration', - value: `${ - entry.duration ? moment.duration(entry.duration, 'milliseconds').humanize() : 'N/A' - }`, + value: `${entry.duration ? moment.duration(entry.duration, 'milliseconds').humanize() : 'N/A'}`, inline: true }, { diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index f03e0ad..0c7d7db 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -7,15 +7,7 @@ import { ApplicationCommandOptionType } from 'discord-api-types'; export default class RoleCommand extends BushCommand { private roleWhitelist: Record = { 'Partner': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], - 'Suggester': [ - '*', - 'Admin Perms', - 'Sr. Moderator', - 'Moderator', - 'Helper', - 'Trial Helper', - 'Contributor' - ], + 'Suggester': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator', 'Helper', 'Trial Helper', 'Contributor'], 'Level Locked': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'No Files': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], 'No Reactions': ['*', 'Admin Perms', 'Sr. Moderator', 'Moderator'], @@ -81,59 +73,38 @@ export default class RoleCommand extends BushCommand { }); } - public async exec( - message: Message, - { user, role }: { user: GuildMember; role: Role } - ): Promise { - if ( - !message.member.permissions.has('MANAGE_ROLES') && - !this.client.ownerID.includes(message.author.id) - ) { + public async exec(message: Message, { user, role }: { user: GuildMember; role: Role }): Promise { + 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(`<: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(`<: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.`, - { - allowedMentions: AllowedMentions.none() - } - ); + return message.util.reply(`<: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.`, - { - allowedMentions: AllowedMentions.none() - } - ); + return message.util.reply(`<: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(`<:error:837123021016924261> <@&${role.id}> is managed by an integration and cannot be managed.`, { + allowedMentions: AllowedMentions.none() + }); } } // No checks if the user has MANAGE_ROLES @@ -141,28 +112,16 @@ 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}>.`, - { allowedMentions: AllowedMentions.none() } - ); + return message.util.reply(`<: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}>!`, - { allowedMentions: AllowedMentions.none() } - ); + return message.util.reply(`<: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}>.`, - { allowedMentions: AllowedMentions.none() } - ); + return message.util.reply(`<: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}>!`, - { allowedMentions: AllowedMentions.none() } - ); + return message.util.reply(`<:checkmark:837109864101707807> Successfully added <@&${role.id}> to <@${user.id}>!`, { allowedMentions: AllowedMentions.none() }); } } } diff --git a/src/commands/moderation/warn.ts b/src/commands/moderation/warn.ts index 5651830..3410d81 100644 --- a/src/commands/moderation/warn.ts +++ b/src/commands/moderation/warn.ts @@ -25,10 +25,7 @@ export default class WarnCommand extends BushCommand { } }); } - public async exec( - message: Message, - { member, reason }: { member: GuildMember; reason: string } - ): Promise { + public async exec(message: Message, { member, reason }: { member: GuildMember; reason: string }): Promise { // Create guild entry so postgres doesn't get mad when I try and add a modlog entry await Guild.findOrCreate({ where: { diff --git a/src/commands/moulberry-bush/capePerms.ts b/src/commands/moulberry-bush/capePerms.ts index 92ccd73..79f9d6b 100644 --- a/src/commands/moulberry-bush/capePerms.ts +++ b/src/commands/moulberry-bush/capePerms.ts @@ -58,8 +58,7 @@ export default class CapePermissionsCommand extends BushCommand { type: 'string', prompt: { start: 'Who would you like to see the cape permissions of?', - retry: - '<:error:837123021016924261> Choose someone to see the capes their available capes.', + retry: '<:error:837123021016924261> Choose someone to see the capes their available capes.', optional: false } } @@ -94,13 +93,9 @@ export default class CapePermissionsCommand extends BushCommand { } 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 `<: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')); + const embed = this.client.util.createEmbed(this.client.util.colors.default).setTitle(`${user}'s Capes`).setDescription(userPerm.join('\n')); return embed; } else { return `<:error:837123021016924261> There was an error finding cape perms for ${user}.`; @@ -111,10 +106,7 @@ export default class CapePermissionsCommand extends BushCommand { await message.reply(await this.getResponse(user)); } - public async execSlash( - message: CommandInteraction, - { user }: { user: SlashCommandOption } - ): Promise { + public async execSlash(message: CommandInteraction, { user }: { user: SlashCommandOption }): Promise { 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 06cb254..e11585a 100644 --- a/src/commands/moulberry-bush/giveawayPing.ts +++ b/src/commands/moulberry-bush/giveawayPing.ts @@ -15,14 +15,7 @@ export default class GiveawayPingCommand extends BushCommand { examples: ['giveawayping'] }, clientPermissions: ['MANAGE_MESSAGES'], - userPermissions: [ - 'SEND_MESSAGES', - 'MANAGE_GUILD', - 'MANAGE_MESSAGES', - 'BAN_MEMBERS', - 'KICK_MEMBERS', - 'VIEW_CHANNEL' - ], + userPermissions: ['SEND_MESSAGES', 'MANAGE_GUILD', 'MANAGE_MESSAGES', 'BAN_MEMBERS', 'KICK_MEMBERS', 'VIEW_CHANNEL'], channel: 'guild', ignoreCooldown: [], ignorePermissions: [], @@ -32,21 +25,14 @@ export default class GiveawayPingCommand extends BushCommand { }); } public async exec(message: Message): Promise { - if (message.guild.id !== '516977525906341928') - return message.reply( - "<:error:837123021016924261> This command may only be run in Moulberry's Bush." - ); + if (message.guild.id !== '516977525906341928') return message.reply("<:error:837123021016924261> This command may only be run in Moulberry's Bush."); if (!['767782084981817344', '833855738501267456'].includes(message.channel.id)) - return message.reply( - '<:error:837123021016924261> This command may only be run in giveaway channels.' - ); + return message.reply('<:error:837123021016924261> This command may only be run in giveaway channels.'); await message.delete().catch(() => undefined); const webhooks = await (message.channel as TextChannel | NewsChannel).fetchWebhooks(); let webhookClient: WebhookClient; if (webhooks.size < 1) { - const webhook = await (message.channel as TextChannel | NewsChannel).createWebhook( - 'Giveaway ping webhook' - ); + const webhook = await (message.channel as TextChannel | NewsChannel).createWebhook('Giveaway ping webhook'); webhookClient = new WebhookClient(webhook.id, webhook.token); } else { const webhook = webhooks.first(); diff --git a/src/commands/moulberry-bush/level.ts b/src/commands/moulberry-bush/level.ts index 74d6cf4..2b32abd 100644 --- a/src/commands/moulberry-bush/level.ts +++ b/src/commands/moulberry-bush/level.ts @@ -41,9 +41,7 @@ export default class LevelCommand extends BushCommand { private async getResponse(user: User): Promise { const userLevelRow = await Level.findByPk(user.id); if (userLevelRow) { - return `${user ? `${user.tag}'s` : 'Your'} level is ${userLevelRow.level} (${ - userLevelRow.xp - } XP)`; + return `${user ? `${user.tag}'s` : 'Your'} level is ${userLevelRow.level} (${userLevelRow.xp} XP)`; } else { return `${user ? `${user.tag} does` : 'You do'} not have a level yet!`; } @@ -52,10 +50,7 @@ export default class LevelCommand extends BushCommand { async exec(message: Message, { user }: { user?: User }): Promise { await message.reply(await this.getResponse(user || message.author)); } - async execSlash( - message: CommandInteraction, - { user }: { user?: CommandInteractionOption } - ): Promise { + async execSlash(message: CommandInteraction, { user }: { user?: CommandInteractionOption }): Promise { await message.reply(await this.getResponse(user?.user || message.user)); } } diff --git a/src/commands/moulberry-bush/rule.ts b/src/commands/moulberry-bush/rule.ts index 6984603..e3f9e34 100644 --- a/src/commands/moulberry-bush/rule.ts +++ b/src/commands/moulberry-bush/rule.ts @@ -19,28 +19,23 @@ export default class RuleCommand extends BushCommand { }, { title: 'No Spamming', - description: - 'Including but not limited to: any messages that do not contribute to the conversation, repeated messages, randomly tagging users, and chat flood.' + description: 'Including but not limited to: any messages that do not contribute to the conversation, repeated messages, randomly tagging users, and chat flood.' }, { title: 'English', - description: - 'The primary language of the server is English, please keep all discussions in English.' + description: 'The primary language of the server is English, please keep all discussions in English.' }, { title: 'Safe for Work', - description: - 'Please keep NSFW and NSFL content out of this server, avoid borderline images as well as keeping your status and profile picture SFW.' + description: 'Please keep NSFW and NSFL content out of this server, avoid borderline images as well as keeping your status and profile picture SFW.' }, { title: 'No Advertising', - description: - 'Do not promote anything without prior approval from a staff member, this includes DM advertising.' + description: 'Do not promote anything without prior approval from a staff member, this includes DM advertising.' }, { title: 'Impersonation', - description: - 'Do not try to impersonate others for the express intent of being deceitful, defamation , and/or personal gain.' + description: 'Do not try to impersonate others for the express intent of being deceitful, defamation , and/or personal gain.' }, { title: 'Swearing', @@ -48,23 +43,19 @@ export default class RuleCommand extends BushCommand { }, { title: 'Only ping @emergency in emergencies', - description: - 'Pinging <@&833802660209229854> for no reason will result in severe punishment. <@&833802660209229854> is only to be pinged in true emergencies.' + description: 'Pinging <@&833802660209229854> for no reason will result in severe punishment. <@&833802660209229854> is only to be pinged in true emergencies.' }, { title: 'No Backseat Moderating', - description: - 'If you see a rule being broken be broken, please report it using: `-report [evidence]`.' + description: 'If you see a rule being broken be broken, please report it using: `-report [evidence]`.' }, { title: 'Staff may moderate at their discretion', - description: - 'If there are loopholes in our rules, the staff team may moderate based on what they deem appropriate. The staff team holds final discretion.' + description: 'If there are loopholes in our rules, the staff team may moderate based on what they deem appropriate. The staff team holds final discretion.' }, { title: "Sending media that are able to crash a user's Discord", - description: - "Sending videos, GIFs, emojis, etc. that are able to crash someone's discord will result in a **permanent** mute that cannot be appealed." + description: "Sending videos, GIFs, emojis, etc. that are able to crash someone's discord will result in a **permanent** mute that cannot be appealed." } ]; @@ -117,35 +108,20 @@ export default class RuleCommand extends BushCommand { ] }); } - private getResponse( - message: Message | CommandInteraction, - rule?: number, - user?: User - ): string | MessageEmbed | [string, MessageEmbed] { - if ( - message.guild.id !== '516977525906341928' && - !this.client.ownerID.includes( - message instanceof Message ? message.author.id : message.user.id - ) - ) { + private getResponse(message: Message | CommandInteraction, rule?: number, user?: User): string | 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."; } let rulesEmbed = new MessageEmbed().setColor('ef3929'); if (message instanceof Message) { - rulesEmbed = rulesEmbed.setFooter( - `Triggered by ${message.author.tag}`, - message.author.avatarURL({ dynamic: true }) - ); + rulesEmbed = rulesEmbed.setFooter(`Triggered by ${message.author.tag}`, message.author.avatarURL({ dynamic: true })); } if (rule) { const foundRule = this.rules[rule - 1]; rulesEmbed.addField(`${rule}) ${foundRule.title}`, foundRule.description); } else { for (const curRule of this.rules) { - rulesEmbed.addField( - `${this.rules.indexOf(curRule) + 1}) ${curRule.title}`, - curRule.description - ); + rulesEmbed.addField(`${this.rules.indexOf(curRule) + 1}) ${curRule.title}`, curRule.description); } } if (!user) { @@ -154,10 +130,7 @@ export default class RuleCommand extends BushCommand { return [`<@!${user.id}>`, rulesEmbed]; } } - public async exec( - message: Message, - { rule, user }: { rule?: number; user?: User } - ): Promise { + public async exec(message: Message, { rule, user }: { rule?: number; user?: User }): Promise { const response = this.getResponse(message, rule, user); if (Array.isArray(response)) { await message.util.send(response[0], { @@ -169,10 +142,7 @@ export default class RuleCommand extends BushCommand { await message.delete().catch(() => undefined); } - public async execSlash( - message: CommandInteraction, - { rule, user }: { rule?: SlashCommandOption; user?: SlashCommandOption } - ): Promise { + public async execSlash(message: CommandInteraction, { rule, user }: { rule?: SlashCommandOption; user?: SlashCommandOption }): Promise { const response = this.getResponse(message, rule?.value, user?.user); if (Array.isArray(response)) { await message.reply(response[0], { diff --git a/src/commands/server-config/prefix.ts b/src/commands/server-config/prefix.ts index f02e693..c60f8fb 100644 --- a/src/commands/server-config/prefix.ts +++ b/src/commands/server-config/prefix.ts @@ -57,10 +57,7 @@ export default class PrefixCommand extends BushCommand { } } - async execSlash( - message: CommandInteraction, - { prefix }: { prefix?: SlashCommandOption } - ): Promise { + async execSlash(message: CommandInteraction, { prefix }: { prefix?: SlashCommandOption }): Promise { 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 f835321..ebd8c21 100644 --- a/src/config/example-options.ts +++ b/src/config/example-options.ts @@ -18,7 +18,6 @@ export const channels = { dm: 'id here', command: 'id here' }; -export const verbose = false; // Database specific export const db = { @@ -27,3 +26,9 @@ export const db = { username: 'username here', password: 'password here' }; + +// Logging +export const logging: { info: boolean; verbose: boolean } = { + info: false, + verbose: true +}; diff --git a/src/lib/extensions/BushClient.ts b/src/lib/extensions/BushClient.ts index 99d0dd3..2b674b1 100644 --- a/src/lib/extensions/BushClient.ts +++ b/src/lib/extensions/BushClient.ts @@ -1,10 +1,4 @@ -import { - AkairoClient, - CommandHandler, - InhibitorHandler, - ListenerHandler, - TaskHandler -} from 'discord-akairo'; +import { AkairoClient, CommandHandler, InhibitorHandler, ListenerHandler, TaskHandler } from 'discord-akairo'; import { Guild } from 'discord.js'; import * as path from 'path'; import { Sequelize } from 'sequelize'; @@ -89,17 +83,12 @@ export class BushClient extends AkairoClient { }); this.util = new Util(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.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); } @@ -124,9 +113,7 @@ export class BushClient extends AkairoClient { loaders[loader].loadAll(); this.logger.log(chalk.green('Successfully loaded ' + chalk.cyan(loader) + '.')); } catch (e) { - console.error( - chalk.red('Unable to load loader ' + chalk.cyan(loader) + ' with error ' + e) - ); + console.error(chalk.red('Unable to load loader ' + chalk.cyan(loader) + ' with error ' + e)); } } this.taskHandler.startAll(); diff --git a/src/lib/extensions/Util.ts b/src/lib/extensions/Util.ts index 7bedfc1..431410c 100644 --- a/src/lib/extensions/Util.ts +++ b/src/lib/extensions/Util.ts @@ -5,12 +5,7 @@ import { exec } from 'child_process'; import got from 'got'; import { MessageEmbed, GuildMember, User } from 'discord.js'; import { CommandInteractionOption } from 'discord.js'; -import { - ApplicationCommandOptionType, - APIInteractionDataResolvedGuildMember, - APIInteractionDataResolvedChannel, - APIRole -} from 'discord-api-types'; +import { ApplicationCommandOptionType, APIInteractionDataResolvedGuildMember, APIInteractionDataResolvedChannel, APIRole } from 'discord-api-types'; import { GuildChannel } from 'discord.js'; import { Role } from 'discord.js'; import chalk from 'chalk'; @@ -223,20 +218,13 @@ export class Util extends ClientUtil { author = author.user; // Convert to User if GuildMember } let embed = new MessageEmbed().setTimestamp(); - if (author) - embed = embed.setAuthor( - author.username, - author.displayAvatarURL({ dynamic: true }), - `https://discord.com/users/${author.id}` - ); + if (author) embed = embed.setAuthor(author.username, author.displayAvatarURL({ dynamic: true }), `https://discord.com/users/${author.id}`); if (color) embed = embed.setColor(color); return embed; } public async mcUUID(username: string): Promise { - const apiRes = (await got - .get(`https://api.ashcon.app/mojang/v2/user/${username}`) - .json()) as uuidRes; + const apiRes = (await got.get(`https://api.ashcon.app/mojang/v2/user/${username}`).json()) as uuidRes; return apiRes.uuid.replace(/-/g, ''); } @@ -244,24 +232,11 @@ export class Util extends ClientUtil { let fetchedGuild: Guild; if (guild) fetchedGuild = this.client.guilds.cache.get(guild); try { - const registered = - guild === undefined - ? await this.client.application.commands.fetch() - : await fetchedGuild.commands.fetch(); + const registered = guild === undefined ? await this.client.application.commands.fetch() : await fetchedGuild.commands.fetch(); for (const [, registeredCommand] of registered) { - if ( - !this.client.commandHandler.modules.find((cmd) => cmd.id == registeredCommand.name) - ?.execSlash || - force - ) { - guild === undefined - ? await this.client.application.commands.delete(registeredCommand.id) - : await fetchedGuild.commands.delete(registeredCommand.id); - this.client.logger.verbose( - chalk`{red Deleted slash command ${registeredCommand.name}${ - guild !== undefined ? ` in guild ${fetchedGuild.name}` : '' - }}` - ); + if (!this.client.commandHandler.modules.find((cmd) => cmd.id == registeredCommand.name)?.execSlash || force) { + guild === undefined ? await this.client.application.commands.delete(registeredCommand.id) : await fetchedGuild.commands.delete(registeredCommand.id); + this.client.logger.verbose(chalk`{red Deleted slash command ${registeredCommand.name}${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}}`); } } @@ -277,40 +252,20 @@ export class Util extends ClientUtil { if (found?.id && !force) { if (slashdata.description !== found.description) { - guild === undefined - ? await this.client.application.commands.edit(found.id, slashdata) - : fetchedGuild.commands.edit(found.id, slashdata); - this.client.logger.verbose( - chalk`{yellow Edited slash command ${BushCommand.id}${ - guild !== undefined ? ` in guild ${fetchedGuild.name}` : '' - }}` - ); + guild === undefined ? await this.client.application.commands.edit(found.id, slashdata) : fetchedGuild.commands.edit(found.id, slashdata); + this.client.logger.verbose(chalk`{yellow Edited slash command ${BushCommand.id}${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}}`); } } else { - guild === undefined - ? await this.client.application.commands.create(slashdata) - : fetchedGuild.commands.create(slashdata); - this.client.logger.verbose( - chalk`{green Created slash command ${BushCommand.id}${ - guild !== undefined ? ` in guild ${fetchedGuild.name}` : '' - }}` - ); + guild === undefined ? await this.client.application.commands.create(slashdata) : fetchedGuild.commands.create(slashdata); + this.client.logger.verbose(chalk`{green Created slash command ${BushCommand.id}${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}}`); } } } - return this.client.logger.log( - chalk.green( - `Slash commands registered${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}` - ) - ); + return this.client.logger.log(chalk.green(`Slash commands registered${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}`)); } catch (e) { console.log(chalk.red(e.stack)); - return this.client.logger.error( - chalk`{red Slash commands not registered${ - guild !== undefined ? ` in guild ${fetchedGuild.name}` : '' - }, see above error.}` - ); + return this.client.logger.error(chalk`{red Slash commands not registered${guild !== undefined ? ` in guild ${fetchedGuild.name}` : ''}, see above error.}`); } } diff --git a/src/lib/models/Guild.ts b/src/lib/models/Guild.ts index c224bf5..7902461 100644 --- a/src/lib/models/Guild.ts +++ b/src/lib/models/Guild.ts @@ -8,10 +8,7 @@ export interface GuildModel { } export type GuildModelCreationAttributes = Optional; -export class Guild - extends BaseModel - implements GuildModel -{ +export class Guild extends BaseModel implements GuildModel { id: string; prefix: string; static initModel(seqeulize: Sequelize, client: BushClient): void { diff --git a/src/lib/models/Modlog.ts b/src/lib/models/Modlog.ts index 52a13b2..2b2050a 100644 --- a/src/lib/models/Modlog.ts +++ b/src/lib/models/Modlog.ts @@ -32,10 +32,7 @@ export interface ModlogModelCreationAttributes { guild: string; } -export class Modlog - extends BaseModel - implements ModlogModel -{ +export class Modlog extends BaseModel implements ModlogModel { id: string; type: ModlogType; user: string; diff --git a/src/lib/utils/AllowedMentions.ts b/src/lib/utils/AllowedMentions.ts index 47e440b..68c44d7 100644 --- a/src/lib/utils/AllowedMentions.ts +++ b/src/lib/utils/AllowedMentions.ts @@ -34,11 +34,7 @@ export default class AllowedMentions { } public toObject(): MessageMentionOptions { return { - parse: [ - ...(this.users ? ['users'] : []), - ...(this.roles ? ['roles'] : []), - ...(this.everyone ? ['everyone'] : []) - ] as MessageMentionTypes[] + parse: [...(this.users ? ['users'] : []), ...(this.roles ? ['roles'] : []), ...(this.everyone ? ['everyone'] : [])] as MessageMentionTypes[] }; } } diff --git a/src/lib/utils/Console.ts b/src/lib/utils/Console.ts new file mode 100644 index 0000000..36c078c --- /dev/null +++ b/src/lib/utils/Console.ts @@ -0,0 +1,94 @@ +import chalk from 'chalk'; +import { BushClient } from '../extensions/BushClient'; + +export class CustomConsole { + private client: BushClient; + public constructor(client: BushClient) { + this.client = client; + } + + private parseColors(content: any, color: 'blueBright' | 'blackBright' | 'redBright' | 'yellowBright' | 'greenBright'): string|any { + if (typeof content === 'string') { + const newContent: Array = content.split(/<<|>>/); + const tempParsedArray: Array = []; + newContent.forEach((value, index) => { + if (index % 2 !== 0) { + tempParsedArray.push(chalk[color](value)); + } else { + tempParsedArray.push(value); + } + }); + return tempParsedArray.join(''); + } else { + return content + } + + } + + 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; + } + + return `${hour >= 10 ? hour : `0${hour}`}:${minute >= 10 ? minute : `0${minute}`} ${amOrPm}`; + } + + /** + * Logs debug information. + * @param content - The content to log. + */ + public debug(content: any): void { + console.log(`${chalk.bgGrey(this.timeStamp())} ${chalk.grey('[Debug]')}`, content); + } + + /** + * 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 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 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 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 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')); + } +} diff --git a/src/listeners/client/ready.ts b/src/listeners/client/ready.ts index 8d4ac6a..d4b2808 100644 --- a/src/listeners/client/ready.ts +++ b/src/listeners/client/ready.ts @@ -10,9 +10,6 @@ export default class ReadyListener extends BushListener { } public async exec(): Promise { - await this.client.logger.log( - chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, - true - ); + await this.client.logger.log(chalk`{green Sucessfully logged in as {cyan ${this.client.user.tag}}.}`, true); } } diff --git a/src/listeners/commands/commandBlocked.ts b/src/listeners/commands/commandBlocked.ts index a6a7e7e..916f7cd 100644 --- a/src/listeners/commands/commandBlocked.ts +++ b/src/listeners/commands/commandBlocked.ts @@ -13,9 +13,7 @@ export default class CommandBlockedListener extends BushListener { public async exec(message: Message, command: Command, reason: string): Promise { switch (reason) { case 'owner': { - await message.util.send( - `You must be an owner to run command \`${message.util.parsed.command}\`` - ); + await message.util.send(`You must be an owner to run command \`${message.util.parsed.command}\``); break; } case 'blacklist': { diff --git a/src/listeners/commands/commandError.ts b/src/listeners/commands/commandError.ts index c5d6a74..0e52140 100644 --- a/src/listeners/commands/commandError.ts +++ b/src/listeners/commands/commandError.ts @@ -35,9 +35,7 @@ export default class CommandErrorListener extends BushListener { ` ); } - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; + const channel = (await this.client.channels.fetch(this.client.config.channels.log)) as TextChannel; await channel.send(errorDevEmbed); if (errorUserEmbed) await message.reply(errorUserEmbed); } diff --git a/src/listeners/commands/commandStarted.ts b/src/listeners/commands/commandStarted.ts index d81ed59..1c5b0c7 100644 --- a/src/listeners/commands/commandStarted.ts +++ b/src/listeners/commands/commandStarted.ts @@ -12,12 +12,8 @@ export default class CommandStartedListener extends BushListener { } exec(message: Message, command: BushCommand): void { this.client.logger.verbose( - chalk`{cyan {green ${message.author.tag}} is running {green ${ - command.aliases[0] - }} in {green ${ - message.channel instanceof DMChannel - ? 'DMs' - : `#${message.channel.name} (Server: ${message.guild.name})` + chalk`{cyan {green ${message.author.tag}} is running {green ${command.aliases[0]}} in {green ${ + message.channel instanceof DMChannel ? 'DMs' : `#${message.channel.name} (Server: ${message.guild.name})` }}.}` ); } diff --git a/src/listeners/commands/slashCommandError.ts b/src/listeners/commands/slashCommandError.ts index 32ccd90..e4c3bb0 100644 --- a/src/listeners/commands/slashCommandError.ts +++ b/src/listeners/commands/slashCommandError.ts @@ -35,9 +35,7 @@ export default class SlashCommandErrorListener extends BushListener { ` ); } - const channel = (await this.client.channels.fetch( - this.client.config.channels.log - )) as TextChannel; + const channel = (await this.client.channels.fetch(this.client.config.channels.log)) as TextChannel; await channel.send(errorDevEmbed); if (errorUserEmbed) await message.reply(errorUserEmbed); } diff --git a/src/listeners/message/level.ts b/src/listeners/message/level.ts index c9d5522..1e93055 100644 --- a/src/listeners/message/level.ts +++ b/src/listeners/message/level.ts @@ -30,9 +30,7 @@ export default class LevelListener extends BushListener { const xpToGive = Level.genRandomizedXp(); user.xp += xpToGive; await user.save(); - await this.client.logger.verbose( - chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}` - ); + await this.client.logger.verbose(chalk`{cyan Gave XP to {green ${message.author.tag}}: {green ${xpToGive}xp}.}`); this.levelCooldowns.add(message.author.id); setTimeout(() => this.levelCooldowns.delete(message.author.id), 60_000); } diff --git a/tsconfig.json b/tsconfig.json index e51e0b3..4177350 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,14 +3,7 @@ "module": "commonjs", "target": "esNext", "outDir": "dist", - "lib": [ - "ESNext", - "ESNext.Array", - "ESNext.AsyncIterable", - "ESNext.Intl", - "ESNext.Symbol", - "DOM" - ], + "lib": ["ESNext", "ESNext.Array", "ESNext.AsyncIterable", "ESNext.Intl", "ESNext.Symbol", "DOM"], "sourceMap": false, "inlineSourceMap": true, "inlineSources": true, -- cgit