diff options
21 files changed, 417 insertions, 471 deletions
@@ -1,140 +1,19 @@ - -# Created by https://www.toptal.com/developers/gitignore/api/node,yarn,vscode,webstorm -# Edit at https://www.toptal.com/developers/gitignore?templates=node,yarn,vscode,webstorm - -### Node ### # Logs -logs *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release # Dependency directories node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test -.env*.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt dist -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - ### vscode ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json +!.vscode/*.code-snippets *.code-workspace -### WebStorm ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - # Generated files .idea/**/contentModel.xml @@ -147,90 +26,13 @@ dist .idea/**/uiDesigner.xml .idea/**/dbnavigator.xml -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### WebStorm Patch ### -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 - -# *.iml -# modules.xml -# .idea/misc.xml -# *.ipr - -# Sonarlint plugin -# https://plugins.jetbrains.com/plugin/7973-sonarlint -.idea/**/sonarlint/ - -# SonarQube Plugin -# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin -.idea/**/sonarIssues.xml - -# Markdown Navigator plugin -# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced -.idea/**/markdown-navigator.xml -.idea/**/markdown-navigator-enh.xml -.idea/**/markdown-navigator/ - # Cache file creation bug -# See https://youtrack.jetbrains.com/issue/JBR-2257 -.idea/$CACHE_FILE$ .yarn/cache/* .yarn/sdks/eslint/* .yarn/sdks/prettier/* .yarn/install-state.gz .yarn/build-state.url .yarn/releases.gz -# CodeStream plugin -# https://plugins.jetbrains.com/plugin/12206-codestream -.idea/codestream.xml ### yarn ### # https://yarnpkg.com/advanced/qa#which-files-should-be-gitignored @@ -240,16 +42,8 @@ fabric.properties !.yarn/plugins !.yarn/sdks !.yarn/versions - -# if you are NOT using Zero-installs, then: -# comment the following lines !.yarn/cache -# and uncomment the following lines -# .pnp.* - -# End of https://www.toptal.com/developers/gitignore/api/node,yarn,vscode,webstorm - # Options and credentials for the bot src/config/options.ts diff --git a/.vscode/typescript.code-snippets b/.vscode/typescript.code-snippets new file mode 100644 index 0000000..0984c3c --- /dev/null +++ b/.vscode/typescript.code-snippets @@ -0,0 +1,73 @@ +/* prettier-ignore */ +{ + /** + * Place your snippets for typescript here. Each snippet is defined under a snippet name and has a prefix, body and + * description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are: + * $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the + * same ids are connected. + */ + + "Setup Slash Command": { + "prefix": "slash", + "body": [ + "slash: true,", + "slashOptions: [", + "\t$0", + "]" + ] + }, + + "Slash Command User Argument": { + "prefix": "user", + "body": [ + "{", + "\tname: 'user',", + "\tdescription: 'The user you would like to$1',", + "\ttype: 'USER',", + "\trequired: $2", + "},$0" + ] + }, + + "Slash Command String Argument": { + "prefix": "string", + "body": [ + "{", + "\tname: '$1',", + "\tdescription: '$2',", + "\ttype: 'STRING',", + "\trequired: $3", + "},$0" + ] + }, + + "Slash Command Choice Argument": { + "prefix": "choice", + "body": [ + "{", + "\tname: '$1',", + "\tdescription: '$2',", + "\ttype: 'STRING',", + "\tchoices: [", + "\t\t{", + "\t\t\tname: '$3',", + "\t\t\tvalue: '$4'", + "\t\t},", + "\t],", + "\trequired: $5", + "},$0" + ] + }, + + "Slash Boolean Argument": { + "prefix": "boolean", + "body": [ + "{", + "\tname: '$1',", + "\tdescription: '$2',", + "\ttype: 'BOOLEAN',", + "\trequired: $3", + "},$0" + ] + } +} diff --git a/src/commands/dev/superUser.ts b/src/commands/dev/superUser.ts index 981c0da..4d5ce2d 100644 --- a/src/commands/dev/superUser.ts +++ b/src/commands/dev/superUser.ts @@ -1,6 +1,6 @@ import { Constants } from 'discord-akairo'; import { User } from 'discord.js'; -import { BushCommand, BushMessage, Global } from '../../lib'; +import { BushCommand, BushMessage, BushSlashMessage, Global } from '../../lib'; export default class SuperUserCommand extends BushCommand { public constructor() { @@ -39,7 +39,7 @@ export default class SuperUserCommand extends BushCommand { }; return { action, user }; } - public async exec(message: BushMessage, args: { action: 'add' | 'remove'; user: User }): Promise<unknown> { + public async exec(message: BushMessage | BushSlashMessage, args: { action: 'add' | 'remove'; user: User }): Promise<unknown> { if (!message.author.isOwner()) return await message.util.reply(`${this.client.util.emojis.error} Only my developers can run this command.`); diff --git a/src/commands/info/botInfo.ts b/src/commands/info/botInfo.ts index 4a94318..80ca29d 100644 --- a/src/commands/info/botInfo.ts +++ b/src/commands/info/botInfo.ts @@ -1,5 +1,5 @@ -import { Message, MessageEmbed } from 'discord.js'; -import { BushCommand } from '../../lib'; +import { MessageEmbed } from 'discord.js'; +import { BushCommand, BushMessage, BushSlashMessage } from '../../lib'; export default class BotInfoCommand extends BushCommand { public constructor() { @@ -17,7 +17,7 @@ export default class BotInfoCommand extends BushCommand { }); } - public async exec(message: Message): Promise<void> { + public async exec(message: BushMessage | BushSlashMessage): Promise<void> { 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', ''); diff --git a/src/commands/info/ping.ts b/src/commands/info/ping.ts index c1be3fb..804ede2 100644 --- a/src/commands/info/ping.ts +++ b/src/commands/info/ping.ts @@ -1,5 +1,5 @@ import { Message, MessageEmbed } from 'discord.js'; -import { BushCommand, BushSlashMessage } from '../../lib'; +import { BushCommand, BushMessage, BushSlashMessage } from '../../lib'; export default class PingCommand extends BushCommand { public constructor() { @@ -17,7 +17,7 @@ export default class PingCommand extends BushCommand { }); } - public async exec(message: Message): Promise<void> { + public async exec(message: BushMessage): Promise<void> { const sentMessage = (await message.util.send('Pong!')) as Message; const timestamp: number = message.editedTimestamp ? message.editedTimestamp : message.createdTimestamp; const botLatency = `\`\`\`\n ${Math.floor(sentMessage.createdTimestamp - timestamp)}ms \`\`\``; diff --git a/src/commands/moderation/block.ts b/src/commands/moderation/_block.ts index e69de29..e69de29 100644 --- a/src/commands/moderation/block.ts +++ b/src/commands/moderation/_block.ts diff --git a/src/commands/moderation/unban.ts b/src/commands/moderation/_unban.ts index e69de29..e69de29 100644 --- a/src/commands/moderation/unban.ts +++ b/src/commands/moderation/_unban.ts diff --git a/src/commands/moderation/unblock.ts b/src/commands/moderation/_unblock.ts index e69de29..e69de29 100644 --- a/src/commands/moderation/unblock.ts +++ b/src/commands/moderation/_unblock.ts diff --git a/src/commands/moderation/unmute.ts b/src/commands/moderation/_unmute.ts index e69de29..e69de29 100644 --- a/src/commands/moderation/unmute.ts +++ b/src/commands/moderation/_unmute.ts diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 0c68497..244014b 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -1,5 +1,5 @@ -import { Message, User } from 'discord.js'; -import { BushCommand } from '../../lib'; +import { User } from 'discord.js'; +import { BushCommand, BushMessage, BushSlashMessage } from '../../lib'; export default class BanCommand extends BushCommand { public constructor() { @@ -140,7 +140,7 @@ export default class BanCommand extends BushCommand { // } // } async exec( - message: Message, + message: BushMessage | BushSlashMessage, { user, reason, time }: { user: User; reason?: string; time?: number | string } ): Promise<unknown> { return message.util.reply(`${this.client.util.emojis.error} This command is not finished.`); diff --git a/src/commands/moderation/kick.ts b/src/commands/moderation/kick.ts index 8375198..919c14b 100644 --- a/src/commands/moderation/kick.ts +++ b/src/commands/moderation/kick.ts @@ -1,15 +1,19 @@ -import { GuildMember, Message } from 'discord.js'; -import { BushCommand } from '../../lib'; +import { BushCommand, BushGuildMember, BushMessage, BushSlashMessage, BushUser } from '../../lib'; export default class KickCommand extends BushCommand { public constructor() { super('kick', { aliases: ['kick'], category: 'moderation', + description: { + content: 'Kick a user.', + usage: 'kick <member> <reason>', + examples: ['kick @user bad'] + }, args: [ { id: 'user', - type: 'member', + type: 'user', prompt: { start: 'What user would you like to kick?', retry: '{error} Choose a valid user to kick.' @@ -20,19 +24,13 @@ export default class KickCommand extends BushCommand { type: 'string', match: 'restContent', prompt: { - start: 'Why would you like to kick this user?', - retry: '{error} Choose a valid user to kick.', + start: 'Why should this user be kicked?', + retry: '{error} Choose a valid kick reason.', optional: true } } ], - clientPermissions: ['KICK_MEMBERS'], - userPermissions: ['KICK_MEMBERS'], - description: { - content: 'Kick a member from the server.', - usage: 'kick <member> <reason>', - examples: ['kick @user bad'] - }, + slash: true, slashOptions: [ { type: 'USER', @@ -43,67 +41,30 @@ export default class KickCommand extends BushCommand { { type: 'STRING', name: 'reason', - description: 'Why would you like to kick this user?', + description: 'Why should this user be kicked?', required: false } ], - slash: true + clientPermissions: ['SEND_MESSAGES', 'KICK_MEMBERS'], + userPermissions: ['KICK_MEMBERS'] }); } - // private async *genResponses( - // message: Message | CommandInteraction, - // user: GuildMember, - // reason?: string - // ): AsyncIterable<string> { - // 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) { - // this.client.console.error(`KickCommand`, `Error saving to database. ${e?.stack || e}`); - // yield `${this.client.util.emojis.error} 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 { - // yield `${this.client.util.emojis.warn} Unable to dm user`; - // } - // try { - // await user.kick( - // `Kicked by ${message instanceof Message ? message.author.tag : message.user.tag} with ${ - // reason ? `reason ${reason}` : 'no reason' - // }` - // ); - // } catch { - // yield `${this.client.util.emojis.error} Error kicking :/`; - // await modlogEnry.destroy(); - // return; - // } - // yield `${this.client.util.emojis.success} Kicked <@!${user.id}> with reason \`${reason || 'No reason given'}\``; - // } + async exec(message: BushMessage | BushSlashMessage, { user, reason }: { user: BushUser; reason?: string }): Promise<unknown> { + const member = message.guild.members.cache.get(user.id) as BushGuildMember; + const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'kick'); + // const victimBoldTag = `**${member.user.tag}**`; + + if (typeof canModerateResponse !== 'boolean') { + return message.util.reply(canModerateResponse); + } - async exec(message: Message, { user, reason }: { user: GuildMember; reason?: string }): Promise<unknown> { - return message.util.reply(`${this.client.util.emojis.error} This command is not finished.`); - // for await (const response of this.genResponses(message, user, reason)) { - // await message.util.send(response); - // } + const response = await member.bushKick({ + reason, + moderator: message.author + }); + + } } diff --git a/src/commands/moderation/modlog.ts b/src/commands/moderation/modlog.ts index 5be50a4..36f72fc 100644 --- a/src/commands/moderation/modlog.ts +++ b/src/commands/moderation/modlog.ts @@ -1,7 +1,7 @@ import { Argument } from 'discord-akairo'; import { MessageEmbed } from 'discord.js'; import moment from 'moment'; -import { BushCommand, BushMessage, ModLog } from '../../lib'; +import { BushCommand, BushMessage, BushSlashMessage, ModLog } from '../../lib'; export default class ModlogCommand extends BushCommand { public constructor() { @@ -48,7 +48,7 @@ export default class ModlogCommand extends BushCommand { return modLog.join(`\n`); } - async exec(message: BushMessage, { search }: { search: string }): Promise<unknown> { + async exec(message: BushMessage | BushSlashMessage, { search }: { search: string }): Promise<unknown> { const foundUser = await this.client.util.resolveUserAsync(search); if (foundUser) { const logs = await ModLog.findAll({ diff --git a/src/commands/moderation/mute.ts b/src/commands/moderation/mute.ts index 33c0e32..bc3abf2 100644 --- a/src/commands/moderation/mute.ts +++ b/src/commands/moderation/mute.ts @@ -1,5 +1,5 @@ import { Argument } from 'discord-akairo'; -import { BushCommand, BushGuildMember, BushMessage, BushUser } from '../../lib'; +import { BushCommand, BushGuildMember, BushMessage, BushSlashMessage, BushUser } from '../../lib'; export default class MuteCommand extends BushCommand { public constructor() { @@ -8,8 +8,8 @@ export default class MuteCommand extends BushCommand { category: 'moderation', description: { content: 'Mute a user.', - usage: 'mute <member> <reason> [--time]', - examples: ['mute @user bad boi --time 1h'] + usage: 'mute <member> [reason] [duration]', + examples: ['mute 322862723090219008 1 day commands in #general'] }, args: [ { @@ -31,8 +31,7 @@ export default class MuteCommand extends BushCommand { } } ], - clientPermissions: ['MANAGE_ROLES'], - userPermissions: ['MANAGE_MESSAGES'], + slash: true, slashOptions: [ { type: 'USER', @@ -47,37 +46,29 @@ export default class MuteCommand extends BushCommand { required: false } ], - slash: true + channel: 'guild', + clientPermissions: ['SEND_MESSAGES', 'MANAGE_ROLES'], + userPermissions: ['MANAGE_MESSAGES'] }); } async exec( - message: BushMessage, + message: BushMessage | BushSlashMessage, { user, reason }: { user: BushUser; reason?: { duration: number; contentWithoutTime: string } } ): Promise<unknown> { const error = this.client.util.emojis.error; const member = message.guild.members.cache.get(user.id) as BushGuildMember; - const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member); + const canModerateResponse = this.client.util.moderationPermissionCheck(message.member, member, 'mute'); const victimBoldTag = `**${member.user.tag}**`; - switch (canModerateResponse) { - case 'moderator': - return message.util.reply(`${error} You cannot mute ${victimBoldTag} because they are a moderator.`); - case 'user hierarchy': - return message.util.reply( - `${error} You cannot mute ${victimBoldTag} because they have higher or equal role hierarchy as you do.` - ); - case 'client hierarchy': - return message.util.reply( - `${error} You cannot mute ${victimBoldTag} because they have higher or equal role hierarchy as I do.` - ); - case 'self': - return message.util.reply(`${error} You cannot mute yourself.`); + + if (typeof canModerateResponse !== 'boolean') { + return message.util.reply(canModerateResponse); } let time: number; if (reason) { time = typeof reason === 'string' - ? await Argument.cast('duration', this.client.commandHandler.resolver, message, reason) + ? await Argument.cast('duration', this.client.commandHandler.resolver, message as BushMessage, reason) : reason.duration; } const parsedReason = reason.contentWithoutTime; diff --git a/src/commands/moderation/role.ts b/src/commands/moderation/role.ts index 83e85e0..6bac9e8 100644 --- a/src/commands/moderation/role.ts +++ b/src/commands/moderation/role.ts @@ -1,28 +1,7 @@ /* eslint-disable @typescript-eslint/no-empty-function */ -import { GuildMember, Message, Role } from 'discord.js'; -import { AllowedMentions, BushCommand } from '../../lib'; +import { AllowedMentions, BushCommand, BushGuildMember, BushMessage, BushRole, BushSlashMessage } from '../../lib'; export default class RoleCommand extends BushCommand { - private roleWhitelist: Record<string, string[]> = { - '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'] - }; public constructor() { super('role', { aliases: ['role', 'addrole', 'removerole'], @@ -32,59 +11,103 @@ export default class RoleCommand extends BushCommand { usage: 'role <add|remove> <user> <role>', examples: ['role add tyman adminperms'] }, - clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'], - channel: 'guild', - typing: true, - args: [ + slash: true, + slashOptions: [ { - id: 'user', - type: 'member', - prompt: { - start: `What user do you want to add/remove the role on?`, - retry: `{error} Choose a valid user to add/remove the role on.` - } + name: 'action', + description: 'Would you like to add or remove a role?', + type: 'STRING', + choices: [ + { + name: 'add', + value: 'add' + }, + { + name: 'remove', + value: 'remove' + } + ], + required: true }, { - id: 'role', - type: 'role', - match: 'restContent', - prompt: { - start: `What role do you want to add/remove?`, - retry: `{error} Choose a valid role to add/remove.` - } - } - ], - slashOptions: [ - { - type: 'USER', name: 'user', - description: 'The user to add/remove the role on', + description: 'The user you would like to add/remove the role from.', + type: 'USER', required: true }, { - type: 'ROLE', name: 'role', - description: 'The role to add/remove', + description: 'The role you would like to add/remove from the user.', + type: 'ROLE', required: true } - ] + ], + channel: 'guild', + typing: true, + clientPermissions: ['MANAGE_ROLES', 'EMBED_LINKS', 'SEND_MESSAGES'], + userPermissions: ['SEND_MESSAGES'] }); } - public async exec(message: Message, { user, role }: { user: GuildMember; role: Role }): Promise<unknown> { + *args(): unknown { + const action: 'add' | 'remove' = yield { + id: 'action', + type: [['add'], ['remove']], + prompt: { + start: 'Would you like to `add` or `remove` a role?', + retry: '{error} Choose whether you would you like to `add` or `remove` a role.' + } + }; + let action2: 'to' | 'from'; + if (action === 'add') action2 = 'to'; + else if (action === 'remove') action2 = 'from'; + else return; + const user = yield { + id: 'user', + type: 'member', + prompt: { + start: `What user do you want to ${action} the role ${action2}?`, + retry: `{error} Choose a valid user to ${action} the role ${action2}.` + } + //unordered: true + }; + const role = yield { + id: 'role', + type: 'role', + match: 'restContent', + prompt: { + start: `What role do you want to ${action}?`, + retry: `{error} Choose a valid role to ${action}.` + } + }; + return { action, user, role }; + } + + public async exec( + message: BushMessage | BushSlashMessage, + { action, user, role }: { action: 'add' | 'remove'; user: BushGuildMember; role: BushRole } + ): Promise<unknown> { if (!message.member.permissions.has('MANAGE_ROLES') && !this.client.ownerID.includes(message.author.id)) { - const mappedRole = this.client.consts.moulberryBushRoleMap.find((m) => m.id === role.id); - if (!mappedRole || !this.roleWhitelist[mappedRole.name]) { - return message.util.reply({ + const mappings = this.client.consts.mappings; + let mappedRole: { name: string; id: string }; + for (let i = 0; i < mappings.roleMap.length; i++) { + const a = mappings.roleMap[i]; + if (a.id == role.id) mappedRole = a; + } + if (!mappedRole || !mappings.roleWhitelist[mappedRole.name]) { + return await message.util.reply({ content: `${this.client.util.emojis.error} <@&${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.consts.moulberryBushRoleMap.find((m) => m.name === r).id; + const allowedRoles = mappings.roleWhitelist[mappedRole.name].map((r) => { + for (let i = 0; i < mappings.roleMap.length; i++) { + if (mappings.roleMap[i].name == r) return mappings.roleMap[i].id; + } + return; }); if (!message.member.roles.cache.some((role) => allowedRoles.includes(role.id))) { - return message.util.reply({ + return await message.util.reply({ content: `${this.client.util.emojis.error} <@&${role.id}> is whitelisted, but you do not have any of the roles required to manage it.`, allowedMentions: AllowedMentions.none() }); @@ -92,51 +115,51 @@ export default class RoleCommand extends BushCommand { } if (!this.client.ownerID.includes(message.author.id)) { if (role.comparePositionTo(message.member.roles.highest) >= 0) { - return message.util.reply({ + return await message.util.reply({ content: `${this.client.util.emojis.error} <@&${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({ + return await message.util.reply({ content: `${this.client.util.emojis.error} <@&${role.id}> is higher or equal to my highest role.`, allowedMentions: AllowedMentions.none() }); } if (role.managed) { - await message.util.reply({ + await await message.util.reply({ content: `${this.client.util.emojis.error} <@&${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({ + // no checks if the user has MANAGE_ROLES + if (action == 'remove') { + const success = await user.roles.remove(role.id).catch(() => {}); + if (success) { + return await message.util.reply({ + content: `${this.client.util.emojis.success}Successfully removed <@&${role.id}> from <@${user.id}>!`, + allowedMentions: AllowedMentions.none() + }); + } else { + return await message.util.reply({ content: `${this.client.util.emojis.error} Could not remove <@&${role.id}> from <@${user.id}>.`, allowedMentions: AllowedMentions.none() }); } - return message.util.reply({ - content: `${this.client.util.emojis.success} Successfully removed <@&${role.id}> from <@${user.id}>!`, - allowedMentions: AllowedMentions.none() - }); - } else { - try { - await user.roles.add(role.id); - } catch { - return message.util.reply({ + } else if (action == 'add') { + const success = await user.roles.add(role.id).catch(() => {}); + if (success) { + return await message.util.reply({ + content: `${this.client.util.emojis.success} Successfully added <@&${role.id}> to <@${user.id}>!`, + allowedMentions: AllowedMentions.none() + }); + } else { + return await message.util.reply({ content: `${this.client.util.emojis.error} Could not add <@&${role.id}> to <@${user.id}>.`, allowedMentions: AllowedMentions.none() }); } |
