diff --git a/src/commands/admin/fixverify.ts b/src/commands/admin/fixverify.ts deleted file mode 100644 index eea9706..0000000 --- a/src/commands/admin/fixverify.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Message, Role } from "discord.js"; -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 { Types } from "mongoose"; -import { DocumentType } from "@typegoose/typegoose"; - - - -/** - * The Command Definition - */ -const command: Command = { - name: "fixverify", - guildOnly: false, - description: "Temp Command that fixes the Verify DB.", - 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 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 users = await UserModel.find({}); - let count = 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][])) { - 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); - 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, - // ], - // }); - }, -}; - -/** - * Exporting the Command using CommonJS - */ -module.exports = command; \ No newline at end of file diff --git a/src/commands/admin/genverifyroles.ts b/src/commands/admin/genverifyroles.ts deleted file mode 100644 index ff9eaf8..0000000 --- a/src/commands/admin/genverifyroles.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Message, Role } 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"; - - - -/** - * The Command Definition - */ -const command: Command = { - name: "genverifyroles", - guildOnly: false, - description: "Temp Command that fixes the Verify DB.", - 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 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, ArraySubDocumentType, InternalRoles][])) { - if (!r) continue; - if (!dbr) { - console.log(`creating role ${irn}`); - if (!dbGuild.guild_settings.roles) { - dbGuild.guild_settings.roles = new mongoose.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)!; - } - } - } - }, -}; - -/** - * Exporting the Command using CommonJS - */ -module.exports = command; \ No newline at end of file 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 diff --git a/src/commands/admin/updatebotroles.ts b/src/commands/admin/updatebotroles.ts new file mode 100644 index 0000000..308f542 --- /dev/null +++ b/src/commands/admin/updatebotroles.ts @@ -0,0 +1,110 @@ +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 { RoleScopes } from "../../models/bot_roles"; +import { mongoose } from "@typegoose/typegoose"; + + + +/** + * The Command Definition + */ +const command: Command = { + name: "updatebotroles", + guildOnly: false, + 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; + } + 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 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 + // 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) { + dbGuild.guild_settings.roles = new mongoose.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(); + } + } + 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"}`, + ); + }, +}; + +/** + * Exporting the Command using CommonJS + */ +module.exports = command; \ No newline at end of file diff --git a/src/commands/admin/updateuserroles.ts b/src/commands/admin/updateuserroles.ts new file mode 100644 index 0000000..33b75a5 --- /dev/null +++ b/src/commands/admin/updateuserroles.ts @@ -0,0 +1,66 @@ +import { Message, Role } from "discord.js"; +import { Command } from "../../../typings"; +import "moment-duration-format"; +import { UserModel } from "../../models/users"; +import { GuildModel } from "../../models/guilds"; +import { DBRole } from "../../models/bot_roles"; +import { Types } from "mongoose"; +import { ArraySubDocumentType } from "@typegoose/typegoose"; + + + +/** + * The Command Definition + */ +const command: Command = { + name: "updateuserroles", + guildOnly: false, + 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; + } + 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 members = await interaction.guild.members.fetch(); + 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 [dbr, r] of roles) { + if (!r || !dbr) continue; + if (members.get(u._id)?.roles.cache.has(r.id)) { + if (!u.token_roles) u.token_roles = new Types.Array(); + 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, "Administration", `Done. Updated ${updatedCount} User-Roles in the DB.`); + }, +}; + +/** + * Exporting the Command using CommonJS + */ +module.exports = command; \ No newline at end of file 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",