Add command permission checks

Level 0: Every user - Public commands
Level 1: Guild owners or members with respective admin role - Elevated guild commands
Level 2: Global admins - Every command including levels below
This commit is contained in:
2022-08-19 19:18:01 +02:00
parent 6a7e3f6647
commit eb4ffae173
8 changed files with 147 additions and 4 deletions

View File

@@ -13,7 +13,7 @@ module.exports = {
.setDescription("The command to debug") .setDescription("The command to debug")
.setRequired(false) .setRequired(false)
), ),
permissionLevel: 2,
async execute(interaction) { async execute(interaction) {
const identifier = CardUtils.generateIdentifier(); const identifier = CardUtils.generateIdentifier();
let user = await UserUtils.getUserByDiscordId(interaction.member.id); let user = await UserUtils.getUserByDiscordId(interaction.member.id);
@@ -70,6 +70,13 @@ module.exports = {
content: `Reset cooldowns`, content: `Reset cooldowns`,
ephemeral: false ephemeral: false
}); });
break;
default:
interaction.reply({
content: `Your permission level is ${await UserUtils.getPermissionLevel(interaction.member)}`,
ephemeral: false
});
break;
} }
} }
} }

View File

@@ -12,7 +12,7 @@ module.exports = {
.setDescription("The id of the character to drop") .setDescription("The id of the character to drop")
.setRequired(true) .setRequired(true)
), ),
permissionLevel: 2,
async execute(interaction) { async execute(interaction) {
//get user id from database given the userID //get user id from database given the userID
const user = await User.findOne({ const user = await User.findOne({

61
commands/guildSettings.js Normal file
View File

@@ -0,0 +1,61 @@
const { SlashCommandBuilder, ComponentType, ActionRowBuilder, ButtonBuilder, ButtonStyle, SelectMenuBuilder } = require("discord.js");
const { GeneralUtils, GuildUtils } = require("../util");
module.exports = {
data: new SlashCommandBuilder()
.setName("settings")
.setDescription("Change Guild Settings"),
permissionLevel: 1,
async execute(interaction) {
let reply = "Guild settings:\n";
const row = new ActionRowBuilder();
row.addComponents(
new ButtonBuilder()
.setCustomId(`set-admin-role`)
.setLabel('Set Admin Role')
.setStyle(ButtonStyle.Primary)
);
const message = await interaction.reply({ content: reply, components: [row], fetchReply: true });
const filter = (m) => m.author.id === message.author.id;
const collector = message.createMessageComponentCollector(filter, { componentType: ComponentType.Button, time: 120000 });
//BUGBUG: end callback does not trigger
collector.on('end', collected => {
console.log(`Collected ${collected.size} interactions.`);
message.edit({ components: [] });
});
collector.on('collect', async m => {
switch (m.customId) {
case 'set-admin-role':
//Build select menu with every role in the guild
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
.setCustomId('select')
.setPlaceholder('Nothing selected')
.addOptions(
m.guild.roles.cache.map(role => ({
label: role.name,
description: role.id,
value: role.id
}))
),
);
const message = await m.reply({ content: 'Select a role', components: [row], fetchReply: true });
const filter = (m) => m.author.id === message.author.id;
const replyCollector = message.createMessageComponentCollector(filter, { componentType: ComponentType.SelectMenu, time: 25000 });
replyCollector.on('collect', async r => {
const role = m.guild.roles.cache.find(role => role.id === r.values[0]);
m.deleteReply(); //Delete select menu message
GuildUtils.setProperty(m.guild.id, 'adminRoleId', role.id);
r.reply({ content: `Selected role: ${role.name} \n(${role.id})`, components: [] });
});
break;
}
});
}
}

View File

@@ -4,6 +4,7 @@ module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName("ping") .setName("ping")
.setDescription("Ping, yes"), .setDescription("Ping, yes"),
permissionLevel: 1,
async execute(interaction) { async execute(interaction) {
interaction.reply({ interaction.reply({
content: "Pong!", content: "Pong!",

View File

@@ -38,6 +38,18 @@ module.exports = {
const command = interaction.client.commands.get(interaction.commandName); const command = interaction.client.commands.get(interaction.commandName);
//check if user has permissions to run the command
//TODO: pass down this user object to avoid duplicate queries
let user = await UserUtils.getUserByDiscordId(interaction.member.id);
let permissionLevel = await UserUtils.getPermissionLevel(interaction.member);
if (command.permissionLevel > permissionLevel) {
interaction.reply({
content: `You do not have permission to run this command`,
ephemeral: true
});
return;
}
if (!command) return; if (!command) return;
try { try {

View File

@@ -5,5 +5,11 @@ module.exports = {
getBotProperty: async function(property) { getBotProperty: async function(property) {
let bot = await Bot.findOne(); let bot = await Bot.findOne();
return property ? bot[property] : bot; return property ? bot[property] : bot;
},
setBotProperty: async function(property, value) {
let bot = await Bot.findOne();
bot[property] = value;
await bot.save();
} }
} }

23
util/guilds.js Normal file
View File

@@ -0,0 +1,23 @@
const { Guild } = require("../models");
module.exports = {
name: "GuildUtils",
getProperty: async function(guildId, property) {
let guild = await Guild.findOne({
where: {
guildId: guildId
}
});
return property ? guild[property] : guild;
},
setProperty: async function(guildId, property, value) {
let guild = await Guild.findOne({
where: {
guildId: guildId
}
});
guild[property] = value;
await guild.save();
}
}

View File

@@ -1,4 +1,5 @@
const { User } = require("../models"); const { User, Guild } = require("../models");
const GeneralUtils = require("./general");
module.exports = { module.exports = {
name: "UserUtils", name: "UserUtils",
@@ -65,5 +66,37 @@ module.exports = {
let newCooldown = new Date(new Date().getTime() + cooldown); let newCooldown = new Date(new Date().getTime() + cooldown);
user[`next${cooldownType[0].toUpperCase() + cooldownType.slice(1)}`] = newCooldown; user[`next${cooldownType[0].toUpperCase() + cooldownType.slice(1)}`] = newCooldown;
await user.save(); await user.save();
},
getPermissionLevel: async function(user) {
/* THIS FUNCTION EXPECTS A DISCORD USER INSTANCE!
* Returns the permission level of the user
* 0 - no permissions
* 1 - guild permissions
* 2 - admin permissions
*/
let guild = await Guild.findOne({
where: {
guildId: user.guild.id
}
});
//Global Admin
let adminIDs = await GeneralUtils.getBotProperty("adminIDs");
if (adminIDs.includes(user.id)) {
return 2;
}
//Guild Admin if role is present
if(user._roles.includes(String(guild.adminRoleId))) {
return 1;
}
//or if user is owner
if(user.guild.ownerId === user.id) {
return 1;
}
//Regular User
return 0;
} }
} }