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/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 ++++++++++ 10 files changed, 743 insertions(+), 739 deletions(-) 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 (limited to 'src/commands/moderation') 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}".` + ); + } +} -- cgit