From 6bf06e2391cb3930dcd1b380bacd20bcb3269c21 Mon Sep 17 00:00:00 2001
From: Patric Plattner <patric@patricplattner.de>
Date: Mon, 30 Aug 2021 02:18:03 +0200
Subject: [PATCH] Adding permission system.

---
 .dockerignore                |  3 +-
 .gitignore                   |  3 +-
 .idea/finf-discord-js.iml    |  1 +
 commands/AssignPermission.js | 14 +++++++
 commands/ListPermissions.js  |  5 +++
 commands/RevokePermission.js | 14 +++++++
 components/permConfig.js     | 77 ++++++++++++++++++++++++++++++++++++
 components/roles.js          | 22 +++++++++++
 docker-compose.yml           |  2 +
 index.js                     |  2 +-
 models/ServerConfig.js       | 16 ++++++++
 package.json                 |  2 +-
 {config => util}/db.js       |  0
 util/perm.js                 | 17 ++++++++
 14 files changed, 174 insertions(+), 4 deletions(-)
 create mode 100644 commands/AssignPermission.js
 create mode 100644 commands/ListPermissions.js
 create mode 100644 commands/RevokePermission.js
 create mode 100644 components/permConfig.js
 create mode 100644 models/ServerConfig.js
 rename {config => util}/db.js (100%)
 create mode 100644 util/perm.js

diff --git a/.dockerignore b/.dockerignore
index 2afd11a..45ec588 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -3,4 +3,5 @@ node_modules
 npm-debug.log
 .env
 .env.example
-.DS_store
\ No newline at end of file
+.DS_store
+mongo
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c223885..8a59002 100644
--- a/.gitignore
+++ b/.gitignore
@@ -235,4 +235,5 @@ dist
 .pnp.*
 
 .env
-.DS_store
\ No newline at end of file
+.DS_store
+mongo
\ No newline at end of file
diff --git a/.idea/finf-discord-js.iml b/.idea/finf-discord-js.iml
index 0c8867d..1616246 100644
--- a/.idea/finf-discord-js.iml
+++ b/.idea/finf-discord-js.iml
@@ -5,6 +5,7 @@
       <excludeFolder url="file://$MODULE_DIR$/temp" />
       <excludeFolder url="file://$MODULE_DIR$/.tmp" />
       <excludeFolder url="file://$MODULE_DIR$/tmp" />
+      <excludeFolder url="file://$MODULE_DIR$/mongo" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
diff --git a/commands/AssignPermission.js b/commands/AssignPermission.js
new file mode 100644
index 0000000..b2009c4
--- /dev/null
+++ b/commands/AssignPermission.js
@@ -0,0 +1,14 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+
+module.exports = new SlashCommandBuilder()
+    .setName('assign_permission')
+    .setDescription('Assign a permission to a role')
+    .addStringOption(option =>
+        option.setName('permission')
+            .setDescription('Permission to assign.')
+            .setRequired(true)
+            .addChoice("Role Menus", 'role_menu'))
+    .addRoleOption(option =>
+        option.setName('role')
+	        .setDescription('Role to assign permission to.')
+            .setRequired(true))
\ No newline at end of file
diff --git a/commands/ListPermissions.js b/commands/ListPermissions.js
new file mode 100644
index 0000000..b6d35be
--- /dev/null
+++ b/commands/ListPermissions.js
@@ -0,0 +1,5 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+
+module.exports = new SlashCommandBuilder()
+    .setName('list_permissions')
+    .setDescription('List all Permissions for this Server.');
\ No newline at end of file
diff --git a/commands/RevokePermission.js b/commands/RevokePermission.js
new file mode 100644
index 0000000..45b18c4
--- /dev/null
+++ b/commands/RevokePermission.js
@@ -0,0 +1,14 @@
+const { SlashCommandBuilder } = require('@discordjs/builders');
+
+module.exports = new SlashCommandBuilder()
+    .setName('revoke_permission')
+    .setDescription('Revoke a permission from a role')
+    .addStringOption(option =>
+        option.setName('permission')
+            .setDescription('Permission to revoke.')
+            .setRequired(true)
+            .addChoice("Role Menus", 'role_menu'))
+    .addRoleOption(option =>
+        option.setName('role')
+            .setDescription('Role to revoke permission from.')
+            .setRequired(true))
\ No newline at end of file
diff --git a/components/permConfig.js b/components/permConfig.js
new file mode 100644
index 0000000..4235839
--- /dev/null
+++ b/components/permConfig.js
@@ -0,0 +1,77 @@
+const ServerConfig = require('../models/ServerConfig');
+const {Permissions} = require('discord.js');
+
+async function assignPermission(interaction) {
+    if (interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) {
+        const role = interaction.options.getRole('role');
+        const perm = interaction.options.getString('permission');
+        let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()});
+        if (config == null) {
+            config = new ServerConfig();
+            config.guildID = interaction.guild.id.toString();
+            config.permissions.roleMenu = [];
+        }
+        if (perm === 'role_menu') {
+            if (!config.permissions.roleMenu.includes(role.id.toString())) {
+                config.permissions.roleMenu.push(role.id.toString());
+            }
+        }
+        await config.save();
+        await interaction.reply({content: `Role ${role.name} has been added to ${perm} permission.`, ephemeral: true});
+    } else {
+        await interaction.reply({content: 'Only administrators can access this command.', ephemeral: true});
+    }
+}
+
+async function revokePermission(interaction) {
+    if (interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) {
+        const role = interaction.options.getRole('role');
+        const perm = interaction.options.getString('permission');
+        let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()});
+        if (config == null) {
+            config = new ServerConfig();
+            config.guildID = interaction.guild.id.toString();
+            config.permissions.roleMenu = [];
+        }
+        if (perm === 'role_menu') {
+            if (config.permissions.roleMenu.includes(role.id.toString())) {
+                config.permissions.roleMenu.splice(config.permissions.roleMenu.indexOf(role.id.toString()), 1);
+            }
+        }
+        await config.save();
+        await interaction.reply({
+            content: `Role ${role.name} has been removed from ${perm} permission.`,
+            ephemeral: true
+        });
+    } else {
+        await interaction.reply({content: 'Only administrators can access this command.', ephemeral: true});
+    }
+}
+
+async function listPermissions(interaction) {
+    if (interaction.member.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) {
+        let config = await ServerConfig.findOne({guildID: interaction.guild.id.toString()});
+        if (config == null) {
+            config = new ServerConfig();
+            config.guildID = interaction.guild.id.toString();
+            config.permissions.roleMenu = [];
+        }
+        const roles = await Promise.all(config.permissions.roleMenu.map(async (roleID) => (await interaction.guild.roles.fetch(roleID)).name));
+
+        await interaction.reply({content: `List of Permissions:\n\nRole Menu: ${roles.join(',')}`, ephemeral: true});
+    }
+}
+
+const slashCommandHandler = async (interaction) => {
+    if (interaction.commandName === 'assign_permission') {
+        await assignPermission(interaction);
+    } else if (interaction.commandName === 'revoke_permission') {
+        await revokePermission(interaction);
+    } else if (interaction.commandName === 'list_permissions') {
+        await listPermissions(interaction);
+    }
+}
+
+module.exports = {
+    slashCommandHandler
+}
\ No newline at end of file
diff --git a/components/roles.js b/components/roles.js
index 6721134..0968774 100644
--- a/components/roles.js
+++ b/components/roles.js
@@ -1,8 +1,14 @@
 const RoleMenu = require('../models/RoleMenu');
 const {MessageActionRow, MessageSelectMenu} = require("discord.js");
+const checkPerm = require('../util/perm');
 
 // slashcommand handler
 async function addRoleMenu(interaction) {
+    console.log('test');
+    if (!(await checkPerm(interaction.member, 'role_menu'))) {
+        await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true})
+        return;
+    }
     const channel = interaction.options.getChannel('channel');
     const messageContent = interaction.options.getString('message');
     const type = interaction.options.getString('type');
@@ -32,6 +38,10 @@ async function addRoleMenu(interaction) {
 }
 
 async function deleteRoleMenu(interaction) {
+    if (!(await checkPerm(interaction.member, 'role_menu'))) {
+        await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true})
+        return;
+    }
     const channel = interaction.options.getChannel('channel');
     const messageID = interaction.options.getString('message_id');
     let menu = await RoleMenu.findOne({guildID: channel.guild.id, channelID: channel.id, messageID});
@@ -58,6 +68,10 @@ async function deleteRoleMenu(interaction) {
 }
 
 async function addRoleToMenu(interaction) {
+    if (!(await checkPerm(interaction.member, 'role_menu'))) {
+        await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true})
+        return;
+    }
     const channel = interaction.options.getChannel('channel');
     const messageID = interaction.options.getString('message_id');
     const role = interaction.options.getRole('role')
@@ -123,6 +137,10 @@ async function addRoleToMenu(interaction) {
 }
 
 async function deleteRoleFromMenu(interaction) {
+    if (!(await checkPerm(interaction.member, 'role_menu'))) {
+        await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true})
+        return;
+    }
     const channel = interaction.options.getChannel('channel');
     const messageID = interaction.options.getString('message_id');
     const role = interaction.options.getRole('role');
@@ -183,6 +201,10 @@ async function deleteRoleFromMenu(interaction) {
 }
 
 async function editRoleMenu(interaction) {
+    if (!(await checkPerm(interaction.member, 'role_menu'))) {
+        await interaction.reply({content: 'You are not authorized to do this.', ephemeral: true})
+        return;
+    }
     const channel = interaction.options.getChannel('channel');
     const messageID = interaction.options.getString('message_id');
     const messageContent = interaction.options.getString('message');
diff --git a/docker-compose.yml b/docker-compose.yml
index 43cb997..ae04fab 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -18,6 +18,8 @@ services:
 
   mongo:
     image: mongo
+    volumes:
+      - "./mongo:/data/db"
     ports:
       - "${MONGO_PORT}:${MONGO_PORT}"
     restart: always
diff --git a/index.js b/index.js
index 49b1841..65968dd 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,4 @@
-const connectDB = require('./config/db')
+const connectDB = require('./util/db')
 const {Client, Intents, MessageActionRow, MessageButton, MessageSelectMenu, MessageEmbed} = require('discord.js');
 const fs = require('fs');
 const botConfig = {
diff --git a/models/ServerConfig.js b/models/ServerConfig.js
new file mode 100644
index 0000000..1428e8a
--- /dev/null
+++ b/models/ServerConfig.js
@@ -0,0 +1,16 @@
+const {Schema, model} = require('mongoose');
+
+const ServerConfigSchema = new Schema({
+    guildID: {
+        type: String,
+        unique: true,
+        required: true
+    },
+    permissions: {
+    roleMenu: [{
+        type: String
+    }]}
+});
+
+
+module.exports = model('serverconfig', ServerConfigSchema);
\ No newline at end of file
diff --git a/package.json b/package.json
index 420b0a1..a5d606b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "finf-discord",
-  "version": "0.1.0",
+  "version": "0.1.1",
   "description": "",
   "main": "index.ts",
   "scripts": {
diff --git a/config/db.js b/util/db.js
similarity index 100%
rename from config/db.js
rename to util/db.js
diff --git a/util/perm.js b/util/perm.js
new file mode 100644
index 0000000..7cf3f0f
--- /dev/null
+++ b/util/perm.js
@@ -0,0 +1,17 @@
+const ServerConfig = require('../models/ServerConfig');
+const {Permissions} = require('discord.js');
+
+module.exports = async (member, permission) => {
+    const guildID = member.guild.id;
+    const config = await ServerConfig.findOne({guildID});
+    if (config == null) {
+        return false;
+    }
+    if (permission === 'role_menu') {
+        console.log(member.roles.cache);
+        console.log(config.permissions.roleMenu);
+        return member.roles.cache.some((role) => config.permissions.roleMenu.some((prole) => prole == role.id.toString()))
+            || member.permissions.has(Permissions.FLAGS.ADMINISTRATOR);
+    }
+    return false;
+}
\ No newline at end of file
-- 
GitLab