diff --git a/.eslintrc.json b/.eslintrc.json index c940a14..9b95074 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,9 +13,21 @@ "sourceType": "module" }, "plugins": [ - "@typescript-eslint" + "@typescript-eslint", + "unused-imports" ], "rules": { + "object-curly-spacing": [ + "warn", + "always" + ], + "comma-spacing": [ + "warn", + { + "before": false, + "after": true + } + ], "indent": [ "error", 4 @@ -34,9 +46,12 @@ ], "@typescript-eslint/no-non-null-assertion": "off", "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "warn" - ], + "@typescript-eslint/no-unused-vars": "off", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "warn", + { "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" } + ], "prefer-const": "warn", "@typescript-eslint/no-empty-interface": [ "warn", diff --git a/.github/workflows/docker-img.yml b/.github/workflows/docker-img.yml index a6e9bda..c5dd8b9 100644 --- a/.github/workflows/docker-img.yml +++ b/.github/workflows/docker-img.yml @@ -20,6 +20,9 @@ jobs: ghr_push: name: Build and publish container runs-on: ubuntu-latest + permissions: + contents: read + packages: write steps: - name: checkout diff --git a/.gitignore b/.gitignore index e1ce007..33b022c 100644 --- a/.gitignore +++ b/.gitignore @@ -122,4 +122,5 @@ dist .pnp.* report*.csv -result.json \ No newline at end of file +result.json +result.json.bak diff --git a/README.md b/README.md index c072fbc..d1c4c7f 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,28 @@ # Rubot2 -[![Build](https://github.com/Rdeisenroth/Rubot2/actions/workflows/build.yml/badge.svg)](https://github.com/Rdeisenroth/Rubot2/actions/workflows/build.yml) +[![Build](https://github.com/Rdeisenroth/Rubot2/actions/workflows/docker-img.yml/badge.svg)](https://github.com/Rdeisenroth/Rubot2/actions/workflows/docker-img.yml) ![GitHub top language](https://img.shields.io/github/languages/top/Rdeisenroth/Rubot2?logo=Github) ![GitHub repo size](https://img.shields.io/github/repo-size/Rdeisenroth/Rubot2?color=success&logo=Github) ![Commands](https://img.shields.io/badge/Commands-77-orange?logo=Discord&logoColor=ffffff) -a general Purpose Discord Bot -# Setup -- Install Mongodb and add a new user and give it acess to the schema you want to use - - Hint: A strong password can be generated with `openssl rand -base64 ` and converted for the coonnection string with `php -r "echo urlencode(\"\");"` -- install the Dependencies with `npm install` -- Create a config.json File and Fill it according to the Json Schema -- run `npm start` to compile and run the Bot +A general Purpose Discord Bot. + +# Development Setup +- Clone the Repository +- Either use the docker compose file to start a mongodb instance or install mongodb locally + - To use the docker compose file run `docker-compose -f docker-compose.development.yml up -d` +- Setup a `.env` File + - `TOKEN` is the Discord Bot Token. To see how to create a Discord Bot see [Setup.md](Setup.md) + - `OWNER_ID` is the Discord ID of the Bot Owner. To get your Discord ID enable Developer Mode in the Discord Settings and right click on your name in the User List. + - `CLIENT_ID` is the Discord ID of the Bot. To get the ID go to the [Discord Developer Portal](https://discord.com/developers/applications) and select your application. The ID is displayed under the name of the application. Alternatively enable Developer Mode in the Discord Settings and right click on the Bot in the User List to copy the ID. + - `MONGODB_USER` is the Username for the Mongodb Database. If you use the docker compose development file you can freely choose the value. If you use a local instance of mongodb you have to create a new user and give it access to the database you want to use. + - `MONGODB_PASSWORD` is the Password for the Mongodb Database. If you use the docker compose development file you can freely choose the value. If you use a local instance of mongodb you have to create a new user and give it access to the database you want to use. + - `MONGODB_DBNAME` is the Name of the Mongodb Database. If you use the docker compose development file you can freely choose the value. If you use a local instance of mongodb you have to create a new user and give it access to the database you want to use. + - `MONGODB_CONNECTION_URL` is the Connection String for the Mongodb Database. The connection string should look like this: `mongodb://:@:/?authSource=admin` make sure to replace the values with the ones you chose above, host and port should be `localhost` or `127.0.0.1` and `27017` respectively. +- Install the Dependencies with `npm install` +- Run `npm start` to compile and run the Bot in development mode + +## Setup for manual Testing +See [Setup.md](Setup.md). # Deployment - Run `npm build` once to compile to the dist directory on the target device diff --git a/Setup.md b/Setup.md new file mode 100644 index 0000000..8d02ec4 --- /dev/null +++ b/Setup.md @@ -0,0 +1,28 @@ +# Setup to test the bot + +## Create a Discord Bot + +1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) and create a new application. +2. Under the `Bot` tab, of the application, enable all the `Privileged Gateway Intents`. +3. Under OAuth2 go the URL Generator and select the `applications.commands` and `bot` scopes. For bot permissions, select `Administrator`. +4. Copy the generated URL and paste it into your browser. Select the server you want to add the bot to and click `Authorize`. + +## Creating a Test Server + +1. Create a new server and add the bot to it (see above). +2. Go to the `Server Settings` and under `Roles` create a new role called `Admin` and give it the `Administrator` permission. +3. Assign the `Admin` role to the bot. +4. Also under `Roles` create three new roles called `Orga`, `Tutor`, and `Verified`, they don't need any permissions. +5. Call the command `/admin genverifyroles` to generate the roles in the database. +6. Create a new category, call it for example `Tutoring` and make it private but allow the `Tutor` role to see it. +7. Create one voice channel in the category, for example `tutoring-waiting` and allow the `Verified` role to see it. The `Tutor` role should be able to see it already because it is in the same category. +8. Call the command `/create-queue` and pass it the name for the queue and a description, for example `tutoring`. This will create a new queue in the database. +9. Call the command `/setqueue` and pass it the name of the waiting channel, for example `tutoring-waiting` and the name of the queue, for example `tutoring` as well as the supervisor role, for example `Tutor`. This will set the waiting channel for the queue and the supervisor role. +10. Optionally rename the `coach` commands on the server to `tutor` by calling the command `/config commands rename` and passing it the old and new name of the command. + +## Testing the Bot + +1. Create a second discord account and join the test server. +2. Assign the `Verified` role to the test account. +3. Assign the `Tutor` role to the primary account. +4. You now have a working test setup. The test account acts as a student and the primary account acts as a tutor. \ No newline at end of file diff --git a/docker-compose.development.yml b/docker-compose.development.yml new file mode 100644 index 0000000..e8f86bb --- /dev/null +++ b/docker-compose.development.yml @@ -0,0 +1,12 @@ +version: '3.8' +services: + db: + image: mongo + container_name: sprechi-mongodb + environment: + - MONGO_INITDB_ROOT_USERNAME=$MONGODB_USER + - MONGO_INITDB_ROOT_PASSWORD=$MONGODB_PASSWORD + - MONGO_INITDB_DATABASE=$MONGODB_DBNAME + restart: unless-stopped + ports: + - 27017:27017 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7dcfab4..1e9ffc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "commonjs": "^0.0.1", "consola": "^3.2.3", "cron": "^3.1.0", - "crypto-js": "^4.1.1", + "crypto-js": "^4.2.0", "csv-parse": "^5.5.2", "discord.js": "^14.13.0", "dotenv": "^16.3.1", @@ -52,6 +52,7 @@ "@typescript-eslint/parser": "^6.7.5", "chai": "^4.3.10", "eslint": "^8.51.0", + "eslint-plugin-unused-imports": "^3.0.0", "jest": "^29.7.0", "mocha": "^10.2.0", "nodemon": "^3.0.1", @@ -2879,9 +2880,9 @@ } }, "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/css-select": { "version": "5.1.0", @@ -3365,6 +3366,36 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.0.0.tgz", + "integrity": "sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==", + "dev": true, + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0", + "eslint": "^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", diff --git a/package.json b/package.json index fc7cc4b..e1cae2f 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "commonjs": "^0.0.1", "consola": "^3.2.3", "cron": "^3.1.0", - "crypto-js": "^4.1.1", + "crypto-js": "^4.2.0", "csv-parse": "^5.5.2", "discord.js": "^14.13.0", "dotenv": "^16.3.1", @@ -58,6 +58,7 @@ "@typescript-eslint/parser": "^6.7.5", "chai": "^4.3.10", "eslint": "^8.51.0", + "eslint-plugin-unused-imports": "^3.0.0", "jest": "^29.7.0", "mocha": "^10.2.0", "nodemon": "^3.0.1", diff --git a/src/bot.ts b/src/bot.ts index c399fcd..f62433a 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,13 +1,12 @@ import { ConfigHandler } from "./handlers/configHandler"; import { Client, Collection, Partials, TextChannel, VoiceChannel } from "discord.js"; import consola, { ConsolaInstance } from "consola"; -import cron, { CronJob } from "cron"; -import { BotConfig, BotEvent, ButtonInteraction, Command } from "../typings"; +import { CronJob } from "cron"; +import { BotEvent, ButtonInteraction, Command } from "../typings"; import glob from "glob-promise"; -import { promisify } from "util"; import * as fs from "fs"; import * as utils from "./utils/utils"; -import {GuildModel} from "./models/guilds"; +import { GuildModel } from "./models/guilds"; import parser from "yargs-parser"; import mongoose from "mongoose"; import path from "path/posix"; @@ -31,7 +30,7 @@ export class Bot extends Client { public parser = parser; public database = mongoose; public readonly initTimestamp = Date.now(); - public jobs: CronJob[] = []; + public jobs: CronJob[] = []; public constructor() { super({ intents: [ @@ -61,14 +60,6 @@ export class Bot extends Client { * @param config The bot Configuration */ public async start(): Promise { - this.logger.info("starting Bot..."); - try { - await this.login(this.config.get("token")); - } catch (error) { - this.logger.error("Invalid token", error); - process.exit(1); - } - // Commands this.logger.info("Loading Commands..."); const commandFiles = fs.readdirSync(`${__dirname}/commands`).filter(file => file.endsWith(".js") || file.endsWith("ts")); @@ -135,12 +126,12 @@ export class Bot extends Client { // Event Files this.logger.info("Loading Events..."); const eventFiles = fs.readdirSync(`${__dirname}/events`).filter(file => file.endsWith(".js") || file.endsWith("ts")); - await eventFiles.map(async (eventFile: string) => { + await Promise.all(eventFiles.map(async (eventFile: string) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const event = (await import(`${__dirname}/events/${eventFile}`)) as BotEvent; console.log(`${JSON.stringify(event.name)} (./events/${eventFile})`); this.on(event.name, event.execute.bind(null, this)); - }); + })); const queueGuardJob = new CronJob("*/30 * * * * *", async () => { for (const g of this.guilds.cache.values()) { @@ -194,6 +185,14 @@ export class Bot extends Client { }, null, true, "America/Los_Angeles"); queueGuardJob.start(); + this.logger.info("starting Bot..."); + try { + await this.login(this.config.get("token")); + } catch (error) { + this.logger.error("Invalid token", error); + process.exit(1); + } + this.logger.ready(`Bot ${this.user?.displayName} is Ready!`); // public async createGuildCommand(data:any, guildId:string) { diff --git a/src/commands/admin/decrypttoken.ts b/src/commands/admin/decrypttoken.ts index 035b949..b5d77ea 100644 --- a/src/commands/admin/decrypttoken.ts +++ b/src/commands/admin/decrypttoken.ts @@ -1,10 +1,6 @@ -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../typings"; import "moment-duration-format"; -import {UserModel, User } from "../../models/users"; -import {GuildModel} from "../../models/guilds"; -import { DBRole, InternalRoles, RoleScopes } from "../../models/bot_roles"; -import { Types } from "mongoose"; @@ -37,7 +33,7 @@ const command: Command = { } catch (error) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Verification System Error", text: "Token is not valid.", empheral: true }); } - return client.utils.embeds.SimpleEmbed(interaction, { title: "Decrypted Text", text: `decrypted: ${decrypted}`, empheral:true}); + return client.utils.embeds.SimpleEmbed(interaction, { title: "Decrypted Text", text: `decrypted: ${decrypted}`, empheral:true }); }, }; diff --git a/src/commands/admin/fixverify.ts b/src/commands/admin/fixverify.ts deleted file mode 100644 index 81269c5..0000000 --- a/src/commands/admin/fixverify.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Message, Role } from "discord.js"; -import { Command } from "../../../typings"; -import "moment-duration-format"; -import {UserModel, User } 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); - - // 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]] 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)!; - } - } - } - - 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]] 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/generatetoken.ts b/src/commands/admin/generatetoken.ts index e2c202b..4e34420 100644 --- a/src/commands/admin/generatetoken.ts +++ b/src/commands/admin/generatetoken.ts @@ -28,7 +28,7 @@ const command: Command = { } const encrypted = client.utils.general.encryptText(interaction.options.getString("encryptionstring", true)); const decrypted = client.utils.general.decryptText(encrypted); - return client.utils.embeds.SimpleEmbed(interaction, { title: "Encrypted+Decrypted Text", text: `encrypted: ${encrypted}\ndecrypted: ${decrypted}`, empheral:true}); + return client.utils.embeds.SimpleEmbed(interaction, { title: "Encrypted+Decrypted Text", text: `encrypted: ${encrypted}\ndecrypted: ${decrypted}`, empheral:true }); }, }; diff --git a/src/commands/admin/genverifyroles.ts b/src/commands/admin/genverifyroles.ts deleted file mode 100644 index 9aa8e7a..0000000 --- a/src/commands/admin/genverifyroles.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Message, Role } from "discord.js"; -import { Command } from "../../../typings"; -import "moment-duration-format"; -import {UserModel, User } from "../../models/users"; -import {GuildModel} from "../../models/guilds"; -import { DBRole, InternalRoles, RoleScopes } from "../../models/bot_roles"; -import { ArraySubDocumentType, DocumentType, 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); - - // 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]] 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)!; - } - } - } - }, -}; - -/** - * Exporting the Command using CommonJS - */ -module.exports = command; \ No newline at end of file diff --git a/src/commands/admin/member/lookup-by.ts b/src/commands/admin/member/lookup-by.ts index 041bad5..270d860 100644 --- a/src/commands/admin/member/lookup-by.ts +++ b/src/commands/admin/member/lookup-by.ts @@ -1,10 +1,6 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; -import moment from "moment"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel, User } from "../../../models/users"; -import {SessionModel} from "../../../models/sessions"; -import {QueueModel} from "../../../models/queues"; +import { UserModel, User } from "../../../models/users"; import { FilterQuery } from "mongoose"; import { DocumentType, mongoose } from "@typegoose/typegoose"; import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; diff --git a/src/commands/admin/member/lookup.ts b/src/commands/admin/member/lookup.ts index a628bb8..a72f8cd 100644 --- a/src/commands/admin/member/lookup.ts +++ b/src/commands/admin/member/lookup.ts @@ -1,10 +1,6 @@ -import { ApplicationCommandOptionType, EmbedData, EmbedField, Message } from "discord.js"; -import moment from "moment"; +import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {SessionModel} from "../../../models/sessions"; -import {QueueModel} from "../../../models/queues"; +import { UserModel } from "../../../models/users"; const command: Command = { name: "lookup", diff --git a/src/commands/admin/member/unverify.ts b/src/commands/admin/member/unverify.ts index 8c7e788..96a433e 100644 --- a/src/commands/admin/member/unverify.ts +++ b/src/commands/admin/member/unverify.ts @@ -1,10 +1,6 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; -import moment from "moment"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {SessionModel} from "../../../models/sessions"; -import {QueueModel} from "../../../models/queues"; +import { UserModel } from "../../../models/users"; const command: Command = { name: "unverify", diff --git a/src/commands/admin/queue/fixup.ts b/src/commands/admin/queue/fixup.ts index 730f44e..9fcb03a 100644 --- a/src/commands/admin/queue/fixup.ts +++ b/src/commands/admin/queue/fixup.ts @@ -1,8 +1,7 @@ -import { ApplicationCommandOptionType, EmbedField, Message, Role } from "discord.js"; -import path from "path"; +import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; const command: Command = { name: "fixup", diff --git a/src/commands/admin/queue/info.ts b/src/commands/admin/queue/info.ts index 6157e20..f51a684 100644 --- a/src/commands/admin/queue/info.ts +++ b/src/commands/admin/queue/info.ts @@ -1,7 +1,6 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "info", diff --git a/src/commands/admin/queue/kick.ts b/src/commands/admin/queue/kick.ts index 0fa54c4..a038d8d 100644 --- a/src/commands/admin/queue/kick.ts +++ b/src/commands/admin/queue/kick.ts @@ -1,8 +1,6 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "kick", diff --git a/src/commands/admin/queue/list.ts b/src/commands/admin/queue/list.ts index 52ceae1..59e1fa5 100644 --- a/src/commands/admin/queue/list.ts +++ b/src/commands/admin/queue/list.ts @@ -1,8 +1,6 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "list", diff --git a/src/commands/admin/queue/lock.ts b/src/commands/admin/queue/lock.ts index 6109b72..5cc42b3 100644 --- a/src/commands/admin/queue/lock.ts +++ b/src/commands/admin/queue/lock.ts @@ -1,8 +1,6 @@ -import { ApplicationCommandOptionType, EmbedField, Message, VoiceChannel } from "discord.js"; -import path from "path"; +import { ApplicationCommandOptionType, Message, VoiceChannel } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "lock", diff --git a/src/commands/admin/queue/unlock.ts b/src/commands/admin/queue/unlock.ts index 04b6f24..b7eb6af 100644 --- a/src/commands/admin/queue/unlock.ts +++ b/src/commands/admin/queue/unlock.ts @@ -1,8 +1,6 @@ -import { ApplicationCommandOptionType, EmbedField, Message, VoiceChannel } from "discord.js"; -import path from "path"; +import { ApplicationCommandOptionType, Message, VoiceChannel } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "unlock", diff --git a/src/commands/admin/queue/userrank.ts b/src/commands/admin/queue/userrank.ts index fabb580..7e4ea7c 100644 --- a/src/commands/admin/queue/userrank.ts +++ b/src/commands/admin/queue/userrank.ts @@ -1,9 +1,8 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {RoomModel} from "../../../models/rooms"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; +import { RoomModel } from "../../../models/rooms"; const command: Command = { name: "userrank", @@ -75,7 +74,7 @@ const command: Command = { inline: false, }); } catch (error) { - client.logger.error(`could not get user details for ${u._id}`,error); + client.logger.error(`could not get user details for ${u._id}`, error); fields.push({ name: u._id, value: `-Sprechstunden Beigetreten: ${u.roomCount}`, diff --git a/src/commands/admin/queue/userstats.ts b/src/commands/admin/queue/userstats.ts index 47f3ea0..f76aa94 100644 --- a/src/commands/admin/queue/userstats.ts +++ b/src/commands/admin/queue/userstats.ts @@ -1,9 +1,7 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {RoomModel} from "../../../models/rooms"; +import { GuildModel } from "../../../models/guilds"; +import { RoomModel } from "../../../models/rooms"; const command: Command = { name: "userstats", diff --git a/src/commands/admin/serverstats.ts b/src/commands/admin/serverstats.ts index a37dbe8..1b2427c 100644 --- a/src/commands/admin/serverstats.ts +++ b/src/commands/admin/serverstats.ts @@ -1,10 +1,7 @@ -import { ApplicationCommandOptionType, AttachmentBuilder, AuditLogEvent, Collection, GuildAuditLogs, GuildAuditLogsEntry, Message } from "discord.js"; +import { ApplicationCommandOptionType, AttachmentBuilder, AuditLogEvent, Collection, GuildAuditLogsEntry, Message } from "discord.js"; import { Command } from "../../../typings"; -import { version as djsversion } from "discord.js"; -import * as moment from "moment"; import "moment-duration-format"; import { ChartJSNodeCanvas } from "chartjs-node-canvas"; -import { string } from "yargs"; import { APIRole } from "discord-api-types/v9"; @@ -172,7 +169,7 @@ const command: Command = { }, }, ); - const attachment = new AttachmentBuilder(image, {name: "graph.png"}); + const attachment = new AttachmentBuilder(image, { name: "graph.png" }); await client.utils.embeds.SimpleEmbed(interaction, { title: "Server Stats", text: "Server Information", diff --git a/src/commands/admin/session/list.ts b/src/commands/admin/session/list.ts index 2474491..56e1492 100644 --- a/src/commands/admin/session/list.ts +++ b/src/commands/admin/session/list.ts @@ -1,10 +1,7 @@ import { EmbedField, Message } from "discord.js"; -import moment from "moment"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {SessionModel} from "../../../models/sessions"; -import {QueueModel} from "../../../models/queues"; +import { GuildModel } from "../../../models/guilds"; +import { SessionModel } from "../../../models/sessions"; const command: Command = { name: "list", diff --git a/src/commands/admin/session/terminate.ts b/src/commands/admin/session/terminate.ts index 344c3f8..82a8e79 100644 --- a/src/commands/admin/session/terminate.ts +++ b/src/commands/admin/session/terminate.ts @@ -1,10 +1,8 @@ -import { ApplicationCommandOptionType, EmbedField, GuildMember, Message, User } from "discord.js"; +import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {SessionModel} from "../../../models/sessions"; -import {QueueModel} from "../../../models/queues"; +import { GuildModel } from "../../../models/guilds"; +import { SessionModel } from "../../../models/sessions"; const command: Command = { name: "terminate", diff --git a/src/commands/admin/session/terminateall.ts b/src/commands/admin/session/terminateall.ts index b289495..58433d3 100644 --- a/src/commands/admin/session/terminateall.ts +++ b/src/commands/admin/session/terminateall.ts @@ -1,10 +1,8 @@ -import { EmbedField, GuildMember, Message, User } from "discord.js"; +import { EmbedField, Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {SessionModel} from "../../../models/sessions"; -import {QueueModel} from "../../../models/queues"; +import { GuildModel } from "../../../models/guilds"; +import { SessionModel } from "../../../models/sessions"; const command: Command = { name: "terminateall", 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/commands/coach/info.ts b/src/commands/coach/info.ts index d0aefba..819129e 100644 --- a/src/commands/coach/info.ts +++ b/src/commands/coach/info.ts @@ -1,8 +1,8 @@ import { Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import {UserModel} from "../../models/users"; +import { GuildModel } from "../../models/guilds"; +import { UserModel } from "../../models/users"; const command: Command = { name: "info", diff --git a/src/commands/coach/queue/info.ts b/src/commands/coach/queue/info.ts index 079ecb6..78b9e0a 100644 --- a/src/commands/coach/queue/info.ts +++ b/src/commands/coach/queue/info.ts @@ -1,7 +1,7 @@ import { Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; const command: Command = { name: "info", diff --git a/src/commands/coach/queue/list.ts b/src/commands/coach/queue/list.ts index a5828b0..49b4403 100644 --- a/src/commands/coach/queue/list.ts +++ b/src/commands/coach/queue/list.ts @@ -1,8 +1,7 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; const command: Command = { name: "list", diff --git a/src/commands/coach/queue/next.ts b/src/commands/coach/queue/next.ts index 080ac12..76b96cf 100644 --- a/src/commands/coach/queue/next.ts +++ b/src/commands/coach/queue/next.ts @@ -1,10 +1,10 @@ -import {EventModel, Event as EVT, eventType } from "./../../../models/events"; +import { Event as EVT, eventType } from "./../../../models/events"; import { PermissionOverwriteData } from "./../../../models/permission_overwrite_data"; import ChannelType, { ApplicationCommandOptionType, Message, TextChannel } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {RoomModel} from "../../../models/rooms"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; +import { RoomModel } from "../../../models/rooms"; import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { mongoose } from "@typegoose/typegoose"; @@ -94,7 +94,7 @@ const command: Command = { // queueData.set("room_spawner", spawner); // await guildData.save(); } else { - spawner.supervisor_roles = new mongoose.Types.Array(...spawner.supervisor_roles.concat(queue_channel_data?.supervisors ?? [])); + spawner.supervisor_roles.push(...(queue_channel_data?.supervisors ?? [])); spawner.owner = user.id; if (spawner.name) { spawner.name = client.utils.general.interpolateString( diff --git a/src/commands/coach/queue/pick.ts b/src/commands/coach/queue/pick.ts index 852eccf..cc51955 100644 --- a/src/commands/coach/queue/pick.ts +++ b/src/commands/coach/queue/pick.ts @@ -1,11 +1,11 @@ import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; -import {EventModel, Event as EVT, eventType } from "../../../models/events"; +import { Event as EVT, eventType } from "../../../models/events"; import { PermissionOverwriteData } from "../../../models/permission_overwrite_data"; import ChannelType, { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {RoomModel} from "../../../models/rooms"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; +import { RoomModel } from "../../../models/rooms"; import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { mongoose } from "@typegoose/typegoose"; diff --git a/src/commands/coach/session/info.ts b/src/commands/coach/session/info.ts index ad70fdd..7ec1111 100644 --- a/src/commands/coach/session/info.ts +++ b/src/commands/coach/session/info.ts @@ -1,8 +1,8 @@ import { Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; const command: Command = { name: "info", diff --git a/src/commands/coach/session/quit.ts b/src/commands/coach/session/quit.ts index 7d7f29a..941db49 100644 --- a/src/commands/coach/session/quit.ts +++ b/src/commands/coach/session/quit.ts @@ -1,8 +1,9 @@ import { Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; import moment from "moment"; +import {removeRoleFromUser} from "../../../utils/general"; const command: Command = { name: "quit", @@ -33,6 +34,8 @@ const command: Command = { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "You Have no Active Coaching Session.", empheral: true }); } + await removeRoleFromUser(g, user, 'active_session') + //TODO: Terminate Rooms // Set inactive diff --git a/src/commands/coach/session/start.ts b/src/commands/coach/session/start.ts index 79e34d0..49aa152 100644 --- a/src/commands/coach/session/start.ts +++ b/src/commands/coach/session/start.ts @@ -1,10 +1,11 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import {UserModel} from "../../../models/users"; -import {SessionModel, sessionRole } from "../../../models/sessions"; +import { GuildModel } from "../../../models/guilds"; +import { UserModel } from "../../../models/users"; +import { SessionModel, sessionRole } from "../../../models/sessions"; import { Queue } from "../../../models/queues"; import { DocumentType } from "@typegoose/typegoose"; +import {assignRoleToUser} from "../../../utils/general"; const command: Command = { name: "start", @@ -58,6 +59,9 @@ const command: Command = { const session = await SessionModel.create({ active: true, user: user.id, guild: g.id, queue: queueData._id, role: sessionRole.coach, started_at: Date.now(), end_certain: false, rooms: [] }); userEntry.sessions.push(session._id); await userEntry.save(); + + await assignRoleToUser(g, user, 'active_session') + client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "The Session was started.", empheral: true }); }, }; diff --git a/src/commands/config/commands/permission.ts b/src/commands/config/commands/permission.ts index ea3f4ac..4af6df2 100644 --- a/src/commands/config/commands/permission.ts +++ b/src/commands/config/commands/permission.ts @@ -1,7 +1,7 @@ import { SlashCommandPermission } from "./../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "permission", diff --git a/src/commands/config/commands/rename.ts b/src/commands/config/commands/rename.ts index 0a834a2..c02e6c0 100644 --- a/src/commands/config/commands/rename.ts +++ b/src/commands/config/commands/rename.ts @@ -1,8 +1,6 @@ -import { SlashCommandSettings } from "./../../../models/slash_command_settings"; import { ApplicationCommandOptionType, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "rename", diff --git a/src/commands/config/commands/visibility.ts b/src/commands/config/commands/visibility.ts index 6261971..f27cb6c 100644 --- a/src/commands/config/commands/visibility.ts +++ b/src/commands/config/commands/visibility.ts @@ -1,8 +1,6 @@ -import { SlashCommandSettings } from "./../../../models/slash_command_settings"; import { ApplicationCommandOptionType, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "visibility", diff --git a/src/commands/config/queue/add_unlock_time.ts b/src/commands/config/queue/add_unlock_time.ts index f54f155..2a595d2 100644 --- a/src/commands/config/queue/add_unlock_time.ts +++ b/src/commands/config/queue/add_unlock_time.ts @@ -1,8 +1,6 @@ -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; const command: Command = { diff --git a/src/commands/config/queue/autolock.ts b/src/commands/config/queue/autolock.ts index d73291d..216159c 100644 --- a/src/commands/config/queue/autolock.ts +++ b/src/commands/config/queue/autolock.ts @@ -1,6 +1,4 @@ -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; import { GuildModel } from "../../../models/guilds"; diff --git a/src/commands/config/queue/get_schedules.ts b/src/commands/config/queue/get_schedules.ts index cb65032..c11754e 100644 --- a/src/commands/config/queue/get_schedules.ts +++ b/src/commands/config/queue/get_schedules.ts @@ -1,8 +1,6 @@ -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; import { WeekTimestamp } from "../../../models/week_timestamp"; diff --git a/src/commands/config/queue/remove_unlock_time.ts b/src/commands/config/queue/remove_unlock_time.ts index 2d151fb..81bb0c7 100644 --- a/src/commands/config/queue/remove_unlock_time.ts +++ b/src/commands/config/queue/remove_unlock_time.ts @@ -1,8 +1,6 @@ -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; import { WeekTimestamp } from "../../../models/week_timestamp"; diff --git a/src/commands/config/queue/set_category.ts b/src/commands/config/queue/set_category.ts index 5ffebe5..43614d6 100644 --- a/src/commands/config/queue/set_category.ts +++ b/src/commands/config/queue/set_category.ts @@ -1,9 +1,8 @@ import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; import { VoiceChannelSpawner } from "./../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "./../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; import { mongoose } from "@typegoose/typegoose"; const command: Command = { diff --git a/src/commands/config/queue/set_closing_shift.ts b/src/commands/config/queue/set_closing_shift.ts index a16968d..6284f18 100644 --- a/src/commands/config/queue/set_closing_shift.ts +++ b/src/commands/config/queue/set_closing_shift.ts @@ -1,9 +1,6 @@ -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import { QueueSpan } from "../../../models/queue_span"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "set_closing_shift", diff --git a/src/commands/config/queue/set_opening_shift.ts b/src/commands/config/queue/set_opening_shift.ts index ea18367..07f3dc6 100644 --- a/src/commands/config/queue/set_opening_shift.ts +++ b/src/commands/config/queue/set_opening_shift.ts @@ -1,9 +1,6 @@ -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import { QueueSpan } from "../../../models/queue_span"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "set_opening_shift", diff --git a/src/commands/config/queue/set_text_channel.ts b/src/commands/config/queue/set_text_channel.ts index 24a7548..17fb6ab 100644 --- a/src/commands/config/queue/set_text_channel.ts +++ b/src/commands/config/queue/set_text_channel.ts @@ -1,10 +1,6 @@ -import { Channel } from "./../../../models/text_channels"; -import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; -import { SlashCommandPermission } from "../../../models/slash_command_permission"; -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import {GuildModel} from "../../../models/guilds"; -import { QueueSpan } from "../../../models/queue_span"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "set_text_channel", diff --git a/src/commands/create-queue.ts b/src/commands/create-queue.ts index dd00d38..306d763 100644 --- a/src/commands/create-queue.ts +++ b/src/commands/create-queue.ts @@ -1,6 +1,6 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../typings"; -import {GuildModel} from "../models/guilds"; +import { GuildModel } from "../models/guilds"; import { Queue } from "../models/queues"; import { mongoose } from "@typegoose/typegoose"; import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; diff --git a/src/commands/help.ts b/src/commands/help.ts index 5cf9ae1..e725b20 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,7 +1,7 @@ import { ApplicationCommandOptionType, EmojiIdentifierResolvable, Message, EmbedBuilder } from "discord.js"; import yargsParser from "yargs-parser"; -import { Command, RunCommand } from "../../typings"; -import {GuildModel} from "../models/guilds"; +import { Command } from "../../typings"; +import { GuildModel } from "../models/guilds"; const command: Command = { diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 310b6a5..dd397fd 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -1,6 +1,5 @@ -import { ChatInputCommandInteraction, Interaction, Message, EmbedBuilder } from "discord.js"; -import { Command, RunCommand } from "../../typings"; -import { APIMessage } from "discord-api-types/v9"; +import { ChatInputCommandInteraction, Message, EmbedBuilder } from "discord.js"; +import { Command } from "../../typings"; /** * The Command Definition @@ -30,8 +29,8 @@ const command: Command = { const embed = new EmbedBuilder() .setTitle("__Response Times__") .setColor(interaction!.guild?.members.me?.roles.highest.color || 0x7289da) - .addFields({name:"Bot Latency:", value:":hourglass_flowing_sand:" + ping + "ms", inline:true}) - .addFields({name:"API Latency:", value:":hourglass_flowing_sand:" + Math.round(client.ws.ping) + "ms", inline:true}); + .addFields({ name:"Bot Latency:", value:":hourglass_flowing_sand:" + ping + "ms", inline:true }) + .addFields({ name:"API Latency:", value:":hourglass_flowing_sand:" + Math.round(client.ws.ping) + "ms", inline:true }); if (interaction instanceof ChatInputCommandInteraction) { await interaction.editReply({ content: "Pong.", embeds: [embed] }); } else { diff --git a/src/commands/queue/info.ts b/src/commands/queue/info.ts index 68168a2..6d7cda2 100644 --- a/src/commands/queue/info.ts +++ b/src/commands/queue/info.ts @@ -1,6 +1,6 @@ import { Message, EmbedField } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "info", diff --git a/src/commands/queue/join.ts b/src/commands/queue/join.ts index 27c7aab..c6376ae 100644 --- a/src/commands/queue/join.ts +++ b/src/commands/queue/join.ts @@ -1,8 +1,7 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; -import path from "path"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import {UserModel} from "../../models/users"; +import { GuildModel } from "../../models/guilds"; +import { UserModel } from "../../models/users"; const command: Command = { name: "join", diff --git a/src/commands/queue/leave.ts b/src/commands/queue/leave.ts index ed78359..eb51148 100644 --- a/src/commands/queue/leave.ts +++ b/src/commands/queue/leave.ts @@ -1,8 +1,6 @@ import { Message } from "discord.js"; -import moment from "moment"; -import path from "path"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "leave", diff --git a/src/commands/queue/list.ts b/src/commands/queue/list.ts index e374893..f6ceee1 100644 --- a/src/commands/queue/list.ts +++ b/src/commands/queue/list.ts @@ -1,7 +1,6 @@ -import { Queue } from "./../../models/queues"; import { Message } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "list", diff --git a/src/commands/rate.ts b/src/commands/rate.ts index 27be955..6161017 100644 --- a/src/commands/rate.ts +++ b/src/commands/rate.ts @@ -1,5 +1,5 @@ -import { ApplicationCommandOptionType, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; -import { Command, RunCommand } from "../../typings"; +import { ApplicationCommandOptionType, ChatInputCommandInteraction } from "discord.js"; +import { Command } from "../../typings"; /** * The Command Definition diff --git a/src/commands/setjointocreate.ts b/src/commands/setjointocreate.ts index b5b7885..83f06b2 100644 --- a/src/commands/setjointocreate.ts +++ b/src/commands/setjointocreate.ts @@ -1,6 +1,6 @@ import { ApplicationCommandOptionType, CategoryChannel, GuildChannel, Message, VoiceChannel as dvc } from "discord.js"; import { Command } from "../../typings"; -import {GuildModel} from "../models/guilds"; +import { GuildModel } from "../models/guilds"; import { VoiceChannel } from "../models/voice_channels"; import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; import { mongoose } from "@typegoose/typegoose"; diff --git a/src/commands/setqueue.ts b/src/commands/setqueue.ts index 2226e05..43e8b24 100644 --- a/src/commands/setqueue.ts +++ b/src/commands/setqueue.ts @@ -1,7 +1,7 @@ import { VoiceChannelModel } from "./../models/voice_channels"; import { ApplicationCommandOptionType, GuildChannel, Message } from "discord.js"; import { Command } from "../../typings"; -import {GuildModel} from "../models/guilds"; +import { GuildModel } from "../models/guilds"; import { VoiceChannel } from "../models/voice_channels"; import { mongoose } from "@typegoose/typegoose"; import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; diff --git a/src/commands/urban.ts b/src/commands/urban.ts index 8312db1..df34970 100644 --- a/src/commands/urban.ts +++ b/src/commands/urban.ts @@ -1,7 +1,7 @@ import { stripIndents } from "common-tags"; import * as urban from "urban-dictionary"; -import { ApplicationCommandOptionType, ChatInputCommandInteraction, Interaction, Message, EmbedBuilder } from "discord.js"; -import { Command, RunCommand } from "../../typings"; +import { ApplicationCommandOptionType, ChatInputCommandInteraction, EmbedBuilder } from "discord.js"; +import { Command } from "../../typings"; /** * The Command Definition diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 34146be..ae8bc0f 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -1,7 +1,5 @@ -import { UserModel, User } from "./../models/users"; -import { ApplicationCommandOptionType, GuildMember, Interaction, Message } from "discord.js"; -import { Command, RunCommand } from "../../typings"; -import * as crypto from "crypto"; +import { ApplicationCommandOptionType, Message } from "discord.js"; +import { Command } from "../../typings"; /** * The Command Definition diff --git a/src/commands/voice/close.ts b/src/commands/voice/close.ts index 85e5670..3188629 100644 --- a/src/commands/voice/close.ts +++ b/src/commands/voice/close.ts @@ -1,7 +1,6 @@ import { Message } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "close", diff --git a/src/commands/voice/kick.ts b/src/commands/voice/kick.ts index a02e91a..d0ab5ae 100644 --- a/src/commands/voice/kick.ts +++ b/src/commands/voice/kick.ts @@ -1,7 +1,6 @@ import { ApplicationCommandOptionType, GuildMember, Message } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "kick", diff --git a/src/commands/voice/lock.ts b/src/commands/voice/lock.ts index d980653..968b4e8 100644 --- a/src/commands/voice/lock.ts +++ b/src/commands/voice/lock.ts @@ -1,6 +1,5 @@ import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "lock", diff --git a/src/commands/voice/permit.ts b/src/commands/voice/permit.ts index c139d8f..07c8b13 100644 --- a/src/commands/voice/permit.ts +++ b/src/commands/voice/permit.ts @@ -1,7 +1,6 @@ import { ApplicationCommandOptionType, GuildMember, Message } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "permit", diff --git a/src/commands/voice/togglelock.ts b/src/commands/voice/togglelock.ts index dc6ff1e..bf4ec49 100644 --- a/src/commands/voice/togglelock.ts +++ b/src/commands/voice/togglelock.ts @@ -1,7 +1,6 @@ import { OverwriteData } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "togglelock", diff --git a/src/commands/voice/togglevisibility.ts b/src/commands/voice/togglevisibility.ts index 56de897..0a60c06 100644 --- a/src/commands/voice/togglevisibility.ts +++ b/src/commands/voice/togglevisibility.ts @@ -1,6 +1,5 @@ import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "togglevisibility", diff --git a/src/commands/voice/transfer.ts b/src/commands/voice/transfer.ts index f58433c..f6f1153 100644 --- a/src/commands/voice/transfer.ts +++ b/src/commands/voice/transfer.ts @@ -1,7 +1,6 @@ import { ApplicationCommandOptionType, GuildMember, Message } from "discord.js"; import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "transfer", diff --git a/src/commands/voice/unlock.ts b/src/commands/voice/unlock.ts index 5df5ec1..00ce396 100644 --- a/src/commands/voice/unlock.ts +++ b/src/commands/voice/unlock.ts @@ -1,6 +1,5 @@ import { Command } from "../../../typings"; -import {GuildModel} from "../../models/guilds"; -import { VoiceChannel } from "../../models/voice_channels"; +import { GuildModel } from "../../models/guilds"; const command: Command = { name: "unlock", diff --git a/src/componentInteractions/buttons/queue/leave.ts b/src/componentInteractions/buttons/queue/leave.ts index cda1bbc..0a6eccf 100644 --- a/src/componentInteractions/buttons/queue/leave.ts +++ b/src/componentInteractions/buttons/queue/leave.ts @@ -1,4 +1,4 @@ -import { Collection, EmbedBuilder } from "discord.js"; +import { EmbedBuilder } from "discord.js"; import { ButtonInteraction } from "../../../../typings"; import { GuildModel, Guild } from "../../../models/guilds"; import { Queue } from "../../../models/queues"; diff --git a/src/componentInteractions/buttons/queue/stay.ts b/src/componentInteractions/buttons/queue/stay.ts index 12e1cb5..239b4ac 100644 --- a/src/componentInteractions/buttons/queue/stay.ts +++ b/src/componentInteractions/buttons/queue/stay.ts @@ -1,6 +1,6 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Collection, EmbedBuilder } from "discord.js"; import { ButtonInteraction } from "../../../../typings"; -import {GuildModel, Guild } from "../../../models/guilds"; +import { GuildModel, Guild } from "../../../models/guilds"; import { Queue } from "../../../models/queues"; import { DocumentType } from "@typegoose/typegoose"; diff --git a/src/crypto_test.ts b/src/crypto_test.ts index e8f9275..92cdcdf 100644 --- a/src/crypto_test.ts +++ b/src/crypto_test.ts @@ -14,7 +14,7 @@ dotenv.config(); * @returns the encrypted Text */ export function encryptText(text: string) { - return cryptojs.AES.encrypt(text, "mbvWKnjLFh4nHbj7Vx79rvp9kcJF9g4C").toString(); + return cryptojs.AES.encrypt(text, ConfigHandler.getInstance().get("verify_secret")).toString(); } /** @@ -55,6 +55,16 @@ type Student = { const fileContent = fs.readFileSync(csvFilePath, { encoding: "utf-8" }); + // if result.json.bak exists, read it and save a list of all moodleIds + const oldResultFilePath = path.resolve(__dirname, "result.json.bak"); + let oldMoodleIds: string[] = []; + if (fs.existsSync(oldResultFilePath)) { + const oldResultFileContent = fs.readFileSync(oldResultFilePath, { encoding: "utf-8" }); + const oldResult: { moodleId: string; token: string; }[] = JSON.parse(oldResultFileContent); + oldMoodleIds = oldResult.map(x => x.moodleId); + console.log(`Skipping ${oldMoodleIds.length} moodleIds`); + } + parse(fileContent, { delimiter: ",", columns: headers, @@ -63,21 +73,25 @@ type Student = { console.error(error); } // {"moodleId":"1","token":"FOP-DiscordV1|guest|1#5630c38fb1ae39b1048e0cf802f4170dba8943f5e13510055357b3cb67a3bac1"} - const newResult = result.map((student) => { - const token = encryptTokenString( - "1078710248086446120", - "01", - student.id_tu, - student.id_moodle, - [InternalRoles.VERIFIED], - ); - return { - moodleId: student.id_moodle, - token, - }; - }); + const newResult = result + .filter((student) => !isNaN(parseInt(student.id_moodle))) + .filter((student) => !oldMoodleIds.includes(student.id_moodle)) + .map((student) => { + const token = encryptTokenString( + "1078710248086446120", + "01", + student.id_tu, + student.id_moodle, + [InternalRoles.VERIFIED], + ); + return { + moodleId: student.id_moodle, + token, + }; + }); console.log("Result", newResult); - + console.log(`Found ${newResult.length} new moodleIds`); + // write result to result.json file fs.writeFileSync(path.resolve(__dirname, "result.json"), JSON.stringify(newResult, null, 4)); }); diff --git a/src/events/GuildCreateEvent.ts b/src/events/GuildCreateEvent.ts index 759c989..e9cd653 100644 --- a/src/events/GuildCreateEvent.ts +++ b/src/events/GuildCreateEvent.ts @@ -1,7 +1,5 @@ -import { ClientEventListener, ExecuteEvent } from "../../typings"; -import { ApplicationCommandData, ApplicationCommandOptionChoiceData, Client, ClientEvents, Guild } from "discord.js"; -import {GuildModel} from "../models/guilds"; -import { inspect } from "util"; +import { ExecuteEvent } from "../../typings"; +import { GuildModel } from "../models/guilds"; export const name = "guildCreate"; diff --git a/src/events/InteractionCreateEvent.ts b/src/events/InteractionCreateEvent.ts index 05f019b..ab6e9dc 100644 --- a/src/events/InteractionCreateEvent.ts +++ b/src/events/InteractionCreateEvent.ts @@ -1,9 +1,7 @@ -import { Command, ExecuteEvent } from "../../typings"; -import { ChatInputCommandInteraction, Collection, CommandInteractionOption } from "discord.js"; +import { ExecuteEvent } from "../../typings"; +import { ChatInputCommandInteraction, Collection } from "discord.js"; export const name = "interactionCreate"; -import {GuildModel} from "../models/guilds"; -import { DocumentType, isDocument } from "@typegoose/typegoose"; -import { GuildSettings } from "../models/guild_settings"; +import { GuildModel } from "../models/guilds"; export const execute: ExecuteEvent<"interactionCreate"> = async (client, interaction) => { diff --git a/src/events/MessageCreateEvent.ts b/src/events/MessageCreateEvent.ts index d17e746..f34c990 100644 --- a/src/events/MessageCreateEvent.ts +++ b/src/events/MessageCreateEvent.ts @@ -1,8 +1,6 @@ import { ExecuteEvent } from "../../typings"; -import { Collection, Guild, GuildMember, Message } from "discord.js"; +import { Collection } from "discord.js"; export const name = "messageCreate"; -import * as crypto from "crypto"; -import {UserModel} from "../models/users"; export const execute: ExecuteEvent<"messageCreate"> = async (client, message) => { if (message.partial) { diff --git a/src/events/ReadyEvent.ts b/src/events/ReadyEvent.ts index cc343ee..7918861 100644 --- a/src/events/ReadyEvent.ts +++ b/src/events/ReadyEvent.ts @@ -1,6 +1,6 @@ import { ExecuteEvent } from "../../typings"; import { ActivityType, ApplicationCommandData } from "discord.js"; -import {GuildModel} from "../models/guilds"; +import { GuildModel } from "../models/guilds"; export const execute: ExecuteEvent<"ready"> = async (client) => { // -- Setup Databases -- // @@ -27,7 +27,7 @@ export const execute: ExecuteEvent<"ready"> = async (client) => { // Guilds client.logger.info("Processing Guilds"); for (const g of [...client.guilds.cache.values()]) { - await GuildModel.prepareGuild(client,g); + await GuildModel.prepareGuild(client, g); } await client.user?.setPresence({ status: "online", activities: [{ name: "Sprechstunden.", type: ActivityType.Watching }], afk: false }); diff --git a/src/events/VoiceStateUpdateEvent.ts b/src/events/VoiceStateUpdateEvent.ts index a4cf509..8621e9b 100644 --- a/src/events/VoiceStateUpdateEvent.ts +++ b/src/events/VoiceStateUpdateEvent.ts @@ -1,13 +1,10 @@ import { ExecuteEvent } from "../../typings"; -import { Collection, ActionRow, Embed, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, APIMessageActionRowComponent } from "discord.js"; -import {GuildModel} from "../models/guilds"; -import { VoiceChannel } from "../models/voice_channels"; -import { Queue } from "../models/queues"; +import { Collection, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js"; +import { GuildModel } from "../models/guilds"; import { QueueEntry } from "../models/queue_entry"; export const name = "voiceStateUpdate"; -import {RoomModel} from "../models/rooms"; -import {UserModel} from "../models/users"; -import {EventModel, Event as EVT, eventType } from "../models/events"; +import { RoomModel } from "../models/rooms"; +import { Event as EVT, eventType } from "../models/events"; export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState, newState) => { const oldUserChannel = oldState.channel; diff --git a/src/events/guildMemberAddEvent.ts b/src/events/guildMemberAddEvent.ts index 5894b2d..879a349 100644 --- a/src/events/guildMemberAddEvent.ts +++ b/src/events/guildMemberAddEvent.ts @@ -1,8 +1,6 @@ -import { ClientEventListener, ExecuteEvent, StringReplacements } from "../../typings"; -import { ApplicationCommandData, ApplicationCommandOptionChoiceData, Client, ClientEvents, Guild } from "discord.js"; -import {GuildModel} from "../models/guilds"; -import {UserModel} from "../models/users"; -import { inspect } from "util"; +import { ExecuteEvent, StringReplacements } from "../../typings"; +import { GuildModel } from "../models/guilds"; +import { UserModel } from "../models/users"; export const name = "guildMemberAdd"; diff --git a/src/events/guildMemberRemoveEvent.ts b/src/events/guildMemberRemoveEvent.ts index 24a1cb7..8cfdce0 100644 --- a/src/events/guildMemberRemoveEvent.ts +++ b/src/events/guildMemberRemoveEvent.ts @@ -1,5 +1,5 @@ import { ExecuteEvent } from "../../typings"; -import {UserModel} from "../models/users"; +import { UserModel } from "../models/users"; export const name = "guildMemberRemove"; diff --git a/src/handlers/configHandler.ts b/src/handlers/configHandler.ts index 4dd5f51..f8ef3b9 100644 --- a/src/handlers/configHandler.ts +++ b/src/handlers/configHandler.ts @@ -1,5 +1,4 @@ import { BotConfig } from "../../typings"; -import dotenv from "dotenv"; /** * This is a readonly data class that holds the config of the bot. diff --git a/src/models/bot_roles.ts b/src/models/bot_roles.ts index d611639..4243bf4 100644 --- a/src/models/bot_roles.ts +++ b/src/models/bot_roles.ts @@ -5,10 +5,13 @@ export enum InternalRoles { SERVER_ADMIN = "server_admin", TUTOR = "tutor", VERIFIED = "verified", + ACTIVE_SESSION = "active_session", BOT_OWNER = "bot_owner", 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", diff --git a/src/models/database_mockup.ts b/src/models/database_mockup.ts index a69a2eb..83bc06b 100644 --- a/src/models/database_mockup.ts +++ b/src/models/database_mockup.ts @@ -18,10 +18,10 @@ const sessions = active: true, user: "313753317484527616", guild: "604601483920408576", - queue: "queue_id",//optional + queue: "queue_id", //optional role: "coach", // participant|coach|supervisor started_at: "1629567410185", - ended_at: undefined,//Set when active=false + ended_at: undefined, //Set when active=false end_certain: false, // Only set to true if session had a clean exit rooms: ["626065226986553343", "626065222936553347"], }, diff --git a/src/models/events.ts b/src/models/events.ts index 600d211..135d46a 100644 --- a/src/models/events.ts +++ b/src/models/events.ts @@ -23,27 +23,27 @@ export class Event { /** * The Unix Time Stamp of the Event */ - @prop({required: true}) + @prop({ required: true }) timestamp!: string; /** * The Event Type */ - @prop({required: true, enum: eventType, default: eventType.other}) + @prop({ required: true, enum: eventType, default: eventType.other }) type!: eventType; /** * Client ID or "me" */ - @prop({required: true}) + @prop({ required: true }) emitted_by!: string; /** * A Target that was affected */ - @prop({required: false}) + @prop({ required: false }) target?: string; /** * The Reason why the Event was Emitted */ - @prop({required: false}) + @prop({ required: false }) reason?: string; } diff --git a/src/models/queues.ts b/src/models/queues.ts index beae820..8c3fdfe 100644 --- a/src/models/queues.ts +++ b/src/models/queues.ts @@ -4,7 +4,6 @@ import { QueueEntry } from "./queue_entry"; import * as utils from "../utils/utils"; import { StringReplacements } from "../../typings"; import * as moment from "moment"; -import momentDurationFormatSetup from "moment-duration-format"; import { QueueSpan } from "./queue_span"; import { Guild } from "./guilds"; import { VoiceChannelSpawner } from "./voice_channel_spawner"; @@ -146,7 +145,7 @@ export class Queue { * Returns true if the ID is contained in the queue * @param discord_id the Discord ID to check if it's contained */ - public contains(this:DocumentType,discord_id: string): boolean { + public contains(this:DocumentType, discord_id: string): boolean { return this.entries.some(x => x.discord_id === discord_id); } /** diff --git a/src/models/sessions.ts b/src/models/sessions.ts index 60ac1ac..2ab769d 100644 --- a/src/models/sessions.ts +++ b/src/models/sessions.ts @@ -1,5 +1,5 @@ -import {RoomModel} from "./rooms"; -import {DocumentType, getModelForClass, prop, mongoose} from "@typegoose/typegoose"; +import { RoomModel } from "./rooms"; +import { DocumentType, getModelForClass, prop, mongoose } from "@typegoose/typegoose"; export enum sessionRole { "participant" = "participant", "coach" = "coach", @@ -10,12 +10,12 @@ export class Session { /** * Whether the Session is currently active */ - @prop({required: true}) + @prop({ required: true }) active!: boolean; /** * The User that the Session belongs to */ - @prop({required: true}) + @prop({ required: true }) user!: string; /** * A Guild that the Session belongs to @@ -30,7 +30,7 @@ export class Session { /** * The Role that the user plays */ - @prop({required: true, enum: sessionRole}) + @prop({ required: true, enum: sessionRole }) role!: sessionRole; /** * The Start Timestamp @@ -45,12 +45,12 @@ export class Session { /** * Only set to true if session had a clean exit */ - @prop({required: true}) + @prop({ required: true }) end_certain!: boolean; /** * The Room IDs That Were Visited in the Session */ - @prop({required: true, type: () => [String], default: []}) + @prop({ required: true, type: () => [String], default: [] }) rooms!: mongoose.Types.Array; /** diff --git a/src/models/slash_command_settings.ts b/src/models/slash_command_settings.ts index 1cb0de0..f9656fc 100644 --- a/src/models/slash_command_settings.ts +++ b/src/models/slash_command_settings.ts @@ -1,11 +1,11 @@ import { SlashCommandPermission } from "./slash_command_permission"; import { PermissionResolvable } from "discord.js"; -import { ArraySubDocumentType, SubDocumentType, getModelForClass, prop, mongoose } from "@typegoose/typegoose"; +import { ArraySubDocumentType, getModelForClass, prop, mongoose } from "@typegoose/typegoose"; export class SlashCommandSettings { /** * The original Command name used to retrieve it from the event handler */ - @prop({ required: true,sparse: true }) + @prop({ required: true, sparse: true }) internal_name!: string; /** * The Command Name overwrite diff --git a/src/models/text_channels.ts b/src/models/text_channels.ts index b40ae97..dc4220a 100644 --- a/src/models/text_channels.ts +++ b/src/models/text_channels.ts @@ -1,5 +1,5 @@ import { ChannelType, TextChannelType } from "discord.js"; -import {getModelForClass, prop} from "@typegoose/typegoose"; +import { getModelForClass, prop } from "@typegoose/typegoose"; /** * Database Representation of a Discord Channel @@ -28,9 +28,9 @@ export interface Channel { } export class TextChannel implements Channel { - @prop({required: true}) + @prop({ required: true }) _id!: string; - @prop({ required: true, type: Number, enum: [ChannelType.DM, ChannelType.GroupDM, ChannelType.GuildAnnouncement, ChannelType.PublicThread, ChannelType.PrivateThread, ChannelType.AnnouncementThread, ChannelType.GuildText, ChannelType.GuildForum, ChannelType.GuildVoice, ChannelType.GuildStageVoice]}) + @prop({ required: true, type: Number, enum: [ChannelType.DM, ChannelType.GroupDM, ChannelType.GuildAnnouncement, ChannelType.PublicThread, ChannelType.PrivateThread, ChannelType.AnnouncementThread, ChannelType.GuildText, ChannelType.GuildForum, ChannelType.GuildVoice, ChannelType.GuildStageVoice] }) channel_type!: TextChannelType; @prop({ required: true }) managed!: boolean; @@ -51,7 +51,7 @@ export class TextChannel implements Channel { /** * WHETHER THE CHANNEL IS CAPS-ONLY */ - @prop({default: false}) + @prop({ default: false }) rage_channel?: boolean; } diff --git a/src/models/voice_channel_spawner.ts b/src/models/voice_channel_spawner.ts index aa7e766..ec43e94 100644 --- a/src/models/voice_channel_spawner.ts +++ b/src/models/voice_channel_spawner.ts @@ -1,5 +1,5 @@ import { PermissionOverwriteData } from "./permission_overwrite_data"; -import {prop,mongoose, getModelForClass, ArraySubDocumentType} from "@typegoose/typegoose"; +import { prop, mongoose, getModelForClass, ArraySubDocumentType } from "@typegoose/typegoose"; export class VoiceChannelSpawner { /** @@ -10,12 +10,12 @@ export class VoiceChannelSpawner { /** * The Roles that can moderate this channel */ - @prop({required: true, type: () => [String], default: []}) + @prop({ required: true, type: () => [String], default: [] }) supervisor_roles!: mongoose.Types.Array; /** * The Channel Permissions */ - @prop({required: true, type: () => [PermissionOverwriteData], default: []}) + @prop({ required: true, type: () => [PermissionOverwriteData], default: [] }) permission_overwrites!: mongoose.Types.DocumentArray>; /** * Limit the amount of Users that can join the channel @@ -25,17 +25,17 @@ export class VoiceChannelSpawner { /** * The Name of the Channel; use ${owner} and so on to create dynamic channel names */ - @prop({required: true, default: "${owner_name}'s VC"}) + @prop({ required: true, default: "${owner_name}'s VC" }) name!: string; /** * Whether the Channel should initially be locked or not */ - @prop({default: false}) + @prop({ default: false }) lock_initially?: boolean; /** * Whether the Channel should initially be hidden or not */ - @prop({default: false}) + @prop({ default: false }) hide_initially?: boolean; /** * The Category Channel ID diff --git a/src/models/week_timestamp.ts b/src/models/week_timestamp.ts index f591b65..4615973 100644 --- a/src/models/week_timestamp.ts +++ b/src/models/week_timestamp.ts @@ -39,7 +39,7 @@ export class WeekTimestamp { /** * The Day of the Week */ - @prop({ required: true,enum: Weekday }) + @prop({ required: true, enum: Weekday }) weekday!: Weekday; /** diff --git a/src/utils/embeds.ts b/src/utils/embeds.ts index 7e095bf..31a7745 100644 --- a/src/utils/embeds.ts +++ b/src/utils/embeds.ts @@ -1,6 +1,4 @@ -import { ColorResolvable, CommandInteraction, DMChannel, Guild, GuildResolvable, Interaction, InteractionReplyOptions, Message, Embed, NewsChannel, PartialDMChannel, TextBasedChannel, TextChannel, ThreadChannel, UserResolvable, EmbedBuilder, InteractionResponse } from "discord.js"; -import { APIMessage } from "discord-api-types/v9"; -import * as utils from "./utils"; +import { CommandInteraction, DMChannel, Message, NewsChannel, TextChannel, ThreadChannel, EmbedBuilder, InteractionResponse } from "discord.js"; import { SimpleEmbedOptions } from "../../typings"; export async function SimpleEmbed(interaction: Message | CommandInteraction | DMChannel | TextChannel | NewsChannel | ThreadChannel, title: string, description: string): Promise; diff --git a/src/utils/errors.ts b/src/utils/errors.ts index edde234..8dac797 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,6 +1,5 @@ -import { DiscordAPIError, Message, EmbedBuilder } from "discord.js"; +import { Message, EmbedBuilder } from "discord.js"; import * as discord from "discord.js"; -import { APIMessage } from "discord-api-types/v9"; // : (message: Message, error: string, deleteinterval: number => Promise) export const errorMessage = async (interaction: Message | discord.CommandInteraction, error: string | Error, deleteinterval?: number) => { if (!interaction.channel) { diff --git a/src/utils/general.ts b/src/utils/general.ts index 13b4cd5..7b19ff6 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -1,13 +1,22 @@ import { ConfigHandler } from "./../handlers/configHandler"; import { DBRole, InternalRoles, RoleScopes } from "./../models/bot_roles"; -import ChannelType, { CommandInteraction, Guild, GuildMember, GuildMemberResolvable, GuildResolvable, Interaction, Message, RoleResolvable, UserResolvable } from "discord.js"; +import ChannelType, { + CommandInteraction, + Guild, + GuildMember, + GuildMemberResolvable, + GuildResolvable, + Interaction, + Message, + RoleResolvable, + User, + UserResolvable +} from "discord.js"; import moment from "moment"; import { Command, StringReplacements } from "../../typings"; -import { promisify } from "util"; -import {GuildModel} from "../models/guilds"; -import glob from "glob"; +import { GuildModel } from "../models/guilds"; import { Bot } from "../bot"; -import {UserModel} from "../models/users"; +import { UserModel } from "../models/users"; import * as cryptojs from "crypto-js"; import { DocumentType } from "@typegoose/typegoose"; @@ -366,6 +375,8 @@ function checkArgument(value: unknown, name: string) { } + + /** * The Possible Annotations for queue Stayings */ @@ -382,4 +393,38 @@ export enum QueueStayOptions { * Annotates user already left */ LEFT, +} + +/** + * Assigns an existing role to a member of a guild. + * @param g Guild + * @param user User to assign the role to + * @param roleName name of the role + */ +export async function assignRoleToUser(g: Guild, user: User, roleName: string) { + const roles = await g.roles.fetch(); + const role = roles.find(role => role.name.toLowerCase() === roleName.toLowerCase()); + const member = g.members.resolve(user); + if (role && member && !member.roles.cache.has(role.id)) { + await member.roles.add(role); + } else { + console.error(`Could not assign role: ${roleName} to ${user.username} on guild: ${g.name}`) + } +} + +/** + * Removes the given role from a user + * @param g Guild + * @param user User to remove the role from + * @param roleName name of the role + */ +export async function removeRoleFromUser(g: Guild, user: User, roleName: string) { + const roles = await g.roles.fetch(); + const role = roles.find(role => role.name.toLowerCase() === roleName.toLowerCase()); + const member = g.members.resolve(user); + if (role && member && member.roles.cache.has(role.id)) { + await member.roles.remove(role); + } else { + console.error(`Could not remove role: ${roleName} from ${user.username} on guild: ${g.name}`) + } } \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 21d67ce..3da88d2 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -9,7 +9,7 @@ import * as errors from "./errors"; import * as general from "./general"; import * as embeds from "./embeds"; import * as voice from "./voice"; -export {errors, general, embeds, voice}; +export { errors, general, embeds, voice }; // module.exports = { //TODO dynamically read new Util classes // errors: require('./errors'), // // functions: require('./functions'), diff --git a/src/utils/voice.ts b/src/utils/voice.ts index ce8d066..dec2481 100644 --- a/src/utils/voice.ts +++ b/src/utils/voice.ts @@ -1,7 +1,7 @@ import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; import { ChannelType, Guild, GuildMember, GuildPremiumTier, OverwriteData } from "discord.js"; import { Bot } from "../bot"; -import {GuildModel} from "../models/guilds"; +import { GuildModel } from "../models/guilds"; import { VoiceChannel } from "../models/voice_channels"; import { VoiceChannelSpawner } from "../models/voice_channel_spawner"; import { mongoose } from "@typegoose/typegoose";