From 46506844a92e18a2161c1529c5c048d19b2ca80b Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Thu, 30 Nov 2023 18:47:10 +0100 Subject: [PATCH 1/6] Add constant for internal guild roles --- src/models/bot_roles.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/models/bot_roles.ts b/src/models/bot_roles.ts index 6f20eb9..4243bf4 100644 --- a/src/models/bot_roles.ts +++ b/src/models/bot_roles.ts @@ -10,6 +10,8 @@ export enum InternalRoles { BOT_ADMIN = "bot_admin", } +export const InternalGuildRoles = [InternalRoles.SERVER_OWNER, InternalRoles.SERVER_ADMIN, InternalRoles.TUTOR, InternalRoles.VERIFIED, InternalRoles.ACTIVE_SESSION]; + export enum RoleScopes { GLOBAL = "global", SERVER = "server", From d2618f1df47e984ec48e17f4a46bc11e6aa8ae35 Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Thu, 30 Nov 2023 18:52:58 +0100 Subject: [PATCH 2/6] improve fixverify --- src/commands/admin/fixverify.ts | 81 ++++++++------------------------- 1 file changed, 20 insertions(+), 61 deletions(-) diff --git a/src/commands/admin/fixverify.ts b/src/commands/admin/fixverify.ts index eea9706..5e79066 100644 --- a/src/commands/admin/fixverify.ts +++ b/src/commands/admin/fixverify.ts @@ -3,9 +3,9 @@ import { Command } from "../../../typings"; import "moment-duration-format"; import { UserModel } from "../../models/users"; import { GuildModel } from "../../models/guilds"; -import { DBRole, InternalRoles, RoleScopes } from "../../models/bot_roles"; +import { DBRole } from "../../models/bot_roles"; import { Types } from "mongoose"; -import { DocumentType } from "@typegoose/typegoose"; +import { ArraySubDocumentType } from "@typegoose/typegoose"; @@ -15,7 +15,7 @@ import { DocumentType } from "@typegoose/typegoose"; const command: Command = { name: "fixverify", guildOnly: false, - description: "Temp Command that fixes the Verify DB.", + description: "This command updates the db roles for all users based on their guild roles.", async execute(client, interaction, args) { if (!interaction || !interaction.guild) { return; @@ -26,78 +26,37 @@ const command: Command = { } await interaction.deferReply(); const guild = interaction.guild; - const roles = await interaction.guild.roles.fetch(); const members = await interaction.guild.members.fetch(); - const dbGuild = (await GuildModel.findOne({ _id: guild.id }))!; - const verifiedRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "verified"); - let dbVerifyRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.VERIFIED); - const orgaRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "orga"); - let dbOrgaRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.SERVER_ADMIN); - const tutorRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "tutor"); - let dbTutorRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.TUTOR); - const activeSessionRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "active_session"); - let dbActiveSessionRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.ACTIVE_SESSION); - - // Create the roles if they don't exist - for (const [r, dbr, irn] of ([[verifiedRole, dbVerifyRole, InternalRoles.VERIFIED], [orgaRole, dbOrgaRole, InternalRoles.SERVER_ADMIN], [tutorRole, dbTutorRole, InternalRoles.TUTOR], [activeSessionRole, dbActiveSessionRole, InternalRoles.ACTIVE_SESSION]] as [Role, DocumentType, InternalRoles][])) { - if (!r) continue; - if (!dbr) { - console.log(`creating role ${irn}`); - if (!dbGuild.guild_settings.roles) dbGuild.guild_settings.roles = new Types.DocumentArray([]); - dbGuild.guild_settings.roles.push({ - internal_name: irn, - role_id: r.id, - scope: RoleScopes.SERVER, - server_id: guild.id, - server_role_name: r.name, - }); - await dbGuild.save(); - if(irn === InternalRoles.VERIFIED) { - dbVerifyRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } else if(irn === InternalRoles.SERVER_ADMIN) { - dbOrgaRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } else if(irn === InternalRoles.TUTOR) { - dbTutorRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } else if(irn === InternalRoles.ACTIVE_SESSION) { - dbActiveSessionRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } - } + const dbGuild = await GuildModel.findById(guild.id); + if(!dbGuild) { + return await client.utils.embeds.SimpleEmbed(interaction, "Adminstration", "Guild not found in Database."); } - + const dbRoles = dbGuild.guild_settings.roles; + if (!dbRoles) { + return await client.utils.embeds.SimpleEmbed(interaction, "Adminstration", "No Roles found in Database."); + } + const roles = new Map(await Promise.all(dbRoles.map(async (x) => [x, await interaction.guild?.roles.resolve(x.role_id!) ?? null] as [ArraySubDocumentType, Role | null]))); const users = await UserModel.find({}); + let count = 0; + let updatedCount = 0; for (const u of users) { console.log(`(${++count}/${users.length}) updating roles for user ${u.tu_id}`); - for (const [r, dbr, irn] of ([[verifiedRole, dbVerifyRole, InternalRoles.VERIFIED], [orgaRole, dbOrgaRole, InternalRoles.SERVER_ADMIN], [tutorRole, dbTutorRole, InternalRoles.TUTOR], [activeSessionRole, dbActiveSessionRole, InternalRoles.ACTIVE_SESSION]] as [Role, DocumentType, InternalRoles][])) { + for (const [dbr, r] of roles) { if (!r || !dbr) continue; if (members.get(u._id)?.roles.cache.has(r.id)) { - console.log(`${u.tu_id} has role ${irn}`); if (!u.token_roles) u.token_roles = new Types.Array(); - u.token_roles.push(dbr); + if(!u.token_roles.find(x => dbr._id.equals(x._id))) { + console.log(`${u.tu_id} has new role ${dbr.internal_name}`); + u.token_roles.push(dbr); + updatedCount++; + } await u.save(); } } } - await client.utils.embeds.SimpleEmbed(interaction, "Fix Verify", "Done"); - - // await client.utils.embeds.SimpleEmbed(interaction, { - // title: "Server Stats", - // text: "Server Information", - // empheral: false, - // fields: [ - // { name: "❯ Members: ", value: `${interaction.guild.memberCount}`, inline: true }, - // { name: "❯ Verified Members: ", value: `${verifiedRole?.members.size ?? 0}`, inline: true }, - // { name: "❯ Unverified Members: ", value: `${interaction.guild.memberCount - (verifiedRole?.members.size ?? 0)}`, inline: true }, - // { name: "❯ Channels: ", value: `${interaction.guild.channels.cache.size}`, inline: true }, - // { name: "❯ Owner: ", value: `<@${interaction.guild.ownerId}>`, inline: true }, - // { name: "❯ Created at: ", value: ``, inline: true }, - // ], - // image: "attachment://graph.png", - // files: [ - // attachment, - // ], - // }); + await client.utils.embeds.SimpleEmbed(interaction, "Fix Verify", `Done. Updated ${updatedCount} DB-Roles.`); }, }; From af7df96ce0b9df34ed213b864f4ad662aea6d1b2 Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Thu, 30 Nov 2023 18:53:44 +0100 Subject: [PATCH 3/6] rename fixverify -> updateuserroles --- src/commands/admin/{fixverify.ts => updateuserroles.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/commands/admin/{fixverify.ts => updateuserroles.ts} (94%) diff --git a/src/commands/admin/fixverify.ts b/src/commands/admin/updateuserroles.ts similarity index 94% rename from src/commands/admin/fixverify.ts rename to src/commands/admin/updateuserroles.ts index 5e79066..33b75a5 100644 --- a/src/commands/admin/fixverify.ts +++ b/src/commands/admin/updateuserroles.ts @@ -13,7 +13,7 @@ import { ArraySubDocumentType } from "@typegoose/typegoose"; * The Command Definition */ const command: Command = { - name: "fixverify", + name: "updateuserroles", guildOnly: false, description: "This command updates the db roles for all users based on their guild roles.", async execute(client, interaction, args) { @@ -56,7 +56,7 @@ const command: Command = { } } - await client.utils.embeds.SimpleEmbed(interaction, "Fix Verify", `Done. Updated ${updatedCount} DB-Roles.`); + await client.utils.embeds.SimpleEmbed(interaction, "Administration", `Done. Updated ${updatedCount} User-Roles in the DB.`); }, }; From b0621baa3236d6902e1b510e2c211a7540781dac Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Thu, 30 Nov 2023 18:54:47 +0100 Subject: [PATCH 4/6] improve genverifyroles --- src/commands/admin/genverifyroles.ts | 90 +++++++++++++++++++--------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/src/commands/admin/genverifyroles.ts b/src/commands/admin/genverifyroles.ts index ff9eaf8..308f542 100644 --- a/src/commands/admin/genverifyroles.ts +++ b/src/commands/admin/genverifyroles.ts @@ -1,9 +1,10 @@ -import { Message, Role } from "discord.js"; +import { InternalGuildRoles } from "../../models/bot_roles"; +import { Message, ApplicationCommandOptionType } from "discord.js"; import { Command } from "../../../typings"; import "moment-duration-format"; import { GuildModel } from "../../models/guilds"; -import { DBRole, InternalRoles, RoleScopes } from "../../models/bot_roles"; -import { ArraySubDocumentType, mongoose } from "@typegoose/typegoose"; +import { RoleScopes } from "../../models/bot_roles"; +import { mongoose } from "@typegoose/typegoose"; @@ -11,9 +12,17 @@ import { ArraySubDocumentType, mongoose } from "@typegoose/typegoose"; * The Command Definition */ const command: Command = { - name: "genverifyroles", + name: "updatebotroles", guildOnly: false, - description: "Temp Command that fixes the Verify DB.", + description: "This command updates or generates the database entries for the internal roles", + options: [ + { + name: "create-if-not-exists", + description: "Create the Roles on the guild with the default name if they don't exist, defaults to true", + type: ApplicationCommandOptionType.Boolean, + required: false, + }, + ], async execute(client, interaction, args) { if (!interaction || !interaction.guild) { return; @@ -22,25 +31,53 @@ const command: Command = { client.utils.embeds.SimpleEmbed(interaction, "Slash Only Command", "This Command is Slash only but you Called it with The Prefix. use the slash Command instead."); return; } + await interaction.deferReply(); const guild = interaction.guild; - const roles = await interaction.guild.roles.fetch(); - const members = await interaction.guild.members.fetch(); - const dbGuild = (await GuildModel.findOne({ _id: guild.id }))!; - const verifiedRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "verified"); - let dbVerifyRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.VERIFIED); - const orgaRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "orga"); - let dbOrgaRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.SERVER_ADMIN); - const tutorRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "tutor"); - let dbTutorRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.TUTOR); - const activeSessionRole = interaction.guild.roles.cache.find(x => x.name.toLowerCase() === "active_session"); - let dbActiveSessionRole = dbGuild.guild_settings.roles?.find(x => x.internal_name === InternalRoles.ACTIVE_SESSION); + const dbGuild = await GuildModel.findById(guild.id); + if (!dbGuild) { + return await client.utils.embeds.SimpleEmbed(interaction, "Adminstration", "Guild not found in Database."); + } + const createIfNotExists = interaction.options.getBoolean("create-if-not-exists", false) ?? true; // Create the roles if they don't exist - for (const [r, dbr, irn] of ([[verifiedRole, dbVerifyRole, InternalRoles.VERIFIED], [orgaRole, dbOrgaRole, InternalRoles.SERVER_ADMIN], [tutorRole, dbTutorRole, InternalRoles.TUTOR], [activeSessionRole, dbActiveSessionRole, InternalRoles.ACTIVE_SESSION]] as [Role, ArraySubDocumentType, InternalRoles][])) { - if (!r) continue; + // iterate over type InternalGuildRoles + for (const irn of InternalGuildRoles) { + let r = guild.roles.cache.find(x => x.name === irn); + const dbr = dbGuild.guild_settings.roles?.find(x => x.internal_name === irn); + if (!r) { + // try to find role by id from db + if (dbr) { + const newR = interaction.guild.roles.resolve(dbr.role_id!); + if (newR) { + r = newR; + if (dbr.server_role_name !== r.name) { + console.log(`Role "${irn}" was renamed from "${dbr.server_role_name}" to "${r.name}". Updating DB`); + // update db + dbr.server_role_name = r.name; + await dbGuild.save(); + } + continue; + } + } + if (!createIfNotExists) { + console.log(`role ${irn} not found in guild ${guild.name}. Skipping`); + continue; + } + console.log(`role ${irn} not found in guild ${guild.name}. Creating`); + const newRole = await guild.roles.create({ + name: irn, + mentionable: false, + }); + if (!newRole) { + console.log(`Could not create role ${irn}`); + continue; + } + console.log(`created role ${irn}`); + r = newRole; + } if (!dbr) { console.log(`creating role ${irn}`); if (!dbGuild.guild_settings.roles) { @@ -54,17 +91,16 @@ const command: Command = { server_role_name: r.name, }); await dbGuild.save(); - if(irn === InternalRoles.VERIFIED) { - dbVerifyRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } else if(irn === InternalRoles.SERVER_ADMIN) { - dbOrgaRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } else if(irn === InternalRoles.TUTOR) { - dbTutorRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } else if(irn === InternalRoles.ACTIVE_SESSION) { - dbActiveSessionRole = dbGuild.guild_settings.roles.find(x => x.role_id === r.id)!; - } } } + await client.utils.embeds.SimpleEmbed( + interaction, + "Adminstration", + `Done generating internal Roles. Internal Roles: \n + ${(dbGuild.guild_settings.roles?.map(x => `❯ ${x.internal_name}: <@&${x.role_id}>`).join("\n") ?? "None")} + \nUnassigned Roles: \n + ${InternalGuildRoles.filter(x => !dbGuild.guild_settings.roles?.find(y => y.internal_name === x)).map(x => `❯ ${x}`).join("\n") ?? "None"}`, + ); }, }; From bc95b4815765bc4a6a4fb4e09595491ea8709502 Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Thu, 30 Nov 2023 18:55:15 +0100 Subject: [PATCH 5/6] rename fixverifyroles -> updatebotroles --- src/commands/admin/{genverifyroles.ts => updatebotroles.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/commands/admin/{genverifyroles.ts => updatebotroles.ts} (100%) diff --git a/src/commands/admin/genverifyroles.ts b/src/commands/admin/updatebotroles.ts similarity index 100% rename from src/commands/admin/genverifyroles.ts rename to src/commands/admin/updatebotroles.ts From c987d2e54134ba879bfcb6c3cda9ed724630656d Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Thu, 30 Nov 2023 18:55:29 +0100 Subject: [PATCH 6/6] add setinternalrole command --- src/commands/admin/setinternalrole.ts | 81 +++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/commands/admin/setinternalrole.ts diff --git a/src/commands/admin/setinternalrole.ts b/src/commands/admin/setinternalrole.ts new file mode 100644 index 0000000..b1a5ef8 --- /dev/null +++ b/src/commands/admin/setinternalrole.ts @@ -0,0 +1,81 @@ +import { Message, ApplicationCommandOptionType } from "discord.js"; +import { Command } from "../../../typings"; +import "moment-duration-format"; +import { GuildModel } from "../../models/guilds"; +import { InternalGuildRoles, InternalRoles, RoleScopes } from "../../models/bot_roles"; +import { mongoose } from "@typegoose/typegoose"; + + + +/** + * The Command Definition + */ +const command: Command = { + name: "setinternalrole", + guildOnly: false, + description: "This command generates the database entries for the internal roles", + options: [ + { + name: "internal-role-name", + description: "The Internal Role Name used for tracking", + type: ApplicationCommandOptionType.String, + choices: InternalGuildRoles.map(x => ({ name: Object.keys(InternalRoles).find(y => InternalRoles[y as keyof typeof InternalRoles] === x)!, value: x })), + required: true, + }, + { + name: "guild-role", + description: "The Guild Role to use for the Internal Role", + type: ApplicationCommandOptionType.Role, + required: true, + }, + ], + async execute(client, interaction, args) { + if (!interaction || !interaction.guild) { + return; + } + if (interaction instanceof Message) { + client.utils.embeds.SimpleEmbed(interaction, "Slash Only Command", "This Command is Slash only but you Called it with The Prefix. use the slash Command instead."); + return; + } + await interaction.deferReply(); + const guild = interaction.guild; + const internalRoleName = interaction.options.getString("internal-role-name", true) as typeof InternalGuildRoles[number]; + const guildRole = interaction.options.getRole("guild-role", true); + const dbGuild = await GuildModel.findById(guild.id); + if(!dbGuild) { + return await client.utils.embeds.SimpleEmbed(interaction, "Adminstration", "Guild not found in Database."); + } + let roles = dbGuild.guild_settings.roles; + if (!roles) { + dbGuild.guild_settings.roles = new mongoose.Types.DocumentArray([]); + roles = dbGuild.guild_settings.roles; + } + const role = roles.find(x => x.internal_name === internalRoleName); + if (role) { + // Update + role.role_id = guildRole.id; + role.server_role_name = guildRole.name; + } else { + roles.push({ + internal_name: internalRoleName, + role_id: guildRole.id, + scope: RoleScopes.SERVER, + server_id: guild.id, + server_role_name: guildRole.name, + }); + } + + await dbGuild.save(); + + await client.utils.embeds.SimpleEmbed( + interaction, + "Adminstration", + `Internal Role ${internalRoleName} set to <@&${guildRole.id}>. Note that orphans are not automatically removed.`, + ); + }, +}; + +/** + * Exporting the Command using CommonJS + */ +module.exports = command; \ No newline at end of file