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') 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