From e62ee7e491cd67dc855825582f1e44476bb741be Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 17 Sep 2022 16:21:00 +0100 Subject: [PATCH] Update permissions --- src/client/events.ts | 47 +---- src/commands/501231711271780357/Lobby/add.ts | 54 ++++++ .../501231711271780357/Lobby/lobby.ts | 41 +++++ .../501231711271780357/Lobby/remove.ts | 39 +++++ src/commands/501231711271780357/entry.ts | 10 +- src/commands/501231711271780357/lobby.ts | 162 ------------------ src/commands/Role/config.ts | 54 ++++++ src/commands/{ => Role}/role.ts | 85 ++------- src/commands/about.ts | 1 - src/commands/audits.ts | 8 +- src/commands/ban.ts | 10 +- src/commands/bunny.ts | 2 - src/commands/clear.ts | 15 +- src/commands/code.ts | 26 +-- src/commands/config.ts | 22 +-- src/commands/disable.ts | 8 +- src/commands/ignore.ts | 10 +- src/commands/kick.ts | 10 +- src/commands/mute.ts | 12 +- src/commands/rules.ts | 10 +- src/commands/setup.ts | 9 +- src/commands/unmute.ts | 12 +- src/commands/warn.ts | 7 +- src/constants/CommandResponse.ts | 7 - src/constants/ErrorMessages.ts | 27 --- src/entity/Role.ts | 4 + src/registry.ts | 15 +- src/type/command.ts | 16 -- 28 files changed, 283 insertions(+), 440 deletions(-) create mode 100644 src/commands/501231711271780357/Lobby/add.ts create mode 100644 src/commands/501231711271780357/Lobby/lobby.ts create mode 100644 src/commands/501231711271780357/Lobby/remove.ts delete mode 100644 src/commands/501231711271780357/lobby.ts create mode 100644 src/commands/Role/config.ts rename src/commands/{ => Role}/role.ts (54%) delete mode 100644 src/constants/CommandResponse.ts delete mode 100644 src/constants/ErrorMessages.ts diff --git a/src/client/events.ts b/src/client/events.ts index bfc61c0..5970f47 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,22 +1,18 @@ -import { GuildMemberRoleManager, Interaction } from "discord.js"; -import { CommandResponse } from "../constants/CommandResponse"; -import ErrorMessages from "../constants/ErrorMessages"; +import { Interaction } from "discord.js"; import ICommandItem from "../contracts/ICommandItem"; import SettingsHelper from "../helpers/SettingsHelper"; -import StringTools from "../helpers/StringTools"; import { CoreClient } from "./client"; export class Events { public async onInteractionCreate(interaction: Interaction) { if (!interaction.isChatInputCommand()) return; - if (!interaction.guild) return; - if (!interaction.member) return; + if (!interaction.guildId) return; - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString?.split(","); if (disabledCommands?.find(x => x == interaction.commandName)) { - interaction.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); + await interaction.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); return; } @@ -28,7 +24,7 @@ export class Events { if (!itemForServer) { if (!item) { - interaction.reply('Command not found'); + await interaction.reply('Command not found'); return; } @@ -37,39 +33,6 @@ export class Events { itemToUse = itemForServer; } - const requiredRoles = itemToUse.Command.Roles; - - if (interaction.member.user.id != process.env.BOT_OWNERID && interaction.member.user.id != interaction.guild.ownerId) { - for (const i in requiredRoles) { - const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, interaction.guildId!); - - if (!setting) { - interaction.reply("Unable to verify if you have this role, please contact your bot administrator"); - return; - } - - const roles = interaction.member.roles as GuildMemberRoleManager; - - if (!roles.cache.find(role => role.name == setting)) { - interaction.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); - return; - } - } - } - - const precheckResponse = itemToUse.Command.precheck(interaction); - const precheckAsyncResponse = await itemToUse.Command.precheckAsync(interaction); - - if (precheckResponse != CommandResponse.Ok) { - interaction.reply(ErrorMessages.GetErrorMessage(precheckResponse)); - return; - } - - if (precheckAsyncResponse != CommandResponse.Ok) { - interaction.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); - return; - } - itemToUse.Command.execute(interaction); } diff --git a/src/commands/501231711271780357/Lobby/add.ts b/src/commands/501231711271780357/Lobby/add.ts new file mode 100644 index 0000000..105844c --- /dev/null +++ b/src/commands/501231711271780357/Lobby/add.ts @@ -0,0 +1,54 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../entity/501231711271780357/Lobby"; + +export default class AddRole extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('addlobby') + .setDescription('Add lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel')) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role to ping on request')) + .addNumberOption(option => + option + .setName('cooldown') + .setDescription('The cooldown in minutes')) + .addStringOption(option => + option + .setName('name') + .setDescription('The game name')); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + const role = interaction.options.get('role'); + const cooldown = interaction.options.get('cooldown'); + const gameName = interaction.options.get('name'); + + if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { + await interaction.reply('Fields are required.'); + return; + } + + const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (lobby) { + await interaction.reply('This channel has already been setup.'); + return; + } + + const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); + await entity.Save(eLobby, entity); + + await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes \` and will ping \`${role.name}\` on use`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/lobby.ts b/src/commands/501231711271780357/Lobby/lobby.ts new file mode 100644 index 0000000..5be32ad --- /dev/null +++ b/src/commands/501231711271780357/Lobby/lobby.ts @@ -0,0 +1,41 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../entity/501231711271780357/Lobby"; + +export default class Lobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('lobby') + .setDescription('Attempt to organise a lobby'); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.channelId) return; + + const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); + + if (!lobby) { + await interaction.reply('This channel is disabled from using the lobby command.'); + return; + } + + const timeNow = Date.now(); + const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms + const timeAgo = timeNow - timeLength; + + // If it was less than x minutes ago + if (lobby.LastUsed.getTime() > timeAgo) { + const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); + + await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); + return; + } + + lobby.MarkAsUsed(); + await lobby.Save(eLobby, lobby); + + await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@${lobby.RoleId}>`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/remove.ts b/src/commands/501231711271780357/Lobby/remove.ts new file mode 100644 index 0000000..9dac70b --- /dev/null +++ b/src/commands/501231711271780357/Lobby/remove.ts @@ -0,0 +1,39 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../entity/501231711271780357/Lobby"; +import BaseEntity from "../../../contracts/BaseEntity"; + +export default class RemoveLobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('removelobby') + .setDescription('Remove a lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel')); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + + if (!channel || !channel.channel) { + await interaction.reply('Channel is required.'); + return; + } + + const entity = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (!entity) { + await interaction.reply('Channel not found.'); + return; + } + + await BaseEntity.Remove(eLobby, entity); + + await interaction.reply(`Removed <#${channel.channel.name}> from the list of lobby channels`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/entry.ts b/src/commands/501231711271780357/entry.ts index 9f8c92e..fd246ca 100644 --- a/src/commands/501231711271780357/entry.ts +++ b/src/commands/501231711271780357/entry.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import EmbedColours from "../../constants/EmbedColours"; import SettingsHelper from "../../helpers/SettingsHelper"; import { Command } from "../../type/command"; @@ -7,14 +7,10 @@ export default class Entry extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('entry') - .setDescription('Sends the entry embed'); + .setDescription('Sends the entry embed') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/501231711271780357/lobby.ts b/src/commands/501231711271780357/lobby.ts deleted file mode 100644 index d03ba53..0000000 --- a/src/commands/501231711271780357/lobby.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { CommandInteraction, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; -import { Command } from "../../type/command"; -import { default as eLobby } from "../../entity/501231711271780357/Lobby"; -import SettingsHelper from "../../helpers/SettingsHelper"; -import BaseEntity from "../../contracts/BaseEntity"; - -export default class Lobby extends Command { - constructor() { - super(); - - super.Category = "General"; - - super.CommandBuilder = new SlashCommandBuilder() - .setName('lobby') - .setDescription('Attempt to organise a lobby') - .addSubcommand(subcommand => - subcommand - .setName('config') - .setDescription('Configure the lobby command (mods only)') - .addStringOption(option => - option - .setName('action') - .setDescription('Add or remove a channel to the lobby list') - .addChoices( - { name: 'add', value: 'add' }, - { name: 'remove', value: 'remove' }, - )) - .addChannelOption(option => - option - .setName('channel') - .setDescription('The channel')) - .addRoleOption(option => - option - .setName('role') - .setDescription('The role to ping on request')) - .addNumberOption(option => - option - .setName('cooldown') - .setDescription('The cooldown in minutes')) - .addStringOption(option => - option - .setName('name') - .setDescription('The game name'))); - } - - public override async execute(interaction: CommandInteraction) { - if (!interaction.isChatInputCommand()) return; - - switch (interaction.options.getSubcommand()) { - case "config": - await this.Configure(interaction); - break; - case "request": - await this.Request(interaction); - break; - } - } - - private async Request(interaction: CommandInteraction) { - if (!interaction.channelId) return; - - const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); - - if (!lobby) { - await interaction.reply('This channel is disabled from using the lobby command.'); - return; - } - - const timeNow = Date.now(); - const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms - const timeAgo = timeNow - timeLength; - - // If it was less than x minutes ago - if (lobby.LastUsed.getTime() > timeAgo) { - const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); - - await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); - return; - } - - lobby.MarkAsUsed(); - await lobby.Save(eLobby, lobby); - - await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@${lobby.RoleId}>`); - } - - private async Configure(interaction: CommandInteraction) { - if (!interaction.guildId) return; - if (!interaction.member) return; - - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", interaction.guildId); - - const roleManager = interaction.member.roles as GuildMemberRoleManager; - - if (!roleManager.cache.find(x => x.name == moderatorRole)) { - await interaction.reply('Sorry, you must be a moderator to be able to configure this command.'); - return; - } - - const action = interaction.options.get('action'); - - if (!action || !action.value) { - await interaction.reply('Action is required.'); - return; - } - - switch (action.value) { - case "add": - await this.AddLobbyConfig(interaction); - break; - case "remove": - await this.RemoveLobbyConfig(interaction); - break; - default: - await interaction.reply('Action not found.'); - } - } - - private async AddLobbyConfig(interaction: CommandInteraction) { - const channel = interaction.options.get('channel'); - const role = interaction.options.get('role'); - const cooldown = interaction.options.get('cooldown'); - const gameName = interaction.options.get('name'); - - if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { - await interaction.reply('Fields are required.'); - return; - } - - const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); - - if (lobby) { - await interaction.reply('This channel has already been setup.'); - return; - } - - const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); - await entity.Save(eLobby, entity); - - await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes \` and will ping \`${role.name}\` on use`); - } - - private async RemoveLobbyConfig(interaraction: CommandInteraction) { - const channel = interaraction.options.get('channel'); - - if (!channel || !channel.channel) { - await interaraction.reply('Channel is required.'); - return; - } - - const entity = await eLobby.FetchOneByChannelId(channel.channel.id); - - if (!entity) { - await interaraction.reply('Channel not found.'); - return; - } - - await BaseEntity.Remove(eLobby, entity); - - await interaraction.reply(`Removed <#${channel.channel.name}> from the list of lobby channels`); - } -} \ No newline at end of file diff --git a/src/commands/Role/config.ts b/src/commands/Role/config.ts new file mode 100644 index 0000000..93d6891 --- /dev/null +++ b/src/commands/Role/config.ts @@ -0,0 +1,54 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../entity/Role"; +import Server from "../../entity/Server"; + +export default class ConfigRole extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('configrole') + .setDescription('Toggle your roles') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId || !interaction.guild) return; + if (!interaction.member) return; + + const role = interaction.options.get('role'); + + if (!role || !role.role) { + await interaction.reply('Fields are required.'); + return; + } + + const existingRole = await eRole.FetchOneByRoleId(role.role.id); + + if (existingRole) { + await eRole.Remove(eRole, existingRole); + + await interaction.reply('Removed role from configuration.'); + } else { + const server = await Server.FetchOneById(Server, interaction.guildId); + + if (!server) { + await interaction.reply('This server has not been setup.'); + return; + } + + const newRole = new eRole(role.role.id); + newRole.SetServer(server); + + await newRole.Save(eRole, newRole); + + await interaction.reply('Added role to configuration.'); + } + } +} diff --git a/src/commands/role.ts b/src/commands/Role/role.ts similarity index 54% rename from src/commands/role.ts rename to src/commands/Role/role.ts index 8e0eaaa..6e57653 100644 --- a/src/commands/role.ts +++ b/src/commands/Role/role.ts @@ -1,32 +1,20 @@ -import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; -import { Command } from "../type/command"; -import SettingsHelper from "../helpers/SettingsHelper"; -import { default as eRole } from "../entity/Role"; -import EmbedColours from "../constants/EmbedColours"; +import { CommandInteraction, EmbedBuilder, GuildMember, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../entity/Role"; +import EmbedColours from "../../constants/EmbedColours"; export default class Role extends Command { constructor() { super(); - super.Category = "General"; - super.CommandBuilder = new SlashCommandBuilder() .setName('role') .setDescription('Toggle your roles') - .addSubcommand(subcommand => - subcommand - .setName('config') - .setDescription('Configure the roles') - .addStringOption(option => - option - .setName('role') - .setDescription('The role name') - .setRequired(true))) .addSubcommand(subcommand => subcommand .setName('toggle') .setDescription('Toggle your role') - .addStringOption(option => + .addRoleOption(option => option .setName('role') .setDescription('The role name') @@ -41,9 +29,6 @@ export default class Role extends Command { if (!interaction.isChatInputCommand()) return; switch (interaction.options.getSubcommand()) { - case 'config': - await this.Configure(interaction); - break; case 'toggle': await this.ToggleRole(interaction); break; @@ -73,28 +58,30 @@ export default class Role extends Command { const roles = await this.GetRolesList(interaction); const requestedRole = interaction.options.get('role'); - if (!requestedRole || !requestedRole.value) { + if (!requestedRole || !requestedRole.role) { await interaction.reply('Fields are required.'); return; } - if (!roles.includes(requestedRole.value.toString())) { + if (!roles.includes(requestedRole.role.name)) { await interaction.reply('This role isn\'t marked as assignable.'); return; } - const assignRole = interaction.guild.roles.cache.find(x => x.name == requestedRole.value); + const roleManager = interaction.member.roles as GuildMemberRoleManager; + const member = interaction.member as GuildMember; - if (!assignRole) { - await interaction.reply('The current server doesn\'t have this role. Please contact the server\'s moderators'); + const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name); + const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id); + + if (!assignRole) return; + + if (!assignRole.editable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); return; } - const roleManager = interaction.member.roles as GuildMemberRoleManager; - - const role = roleManager.cache.find(x => x.name == requestedRole.value); - - if (!role) { + if (!userRole) { await roleManager.add(assignRole); await interaction.reply(`Gave role: \`${assignRole.name}\``); } else { @@ -103,44 +90,6 @@ export default class Role extends Command { } } - private async Configure(interaction: CommandInteraction) { - if (!interaction.guildId || !interaction.guild) return; - if (!interaction.member) return; - - const roleName = interaction.options.get('role'); - - if (!roleName || !roleName.value) { - await interaction.reply('Fields are required.'); - return; - } - - const roleManager = interaction.member.roles as GuildMemberRoleManager; - - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", interaction.guildId); - - if (!roleManager.cache.find(x => x.name == moderatorRole)) { - await interaction.reply('Sorry, you must be a moderator to be able to configure this command.'); - return; - } - - const role = interaction.guild.roles.cache.find(x => x.name == roleName.value); - - if (!role) { - await interaction.reply('Unable to find role.'); - return; - } - - const existingRole = await eRole.FetchOneByRoleId(role.id); - - if (existingRole) { - await eRole.Remove(eRole, existingRole); - await interaction.reply('Removed role from configuration.'); - } else { - const newRole = new eRole(role.id); - await newRole.Save(eRole, newRole); - } - } - private async GetRolesList(interaction: CommandInteraction): Promise { if (!interaction.guildId || !interaction.guild) return []; diff --git a/src/commands/about.ts b/src/commands/about.ts index 30fb6ad..b1ad17b 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -5,7 +5,6 @@ import { Command } from "../type/command"; export default class About extends Command { constructor() { super(); - super.Category = "General"; super.CommandBuilder = new SlashCommandBuilder() .setName('about') diff --git a/src/commands/audits.ts b/src/commands/audits.ts index a88f32f..8c87397 100644 --- a/src/commands/audits.ts +++ b/src/commands/audits.ts @@ -1,7 +1,7 @@ import Audit from "../entity/Audit"; import AuditTools from "../helpers/AuditTools"; import { Command } from "../type/command"; -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { AuditType } from "../constants/AuditType"; import EmbedColours from "../constants/EmbedColours"; @@ -9,14 +9,10 @@ export default class Audits extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("audits") .setDescription("View audits of a particular user in the server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addSubcommand(subcommand => subcommand .setName('user') diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 37ca2a6..a7f2d99 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -8,11 +8,6 @@ import SettingsHelper from "../helpers/SettingsHelper"; export default class Ban extends Command { constructor() { super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; super.CommandBuilder = new SlashCommandBuilder() .setName("ban") @@ -60,6 +55,11 @@ export default class Ban extends Command { }, ]); + if (!member.bannable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await member.ban(); await interaction.reply(`\`${targetUser.user.tag}\` has been banned.`); diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts index cf28dff..b735f61 100644 --- a/src/commands/bunny.ts +++ b/src/commands/bunny.ts @@ -7,8 +7,6 @@ export default class Bunny extends Command { constructor() { super(); - super.Category = "Fun"; - super.CommandBuilder = new SlashCommandBuilder() .setName("bunny") .setDescription("Get a random picture of a rabbit."); diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 160ec07..981c847 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -1,18 +1,14 @@ -import { CommandInteraction, SlashCommandBuilder, TextChannel } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { Command } from "../type/command"; export default class Clear extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("clear") .setDescription("Clears the channel of messages") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages) .addNumberOption(option => option .setName('count') @@ -23,6 +19,7 @@ export default class Clear extends Command { public override async execute(interaction: CommandInteraction) { if (!interaction.isChatInputCommand()) return; + if (!interaction.channel) return; const totalToClear = interaction.options.getNumber('count'); @@ -32,6 +29,12 @@ export default class Clear extends Command { } const channel = interaction.channel as TextChannel; + + if (!channel.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await channel.bulkDelete(totalToClear); await interaction.reply(`${totalToClear} message(s) were removed.`); diff --git a/src/commands/code.ts b/src/commands/code.ts index f3fed94..2f0f7e2 100644 --- a/src/commands/code.ts +++ b/src/commands/code.ts @@ -1,5 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; -import { CommandResponse } from "../constants/CommandResponse"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { Command } from "../type/command"; @@ -8,14 +7,10 @@ export default class Code extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('code') .setDescription('Manage the verification code of the server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addSubcommand(subcommand => subcommand .setName('randomise') @@ -26,23 +21,6 @@ export default class Code extends Command { .setDescription('Sends the embed with the current code to the current channel')); } - public override async precheckAsync(interaction: CommandInteraction): Promise { - if (!interaction.isChatInputCommand()) return CommandResponse.NotInServer; - if (!interaction.guild || !interaction.guildId) return CommandResponse.NotInServer; - - const isEnabled = await SettingsHelper.GetSetting("verification.enabled", interaction.guildId); - - if (!isEnabled) { - return CommandResponse.FeatureDisabled; - } - - if (isEnabled.toLocaleLowerCase() != 'true') { - return CommandResponse.FeatureDisabled; - } - - return CommandResponse.Ok; - } - public override async execute(interaction: CommandInteraction) { if (!interaction.isChatInputCommand()) return; diff --git a/src/commands/config.ts b/src/commands/config.ts index 60cd73d..b4887ea 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,6 +1,5 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { readFileSync } from "fs"; -import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; import EmbedColours from "../constants/EmbedColours"; import Server from "../entity/Server"; @@ -10,14 +9,11 @@ import { Command } from "../type/command"; export default class Config extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "administrator" - ] super.CommandBuilder = new SlashCommandBuilder() .setName('config') .setDescription('Configure the current server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .addSubcommand(subcommand => subcommand .setName('reset') @@ -52,23 +48,19 @@ export default class Config extends Command { .setDescription('Lists all settings')) } - public override async precheckAsync(interaction: CommandInteraction): Promise { - if (!interaction.guildId) return CommandResponse.ServerNotSetup; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; const server = await Server.FetchOneById(Server, interaction.guildId, [ "Settings", ]); if (!server) { - return CommandResponse.ServerNotSetup; + await interaction.reply('Server not setup. Please use the setup command,'); + return; } - return CommandResponse.Ok; - } - - public override async execute(interaction: CommandInteraction) { - if (!interaction.isChatInputCommand()) return; - switch (interaction.options.getSubcommand()) { case 'list': await this.SendHelpText(interaction); diff --git a/src/commands/disable.ts b/src/commands/disable.ts index 4ad4af8..4cca622 100644 --- a/src/commands/disable.ts +++ b/src/commands/disable.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; @@ -6,14 +6,10 @@ export default class Disable extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('disable') .setDescription('Disables a command') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .addSubcommand(subcommand => subcommand .setName('add') diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts index bb5124e..2367762 100644 --- a/src/commands/ignore.ts +++ b/src/commands/ignore.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import IgnoredChannel from "../entity/IgnoredChannel"; import { Command } from "../type/command"; @@ -6,14 +6,10 @@ export default class Ignore extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('ignore') - .setDescription('Ignore events in this channel'); + .setDescription('Ignore events in this channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 8a520d6..6ddee0c 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -8,11 +8,6 @@ import SettingsHelper from "../helpers/SettingsHelper"; export default class Kick extends Command { constructor() { super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; super.CommandBuilder = new SlashCommandBuilder() .setName("kick") @@ -59,6 +54,11 @@ export default class Kick extends Command { value: reason, }, ]); + + if (!member.kickable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } await member.kick(); await interaction.reply(`\`${targetUser.user.tag}\` has been kicked.`); diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 312eff3..d3b1493 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -9,15 +9,10 @@ export default class Mute extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("mute") .setDescription("Mute a member in the server with an optional reason") - .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addUserOption(option => option .setName('target') @@ -65,6 +60,11 @@ export default class Mute extends Command { return; } + if (!targetMember.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await targetMember.roles.add(mutedRole); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 180f779..f6a6406 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { existsSync, readFileSync } from "fs"; import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; @@ -14,14 +14,10 @@ export default class Rules extends Command { constructor() { super(); - super.Category = "Admin"; - super.Roles = [ - "administrator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("rules") - .setDescription("Send the rules embeds for this server"); + .setDescription("Send the rules embeds for this server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 63a0fc8..6676ef8 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -1,18 +1,15 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import Server from "../entity/Server"; import { Command } from "../type/command"; export default class Setup extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "moderator" - ] super.CommandBuilder = new SlashCommandBuilder() .setName('setup') - .setDescription('Makes the server ready to be configured'); + .setDescription('Makes the server ready to be configured') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index c40360c..59817f8 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -7,15 +7,10 @@ export default class Unmute extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("unmute") .setDescription("Unmute a member in the server with an optional reason") - .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addUserOption(option => option .setName('target') @@ -63,6 +58,11 @@ export default class Unmute extends Command { return; } + if (!targetMember.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await targetMember.roles.remove(mutedRole); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); diff --git a/src/commands/warn.ts b/src/commands/warn.ts index 1280983..5a41985 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -9,15 +9,10 @@ export default class Warn extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("warn") .setDescription("Warns a member in the server with an optional reason") - .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addUserOption(option => option .setName('target') diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts deleted file mode 100644 index b876ce8..0000000 --- a/src/constants/CommandResponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum CommandResponse { - Ok, - Unauthorised, - ServerNotSetup, - NotInServer, - FeatureDisabled, -} \ No newline at end of file diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts deleted file mode 100644 index 588a143..0000000 --- a/src/constants/ErrorMessages.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CommandResponse } from "./CommandResponse"; - -export default class ErrorMessages { - public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?"; - public static readonly ChannelNotFound = "Unable to find channel"; - public static readonly RoleNotFound = "Unable to find role"; - - public static readonly UserUnauthorised = "You are not authorised to use this command"; - public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; - public static readonly NotInServer = "This command requires to be ran inside of a server"; - public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator"; - - public static GetErrorMessage(response: CommandResponse): string { - switch (response) { - case CommandResponse.Unauthorised: - return this.UserUnauthorised; - case CommandResponse.ServerNotSetup: - return this.ServerNotSetup; - case CommandResponse.NotInServer: - return this.NotInServer; - case CommandResponse.FeatureDisabled: - return this.FeatureDisabled; - default: - return ""; - } - } -} \ No newline at end of file diff --git a/src/entity/Role.ts b/src/entity/Role.ts index e4b2da7..954c208 100644 --- a/src/entity/Role.ts +++ b/src/entity/Role.ts @@ -15,6 +15,10 @@ export default class Role extends BaseEntity { @ManyToOne(() => Server, x => x.Roles) Server: Server; + + public SetServer(server: Server) { + this.Server = server; + } public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise { const connection = getConnection(); diff --git a/src/registry.ts b/src/registry.ts index 0671523..a83e7fa 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -11,7 +11,8 @@ import Disable from "./commands/disable"; import Ignore from "./commands/ignore"; import Kick from "./commands/kick"; import Mute from "./commands/mute"; -import Role from "./commands/role"; +import Role from "./commands/Role/role"; +import ConfigRole from "./commands/Role/config"; import Rules from "./commands/rules"; import Setup from "./commands/setup"; import Unmute from "./commands/unmute"; @@ -19,7 +20,9 @@ import Warn from "./commands/warn"; // Command Imports: MankBot import Entry from "./commands/501231711271780357/entry"; -import Lobby from "./commands/501231711271780357/lobby"; +import Lobby from "./commands/501231711271780357/Lobby/lobby"; +import AddLobby from "./commands/501231711271780357/Lobby/add"; +import RemoveLobby from "./commands/501231711271780357/Lobby/remove"; // Event Imports import MemberEvents from "./events/MemberEvents"; @@ -38,19 +41,25 @@ export default class Registry { CoreClient.RegisterCommand("ignore", new Ignore()); CoreClient.RegisterCommand("kick", new Kick()); CoreClient.RegisterCommand("mute", new Mute()); - CoreClient.RegisterCommand("role", new Role()); CoreClient.RegisterCommand("rules", new Rules()); CoreClient.RegisterCommand("unmute", new Unmute()); CoreClient.RegisterCommand("warn", new Warn()); CoreClient.RegisterCommand("setup", new Setup()); CoreClient.RegisterCommand("audits", new Audits()); + CoreClient.RegisterCommand("role", new Role()); + CoreClient.RegisterCommand("configrole", new ConfigRole()); + // Exclusive Commands: MankBot CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyAdd", new AddLobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyRemove", new RemoveLobby(), "501231711271780357"); CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357"); // Add Exclusive Commands to Test Server CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002"); + CoreClient.RegisterCommand("addlobby", new AddLobby(), "442730357897429002"); + CoreClient.RegisterCommand("removelobby", new RemoveLobby(), "442730357897429002"); CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002"); } diff --git a/src/type/command.ts b/src/type/command.ts index 6fea8c3..10d091d 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,23 +1,7 @@ -import { CommandResponse } from "../constants/CommandResponse"; import { CommandInteraction } from "discord.js"; export class Command { public CommandBuilder: any; - - public Roles: string[]; - public Category?: string; - - constructor() { - this.Roles = []; - } - - public precheck(interation: CommandInteraction): CommandResponse { - return CommandResponse.Ok; - } - - public async precheckAsync(interation: CommandInteraction): Promise { - return CommandResponse.Ok; - } public execute(interaction: CommandInteraction) {