From bcc18405c3d7610a66aa833a0f749ad0ce4cfa8c Mon Sep 17 00:00:00 2001 From: Rdeisenroth Date: Mon, 23 Oct 2023 02:23:22 +0200 Subject: [PATCH] start migrating to typegoose --- package-lock.json | 31 ++ package.json | 1 + src/bot.ts | 10 +- src/commands/admin/decrypttoken.ts | 6 +- src/commands/admin/fixverify.ts | 21 +- src/commands/admin/generatetoken.ts | 7 +- src/commands/admin/genverifyroles.ts | 16 +- src/commands/admin/member/lookup-by.ts | 26 +- src/commands/admin/member/lookup.ts | 10 +- src/commands/admin/member/unverify.ts | 10 +- src/commands/admin/queue/fixup.ts | 8 +- src/commands/admin/queue/info.ts | 6 +- src/commands/admin/queue/kick.ts | 6 +- src/commands/admin/queue/list.ts | 6 +- src/commands/admin/queue/lock.ts | 7 +- src/commands/admin/queue/unlock.ts | 7 +- src/commands/admin/queue/userrank.ts | 16 +- src/commands/admin/queue/userstats.ts | 10 +- src/commands/admin/session/list.ts | 16 +- src/commands/admin/session/terminate.ts | 16 +- src/commands/admin/session/terminateall.ts | 16 +- src/commands/coach/info.ts | 10 +- src/commands/coach/queue/info.ts | 8 +- src/commands/coach/queue/list.ts | 8 +- src/commands/coach/queue/next.ts | 29 +- src/commands/coach/queue/pick.ts | 32 +- src/commands/coach/session/info.ts | 10 +- src/commands/coach/session/quit.ts | 10 +- src/commands/coach/session/start.ts | 19 +- src/commands/config/commands/permission.ts | 4 +- src/commands/config/commands/rename.ts | 4 +- src/commands/config/commands/visibility.ts | 4 +- src/commands/config/queue/add_unlock_time.ts | 8 +- src/commands/config/queue/autolock.ts | 4 +- src/commands/config/queue/get_schedules.ts | 9 +- .../config/queue/remove_unlock_time.ts | 13 +- src/commands/config/queue/set_category.ts | 14 +- .../config/queue/set_closing_shift.ts | 4 +- .../config/queue/set_opening_shift.ts | 4 +- src/commands/config/queue/set_text_channel.ts | 4 +- src/commands/create-queue.ts | 12 +- src/commands/help.ts | 4 +- src/commands/ping.ts | 1 + src/commands/queue/info.ts | 4 +- src/commands/queue/join.ts | 8 +- src/commands/queue/leave.ts | 4 +- src/commands/queue/list.ts | 8 +- src/commands/setjointocreate.ts | 12 +- src/commands/setqueue.ts | 13 +- src/commands/verify.ts | 2 +- src/commands/voice/close.ts | 10 +- src/commands/voice/kick.ts | 8 +- src/commands/voice/lock.ts | 8 +- src/commands/voice/permit.ts | 8 +- src/commands/voice/togglelock.ts | 8 +- src/commands/voice/togglevisibility.ts | 8 +- src/commands/voice/transfer.ts | 8 +- src/commands/voice/unlock.ts | 8 +- .../buttons/queue/leave.ts | 13 +- .../buttons/queue/refresh.ts | 13 +- .../buttons/queue/stay.ts | 11 +- src/crypto_test.ts | 4 +- src/events/GuildCreateEvent.ts | 4 +- src/events/InteractionCreateEvent.ts | 4 +- src/events/MessageCreateEvent.ts | 2 +- src/events/ReadyEvent.ts | 4 +- src/events/VoiceStateUpdateEvent.ts | 43 +- src/events/guildMemberAddEvent.ts | 12 +- src/events/guildMemberRemoveEvent.ts | 11 +- src/models/bot_roles.ts | 76 +-- src/models/events.ts | 61 +-- src/models/guild_settings.ts | 129 ++--- src/models/guilds.ts | 329 +++++------- src/models/permission_overwrite_data.ts | 50 +- src/models/queue_entry.ts | 53 +- src/models/queue_span.ts | 101 ++-- src/models/queues.ts | 478 +++++++----------- src/models/rooms.ts | 262 +++++----- src/models/sessions.ts | 125 ++--- src/models/slash_command_permission.ts | 44 +- src/models/slash_command_settings.ts | 91 +--- src/models/text_channels.ts | 77 +-- src/models/users.ts | 141 ++---- src/models/voice_channel_spawner.ts | 93 +--- src/models/voice_channels.ts | 209 +++----- src/models/week_timestamp.ts | 173 ++++++- src/utils/general.ts | 155 +----- src/utils/voice.ts | 16 +- 88 files changed, 1388 insertions(+), 1980 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bdfbb6..de9ca6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@discordjs/voice": "^0.16.0", + "@typegoose/typegoose": "^11.6.0", "@types/node": "^20.8.4", "chartjs-node-canvas": "^4.1.6", "cheerio": "^1.0.0-rc.12", @@ -1531,6 +1532,24 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, + "node_modules/@typegoose/typegoose": { + "version": "11.6.0", + "resolved": "https://registry.npmjs.org/@typegoose/typegoose/-/typegoose-11.6.0.tgz", + "integrity": "sha512-c6m5aTrv9tzqLmtR/txlg4sFKXfupBXMJlO4yRfg757rK5h2YfIoshMG3yi+0IEe8gkF5utCV/uh9vf8a+re4Q==", + "dependencies": { + "lodash": "^4.17.20", + "loglevel": "^1.8.1", + "reflect-metadata": "^0.1.13", + "semver": "^7.5.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.17.0" + }, + "peerDependencies": { + "mongoose": "~7.6.1" + } + }, "node_modules/@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -5320,6 +5339,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loglevel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz", + "integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/loupe": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", diff --git a/package.json b/package.json index 47dcd48..4824117 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "license": "ISC", "dependencies": { "@discordjs/voice": "^0.16.0", + "@typegoose/typegoose": "^11.6.0", "@types/node": "^20.8.4", "chartjs-node-canvas": "^4.1.6", "cheerio": "^1.0.0-rc.12", diff --git a/src/bot.ts b/src/bot.ts index d50fc9b..d3c365e 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -7,11 +7,12 @@ import glob from "glob-promise"; import { promisify } from "util"; import * as fs from "fs"; import * as utils from "./utils/utils"; -import GuildSchema from "./models/guilds"; +import {GuildModel} from "./models/guilds"; import parser from "yargs-parser"; import mongoose from "mongoose"; import path from "path/posix"; import { QueueSpan } from "./models/queue_span"; +import { WeekTimestamp } from "./models/week_timestamp"; export class Bot extends Client { public logger: ConsolaInstance = consola; public commands: Collection = new Collection(); @@ -130,6 +131,7 @@ export class Bot extends Client { 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) => { + // 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)); @@ -139,7 +141,7 @@ export class Bot extends Client { for (const g of this.guilds.cache.values()) { console.log(new Date().toLocaleString()); - const guildData = await GuildSchema.findById(g.id); + const guildData = await GuildModel.findById(g.id); if (!guildData) { return; } @@ -149,8 +151,8 @@ export class Bot extends Client { } if (queueData.opening_times.map(x => new QueueSpan( - new utils.general.WeekTimestamp(x.begin.weekday, x.begin.hour, x.begin.minute), - new utils.general.WeekTimestamp(x.end.weekday, x.end.hour, x.end.minute), + new WeekTimestamp(x.begin.weekday, x.begin.hour, x.begin.minute), + new WeekTimestamp(x.end.weekday, x.end.hour, x.end.minute), x.openShift, x.closeShift, x.startDate, diff --git a/src/commands/admin/decrypttoken.ts b/src/commands/admin/decrypttoken.ts index fb16e16..035b949 100644 --- a/src/commands/admin/decrypttoken.ts +++ b/src/commands/admin/decrypttoken.ts @@ -1,9 +1,9 @@ import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../typings"; import "moment-duration-format"; -import UserSchema, { User } from "../../models/users"; -import GuildSchema from "../../models/guilds"; -import { DBRole, DBRoleDocument, InternalRoles, RoleScopes } from "../../models/bot_roles"; +import {UserModel, User } from "../../models/users"; +import {GuildModel} from "../../models/guilds"; +import { DBRole, InternalRoles, RoleScopes } from "../../models/bot_roles"; import { Types } from "mongoose"; diff --git a/src/commands/admin/fixverify.ts b/src/commands/admin/fixverify.ts index f911767..81269c5 100644 --- a/src/commands/admin/fixverify.ts +++ b/src/commands/admin/fixverify.ts @@ -1,10 +1,11 @@ import { Message, Role } from "discord.js"; import { Command } from "../../../typings"; import "moment-duration-format"; -import UserSchema, { User } from "../../models/users"; -import GuildSchema from "../../models/guilds"; -import { DBRole, DBRoleDocument, InternalRoles, RoleScopes } from "../../models/bot_roles"; +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"; @@ -27,7 +28,7 @@ const command: Command = { const guild = interaction.guild; const roles = await interaction.guild.roles.fetch(); const members = await interaction.guild.members.fetch(); - const dbGuild = (await GuildSchema.findOne({ _id: guild.id }))!; + 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"); @@ -36,11 +37,11 @@ const command: Command = { 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, DBRoleDocument, InternalRoles][])) { + 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([]); + if (!dbGuild.guild_settings.roles) dbGuild.guild_settings.roles = new Types.DocumentArray([]); dbGuild.guild_settings.roles.push({ internal_name: irn, role_id: r.id, @@ -59,16 +60,16 @@ const command: Command = { } } - const users = await UserSchema.find({}); + 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, DBRoleDocument, InternalRoles][])) { + 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._id); + if (!u.token_roles) u.token_roles = new Types.Array(); + u.token_roles.push(dbr); await u.save(); } } diff --git a/src/commands/admin/generatetoken.ts b/src/commands/admin/generatetoken.ts index 904bc36..e2c202b 100644 --- a/src/commands/admin/generatetoken.ts +++ b/src/commands/admin/generatetoken.ts @@ -1,11 +1,6 @@ -import { ApplicationCommandOptionType, Message, Role } from "discord.js"; +import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../typings"; import "moment-duration-format"; -import UserSchema, { User } from "../../models/users"; -import GuildSchema from "../../models/guilds"; -import { DBRole, DBRoleDocument, InternalRoles, RoleScopes } from "../../models/bot_roles"; -import { Types } from "mongoose"; - /** diff --git a/src/commands/admin/genverifyroles.ts b/src/commands/admin/genverifyroles.ts index e49c12a..9aa8e7a 100644 --- a/src/commands/admin/genverifyroles.ts +++ b/src/commands/admin/genverifyroles.ts @@ -1,10 +1,10 @@ import { Message, Role } from "discord.js"; import { Command } from "../../../typings"; import "moment-duration-format"; -import UserSchema, { User } from "../../models/users"; -import GuildSchema from "../../models/guilds"; -import { DBRole, DBRoleDocument, InternalRoles, RoleScopes } from "../../models/bot_roles"; -import { Types } from "mongoose"; +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"; @@ -27,7 +27,7 @@ const command: Command = { const guild = interaction.guild; const roles = await interaction.guild.roles.fetch(); const members = await interaction.guild.members.fetch(); - const dbGuild = (await GuildSchema.findOne({ _id: guild.id }))!; + 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"); @@ -36,11 +36,13 @@ const command: Command = { 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, DBRoleDocument, InternalRoles][])) { + 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 Types.DocumentArray([]); + 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, diff --git a/src/commands/admin/member/lookup-by.ts b/src/commands/admin/member/lookup-by.ts index 047eb9c..041bad5 100644 --- a/src/commands/admin/member/lookup-by.ts +++ b/src/commands/admin/member/lookup-by.ts @@ -1,11 +1,13 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema, { User, UserDocument } from "../../../models/users"; -import SessionSchema from "../../../models/sessions"; -import QueueSchema from "../../../models/queues"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel, User } from "../../../models/users"; +import {SessionModel} from "../../../models/sessions"; +import {QueueModel} from "../../../models/queues"; import { FilterQuery } from "mongoose"; +import { DocumentType, mongoose } from "@typegoose/typegoose"; +import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; const command: Command = { name: "lookup-by", @@ -58,17 +60,17 @@ const command: Command = { await interaction.deferReply({ ephemeral: true }); const type = interaction.options.getString("type", true); const query = interaction.options.getString("query", true); - let userQuery: FilterQuery = {}; - let userData: (User & { _id: string; }) | undefined; + let userQuery: FilterQuery> = {}; + let userData: DocumentType | null | undefined; if (type === "discord-tag") { const members = await interaction.guild?.members.fetch(); const member = members?.find(x => x.user.tag === query); - userData = { + userData = new UserModel({ _id: member?.id ?? "", - server_roles: [], - sessions: [], - token_roles: [], - }; + server_roles: new mongoose.Types.Array(), + sessions: new mongoose.Types.DocumentArray([]), + token_roles: new mongoose.Types.DocumentArray([]), + } as FilterOutFunctionKeys); } else { if (type === "tu-id") { userQuery = { tu_id: query }; @@ -77,7 +79,7 @@ const command: Command = { } else { userQuery = { _id: query }; } - userData = (await UserSchema.findOne(userQuery))?.toObject(); + userData = await UserModel.findOne(userQuery); } // user = await user.fetch(); diff --git a/src/commands/admin/member/lookup.ts b/src/commands/admin/member/lookup.ts index 4a7767f..a628bb8 100644 --- a/src/commands/admin/member/lookup.ts +++ b/src/commands/admin/member/lookup.ts @@ -1,10 +1,10 @@ import { ApplicationCommandOptionType, EmbedData, EmbedField, Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import SessionSchema from "../../../models/sessions"; -import QueueSchema from "../../../models/queues"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {SessionModel} from "../../../models/sessions"; +import {QueueModel} from "../../../models/queues"; const command: Command = { name: "lookup", @@ -33,7 +33,7 @@ const command: Command = { await interaction.deferReply({ ephemeral: true }); let user = interaction.options.getUser("user", true); user = await user.fetch(); - const userData = await UserSchema.findById(user.id); + const userData = await UserModel.findById(user.id); if (!userData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Verification System", text: `User ${user} not found in database.`, empheral: true }); diff --git a/src/commands/admin/member/unverify.ts b/src/commands/admin/member/unverify.ts index b7b86bd..8c7e788 100644 --- a/src/commands/admin/member/unverify.ts +++ b/src/commands/admin/member/unverify.ts @@ -1,10 +1,10 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import SessionSchema from "../../../models/sessions"; -import QueueSchema from "../../../models/queues"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {SessionModel} from "../../../models/sessions"; +import {QueueModel} from "../../../models/queues"; const command: Command = { name: "unverify", @@ -40,7 +40,7 @@ const command: Command = { let user = interaction.options.getUser("user", true); const reason = interaction.options.getString("reason"); user = await user.fetch(); - const userData = await UserSchema.findById(user.id); + const userData = await UserModel.findById(user.id); if (!userData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Verification System", text: `User ${user} not found in database.`, empheral: true }); diff --git a/src/commands/admin/queue/fixup.ts b/src/commands/admin/queue/fixup.ts index 0316fcf..730f44e 100644 --- a/src/commands/admin/queue/fixup.ts +++ b/src/commands/admin/queue/fixup.ts @@ -1,8 +1,8 @@ import { ApplicationCommandOptionType, EmbedField, Message, Role } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "fixup", @@ -26,7 +26,7 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } @@ -66,7 +66,7 @@ const command: Command = { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Active Session Role Members Could not be found.", empheral: true }); } const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); const as_oldMemberCount = as_role_members.size; diff --git a/src/commands/admin/queue/info.ts b/src/commands/admin/queue/info.ts index af206d0..6157e20 100644 --- a/src/commands/admin/queue/info.ts +++ b/src/commands/admin/queue/info.ts @@ -1,7 +1,7 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "info", @@ -28,7 +28,7 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/admin/queue/kick.ts b/src/commands/admin/queue/kick.ts index 5877b8d..0fa54c4 100644 --- a/src/commands/admin/queue/kick.ts +++ b/src/commands/admin/queue/kick.ts @@ -1,8 +1,8 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "kick", @@ -40,7 +40,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/admin/queue/list.ts b/src/commands/admin/queue/list.ts index 41bac61..52ceae1 100644 --- a/src/commands/admin/queue/list.ts +++ b/src/commands/admin/queue/list.ts @@ -1,8 +1,8 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "list", @@ -32,7 +32,7 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/admin/queue/lock.ts b/src/commands/admin/queue/lock.ts index ebf1699..6109b72 100644 --- a/src/commands/admin/queue/lock.ts +++ b/src/commands/admin/queue/lock.ts @@ -1,9 +1,8 @@ -import { VoiceChannelDocument } from "./../../../models/voice_channels"; import { ApplicationCommandOptionType, EmbedField, Message, VoiceChannel } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "lock", @@ -28,7 +27,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/admin/queue/unlock.ts b/src/commands/admin/queue/unlock.ts index 188a56c..04b6f24 100644 --- a/src/commands/admin/queue/unlock.ts +++ b/src/commands/admin/queue/unlock.ts @@ -1,9 +1,8 @@ -import { VoiceChannelDocument } from "../../../models/voice_channels"; import { ApplicationCommandOptionType, EmbedField, Message, VoiceChannel } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "unlock", @@ -28,7 +27,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/admin/queue/userrank.ts b/src/commands/admin/queue/userrank.ts index d4cce65..4ba9646 100644 --- a/src/commands/admin/queue/userrank.ts +++ b/src/commands/admin/queue/userrank.ts @@ -1,9 +1,9 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import RoomSchema from "../../../models/rooms"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {RoomModel} from "../../../models/rooms"; const command: Command = { name: "userrank", @@ -35,7 +35,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } @@ -49,16 +49,16 @@ const command: Command = { // await g.members.fetch(); const fields: EmbedField[] = []; - const rooms = await RoomSchema.find({}); + const rooms = await RoomModel.find({}); for (const u of ( await Promise.all( - (await UserSchema.find()) + (await UserModel.find()) .map(async (x, i, a) => { console.log(`Processing User data of ${x._id} (${i}/${a.length})`); return { _id: x._id, - roomCount: await RoomSchema.getParticipantRoomCount(x._id, rooms), + roomCount: await RoomModel.getParticipantRoomCount(x._id, rooms), }; }, )) @@ -67,7 +67,7 @@ const command: Command = { ) { // console.log(u._id); const member = await g.members.fetch(u._id); - const roomCount = await RoomSchema.getParticipantRoomCount(u._id); + const roomCount = await RoomModel.getParticipantRoomCount(u._id); fields.push({ name: member.displayName, value: diff --git a/src/commands/admin/queue/userstats.ts b/src/commands/admin/queue/userstats.ts index 160aca7..47f3ea0 100644 --- a/src/commands/admin/queue/userstats.ts +++ b/src/commands/admin/queue/userstats.ts @@ -1,9 +1,9 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import RoomSchema from "../../../models/rooms"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {RoomModel} from "../../../models/rooms"; const command: Command = { name: "userstats", @@ -34,7 +34,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } @@ -48,7 +48,7 @@ const command: Command = { let user = interaction.options.getUser("user", true); user = await user.fetch(); - const roomCount = await RoomSchema.getParticipantRoomCount(user); + const roomCount = await RoomModel.getParticipantRoomCount(user); const fields: EmbedField[] = [ { diff --git a/src/commands/admin/session/list.ts b/src/commands/admin/session/list.ts index d2bfd90..2474491 100644 --- a/src/commands/admin/session/list.ts +++ b/src/commands/admin/session/list.ts @@ -1,10 +1,10 @@ import { EmbedField, Message } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import SessionSchema from "../../../models/sessions"; -import QueueSchema from "../../../models/queues"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {SessionModel} from "../../../models/sessions"; +import {QueueModel} from "../../../models/queues"; const command: Command = { name: "list", @@ -24,9 +24,9 @@ const command: Command = { } await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; - const sessions = await SessionSchema.find({ guild: g.id, active: true }); - const sortedSessions = sessions.sort((x, y) => (+x.started_at) - (+y.started_at)); + const guildData = (await GuildModel.findById(g.id))!; + const sessions = await SessionModel.find({ guild: g.id, active: true }); + const sortedSessions = sessions.sort((x, y) => (+x.started_at!) - (+y.started_at!)); const fields: EmbedField[] = []; for (const e of sortedSessions) { @@ -35,7 +35,7 @@ const command: Command = { const queue = guildData.queues.id(e.queue); fields.push({ name: member.displayName, value: - `-started_at: ` + `-started_at: ` + `\n-rooms: ${e.getRoomAmount()}` + `\n-participants:${participants}` + `\n-queue:${queue?.name}`, diff --git a/src/commands/admin/session/terminate.ts b/src/commands/admin/session/terminate.ts index ab5f71a..344c3f8 100644 --- a/src/commands/admin/session/terminate.ts +++ b/src/commands/admin/session/terminate.ts @@ -1,10 +1,10 @@ import { ApplicationCommandOptionType, EmbedField, GuildMember, Message, User } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import SessionSchema from "../../../models/sessions"; -import QueueSchema from "../../../models/queues"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {SessionModel} from "../../../models/sessions"; +import {QueueModel} from "../../../models/queues"; const command: Command = { name: "terminate", @@ -32,14 +32,14 @@ const command: Command = { } await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; let member = interaction.options.getUser("coach", true); member = await member.fetch(); // if (!(member instanceof User)) { // return await client.utils.embeds.SimpleEmbed(interaction, { title: "Session System", text: ":X: invalid User", empheral: true }); // } - const sessions = await SessionSchema.find({ guild: g.id, active: true, user: member.id }); - const sortedSessions = sessions.sort((x, y) => (+x.started_at) - (+y.started_at)); + const sessions = await SessionModel.find({ guild: g.id, active: true, user: member.id }); + const sortedSessions = sessions.sort((x, y) => (+x.started_at!) - (+y.started_at!)); const dmChannel = await member.createDM(); @@ -54,7 +54,7 @@ const command: Command = { try { await client.utils.embeds.SimpleEmbed(dmChannel, { title: "Coaching System", text: "Your coaching Session was terminated by an administrator." - + `\n\\> Total Time Spent: ${moment.duration((+e.ended_at!) - (+e.started_at)).format("d[d ]h[h ]m[m ]s.S[s]")}` + + `\n\\> Total Time Spent: ${moment.duration((+e.ended_at!) - (+e.started_at!)).format("d[d ]h[h ]m[m ]s.S[s]")}` + `\n\\> Channels visited: ${e.getRoomAmount()}` + `\n\\> Participants: ${(await e.getParticipantAmount())}`, }); diff --git a/src/commands/admin/session/terminateall.ts b/src/commands/admin/session/terminateall.ts index 7e70503..b289495 100644 --- a/src/commands/admin/session/terminateall.ts +++ b/src/commands/admin/session/terminateall.ts @@ -1,10 +1,10 @@ import { EmbedField, GuildMember, Message, User } from "discord.js"; import moment from "moment"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import SessionSchema from "../../../models/sessions"; -import QueueSchema from "../../../models/queues"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; +import {SessionModel} from "../../../models/sessions"; +import {QueueModel} from "../../../models/queues"; const command: Command = { name: "terminateall", @@ -24,12 +24,12 @@ const command: Command = { } await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; // if (!(member instanceof User)) { // return await client.utils.embeds.SimpleEmbed(interaction, { title: "Session System", text: ":X: invalid User", empheral: true }); // } - const sessions = await SessionSchema.find({ guild: g.id, active: true }); - const sortedSessions = sessions.sort((x, y) => (+x.started_at) - (+y.started_at)); + const sessions = await SessionModel.find({ guild: g.id, active: true }); + const sortedSessions = sessions.sort((x, y) => (+x.started_at!) - (+y.started_at!)); @@ -46,7 +46,7 @@ const command: Command = { const dmChannel = await member.createDM(); await client.utils.embeds.SimpleEmbed(dmChannel, { title: "Coaching System", text: "Your coaching Session was terminated by an administrator." - + `\n\\> Total Time Spent: ${moment.duration((+e.ended_at!) - (+e.started_at)).format("d[d ]h[h ]m[m ]s.S[s]")}` + + `\n\\> Total Time Spent: ${moment.duration((+e.ended_at!) - (+e.started_at!)).format("d[d ]h[h ]m[m ]s.S[s]")}` + `\n\\> Channels visited: ${e.getRoomAmount()}` + `\n\\> Participants: ${(await e.getParticipantAmount())}`, }); diff --git a/src/commands/coach/info.ts b/src/commands/coach/info.ts index 347e198..d0aefba 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 GuildSchema from "../../models/guilds"; -import UserSchema from "../../models/users"; +import {GuildModel} from "../../models/guilds"; +import {UserModel} from "../../models/users"; const command: Command = { name: "info", @@ -22,10 +22,10 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has any Sessions const sessions = await (await userEntry.getSessions()).filter(x => x.guild && x.guild == g.id); if (!sessions.length) { @@ -38,7 +38,7 @@ const command: Command = { for (const session of sessions) { if (session.active || session.end_certain) { - total_time_spent += (session.active ? Date.now() : (+session.ended_at!)) - (+session.started_at); + total_time_spent += (session.active ? Date.now() : (+session.ended_at!)) - (+session.started_at!); } channel_count += session.getRoomAmount(); participants += await session.getParticipantAmount(); diff --git a/src/commands/coach/queue/info.ts b/src/commands/coach/queue/info.ts index 904a745..079ecb6 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 GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "info", @@ -20,13 +20,13 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); // We expect at most 1 active session per guild diff --git a/src/commands/coach/queue/list.ts b/src/commands/coach/queue/list.ts index 2851b74..a5828b0 100644 --- a/src/commands/coach/queue/list.ts +++ b/src/commands/coach/queue/list.ts @@ -1,8 +1,8 @@ import { ApplicationCommandOptionType, EmbedField, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "list", @@ -24,13 +24,13 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); // We expect at most 1 active session per guild diff --git a/src/commands/coach/queue/next.ts b/src/commands/coach/queue/next.ts index b99e2e1..080ac12 100644 --- a/src/commands/coach/queue/next.ts +++ b/src/commands/coach/queue/next.ts @@ -1,11 +1,12 @@ -import EventSchema, { Event as EVT, eventType } from "./../../../models/events"; +import {EventModel, 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 GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import RoomSchema 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"; const command: Command = { name: "next", @@ -33,13 +34,13 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); // We expect at most 1 active session per guild @@ -93,7 +94,7 @@ const command: Command = { // queueData.set("room_spawner", spawner); // await guildData.save(); } else { - spawner.supervisor_roles = spawner.supervisor_roles.concat(queue_channel_data?.supervisors ?? []); + spawner.supervisor_roles = new mongoose.Types.Array(...spawner.supervisor_roles.concat(queue_channel_data?.supervisors ?? [])); spawner.owner = user.id; if (spawner.name) { spawner.name = client.utils.general.interpolateString( @@ -107,14 +108,14 @@ const command: Command = { } else { spawner.name = spawner.name ?? `${member.displayName}' ${queueData.name} Room ${coachingSession.getRoomAmount() + 1}`; } - spawner.permission_overwrites = [ - ...entries.map(x => { + spawner.permission_overwrites = new mongoose.Types.DocumentArray( + entries.map(x => { return { id: x.discord_id, allow: ["ViewChannel", "Connect", "Speak", "Stream"], } as PermissionOverwriteData; }), - ]; + ); } // Spawn Room @@ -127,7 +128,7 @@ const command: Command = { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Channel could not be created.", empheral: true }); } - const roomData = await RoomSchema.create({ _id: room.id, active: true, tampered: false, end_certain: false, guild: g.id }); + const roomData = await RoomModel.create({ _id: room.id, active: true, tampered: false, end_certain: false, guild: g.id }); roomData.events.push({ emitted_by: "me", type: eventType.create_channel, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated room Creation` } as EVT); // Update Coach Session coachingSession.rooms.push(roomData._id); @@ -140,7 +141,7 @@ const command: Command = { console.log("coach queue next: Matches: Notify User"); try { // remove from queue - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueData = guildData.queues.id(queue)!; queueData.entries.remove({ _id: e._id }); await guildData.save(); @@ -171,7 +172,7 @@ const command: Command = { // Try to move try { const member = g.members.resolve(user)!; - roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id}` } as EVT); + roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id}`, target:member.id } as EVT); await member.voice.setChannel(room); } catch (error) { // Ignore Errors @@ -187,7 +188,7 @@ const command: Command = { // Try to move Coach try { await member.voice.setChannel(room); - roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id} (coach)` } as EVT); + roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id} (coach)`, target:member.id } as EVT); } catch (error) { // Ignore Errors } diff --git a/src/commands/coach/queue/pick.ts b/src/commands/coach/queue/pick.ts index c0d1385..852eccf 100644 --- a/src/commands/coach/queue/pick.ts +++ b/src/commands/coach/queue/pick.ts @@ -1,11 +1,13 @@ -import EventSchema, { Event as EVT, eventType } from "../../../models/events"; +import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; +import {EventModel, 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 GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import RoomSchema 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"; const command: Command = { name: "pick", @@ -41,13 +43,13 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); // We expect at most 1 active session per guild @@ -107,7 +109,7 @@ const command: Command = { // queueData.set("room_spawner", spawner); // await guildData.save(); } else { - spawner.supervisor_roles = spawner.supervisor_roles.concat(queue_channel_data?.supervisors ?? []); + spawner.supervisor_roles = new mongoose.Types.Array(...spawner.supervisor_roles.concat(queue_channel_data?.supervisors ?? [])); spawner.owner = user.id; if (spawner.name) { spawner.name = client.utils.general.interpolateString( @@ -121,12 +123,12 @@ const command: Command = { } else { spawner.name = spawner.name ?? `${member.displayName}' ${queueData.name} Room ${coachingSession.getRoomAmount() + 1}`; } - spawner.permission_overwrites = [ + spawner.permission_overwrites = new mongoose.Types.DocumentArray([ { id: pickedUser.id, allow: ["ViewChannel", "Connect", "Speak", "Stream"], - } as PermissionOverwriteData, - ]; + } as FilterOutFunctionKeys, + ]); } // Spawn Room @@ -139,7 +141,7 @@ const command: Command = { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Channel could not be created.", empheral: true }); } - const roomData = await RoomSchema.create({ _id: room.id, active: true, tampered: false, end_certain: false, guild: g.id }); + const roomData = await RoomModel.create({ _id: room.id, active: true, tampered: false, end_certain: false, guild: g.id }); roomData.events.push({ emitted_by: "me", type: eventType.create_channel, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated room Creation` } as EVT); // Update Coach Session coachingSession.rooms.push(roomData._id); @@ -147,7 +149,7 @@ const command: Command = { // Notify Match(es) try { - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueData = guildData.queues.id(queue)!; const user = await client.users.fetch(queueEntry.discord_id); console.log("coach queue pick: Matches: Notify User"); @@ -155,7 +157,7 @@ const command: Command = { await client.utils.embeds.SimpleEmbed((await user.createDM()), "Coaching system", `You found a Coach.\nPlease Join ${room} if you are not automatically moved.`); console.log("coach queue pick: Matches: Remove User from Queue"); // remove from queue - queueData.entries.remove({ _id: queueEntry._id }); + queueData.entries.pull({ _id: queueEntry._id }); await guildData.save(); const roles = await g.roles.fetch(); @@ -168,7 +170,7 @@ const command: Command = { // Try to move try { const member = g.members.resolve(user)!; - roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id}` } as EVT); + roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id}`, target:member.id } as EVT); await member.voice.setChannel(room); } catch (error) { // Ignore Errors @@ -183,7 +185,7 @@ const command: Command = { // Try to move Coach try { await member.voice.setChannel(room); - roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id} (coach)` } as EVT); + roomData.events.push({ emitted_by: "me", type: eventType.move_member, timestamp: Date.now().toString(), reason: `Queue System: '${queueData.name}' Queue automated member Move: ${member.id} (coach)`, target:member.id } as EVT); } catch (error) { // Ignore Errors } diff --git a/src/commands/coach/session/info.ts b/src/commands/coach/session/info.ts index 22cb88b..ad70fdd 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 GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; const command: Command = { name: "info", @@ -22,10 +22,10 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); // We expect at most 1 active session per guild @@ -36,7 +36,7 @@ const command: Command = { await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: ` - \\> Total Time Spent: ${moment.duration(Date.now() - (+coachingSession.started_at)).format("d[d ]h[h ]m[m ]s.S[s]")} + \\> Total Time Spent: ${moment.duration(Date.now() - (+coachingSession.started_at!)).format("d[d ]h[h ]m[m ]s.S[s]")} \n\\> Channels visited: ${coachingSession.getRoomAmount()} \n\\> Participants: ${(await coachingSession.getParticipantAmount())} `, empheral: true, diff --git a/src/commands/coach/session/quit.ts b/src/commands/coach/session/quit.ts index ad95fe0..7d7f29a 100644 --- a/src/commands/coach/session/quit.ts +++ b/src/commands/coach/session/quit.ts @@ -1,7 +1,7 @@ import { Message } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; +import {GuildModel} from "../../../models/guilds"; +import {UserModel} from "../../../models/users"; import moment from "moment"; const command: Command = { @@ -21,10 +21,10 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions const activeSessions = await userEntry.getActiveSessions(); // We expect at most 1 active session per guild @@ -45,7 +45,7 @@ const command: Command = { client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: `Your Session ended. - \n\\> Total Time Spent: ${moment.duration((+coachingSession.ended_at!) - (+coachingSession.started_at)).format("d[d ]h[h ]m[m ]s.S[s]")} + \n\\> Total Time Spent: ${moment.duration((+coachingSession.ended_at!) - (+coachingSession.started_at!)).format("d[d ]h[h ]m[m ]s.S[s]")} \n\\> Channels visited: ${coachingSession.getRoomAmount()} \n\\> Participants: ${(await coachingSession.getParticipantAmount())} `, empheral: true, diff --git a/src/commands/coach/session/start.ts b/src/commands/coach/session/start.ts index 1d65adc..79e34d0 100644 --- a/src/commands/coach/session/start.ts +++ b/src/commands/coach/session/start.ts @@ -1,9 +1,10 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; -import UserSchema from "../../../models/users"; -import SessionSchema, { sessionRole } from "../../../models/sessions"; -import { QueueDocument } from "../../../models/queues"; +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"; const command: Command = { name: "start", @@ -30,10 +31,10 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const user = client.utils.general.getUser(interaction); - const userEntry = await UserSchema.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); + const userEntry = await UserModel.findOneAndUpdate({ _id: user.id }, { _id: user.id }, { new: true, upsert: true, setDefaultsOnInsert: true }); // Check if User has Active Sessions if (await userEntry.hasActiveSessions()) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "You already Have an active Session.", empheral: true }); @@ -46,15 +47,15 @@ const command: Command = { } const queueName = interaction.options.getString("queue"); - let queueData: QueueDocument | undefined = guildData.queues.find(x => x.name === queueName); + let queueData: DocumentType | undefined = guildData.queues.find(x => x.name === queueName); if (!queueData) { // await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: `${queueName} could not be Found. Available Queues: ${guildData.queues.map(x => x.name).join(", ")}`, empheral: true }); // return; - queueData = (guildData.queues[0] as QueueDocument); + queueData = guildData.queues[0]; } // const queue = (guildData.queues[0] as QueueDocument); // Create New Session - const session = await SessionSchema.create({ active: true, user: user.id, guild: g.id, queue: queueData._id, role: sessionRole.coach, started_at: Date.now(), end_certain: false, rooms: [] }); + 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(); 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 8477c33..ea3f4ac 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 GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; const command: Command = { name: "permission", @@ -40,7 +40,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const cmdName = interaction.options.getString("original-name", true); if (!client.commands.has(cmdName)) { diff --git a/src/commands/config/commands/rename.ts b/src/commands/config/commands/rename.ts index 8115ab5..0a834a2 100644 --- a/src/commands/config/commands/rename.ts +++ b/src/commands/config/commands/rename.ts @@ -2,7 +2,7 @@ import { SlashCommandSettings } from "./../../../models/slash_command_settings"; import { ApplicationCommandOptionType, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; const command: Command = { name: "rename", @@ -35,7 +35,7 @@ const command: Command = { const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const originalCommandName = interaction.options.getString("original-name", true); const newCommandName = interaction.options.getString("new-name", true); diff --git a/src/commands/config/commands/visibility.ts b/src/commands/config/commands/visibility.ts index cfd9a81..6261971 100644 --- a/src/commands/config/commands/visibility.ts +++ b/src/commands/config/commands/visibility.ts @@ -2,7 +2,7 @@ import { SlashCommandSettings } from "./../../../models/slash_command_settings"; import { ApplicationCommandOptionType, Message } from "discord.js"; import path from "path"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; const command: Command = { name: "visibility", @@ -35,7 +35,7 @@ const command: Command = { const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const originalCommandName = interaction.options.getString("original-name", true).toLowerCase(); const shouldBeVisible = interaction.options.getBoolean("visible", true); diff --git a/src/commands/config/queue/add_unlock_time.ts b/src/commands/config/queue/add_unlock_time.ts index 86ff828..f54f155 100644 --- a/src/commands/config/queue/add_unlock_time.ts +++ b/src/commands/config/queue/add_unlock_time.ts @@ -2,7 +2,7 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; const command: Command = { @@ -36,7 +36,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const queueSpanString = interaction.options.getString("schedule", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); @@ -47,8 +47,8 @@ const command: Command = { let queueSpan: QueueSpan; try { queueSpan = QueueSpan.fromString(queueSpanString); - } catch (error: any) { - client.utils.embeds.SimpleEmbed(interaction, "Server config", `:x: Error: could not parse the QueueSpan \`${queueSpanString}\`:\n ${error.message}`); + } catch (error: unknown) { + client.utils.embeds.SimpleEmbed(interaction, "Server config", `:x: Error: could not parse the QueueSpan \`${queueSpanString}\`:\n ${(error as {message:unknown}).message}`); return; } queueData.opening_times.push(queueSpan); diff --git a/src/commands/config/queue/autolock.ts b/src/commands/config/queue/autolock.ts index 9f340c8..d73291d 100644 --- a/src/commands/config/queue/autolock.ts +++ b/src/commands/config/queue/autolock.ts @@ -2,7 +2,7 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import { GuildModel } from "../../../models/guilds"; const command: Command = { name: "autolock", @@ -35,7 +35,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const enable = interaction.options.getBoolean("enable", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); diff --git a/src/commands/config/queue/get_schedules.ts b/src/commands/config/queue/get_schedules.ts index 5168736..cb65032 100644 --- a/src/commands/config/queue/get_schedules.ts +++ b/src/commands/config/queue/get_schedules.ts @@ -2,8 +2,9 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; +import { WeekTimestamp } from "../../../models/week_timestamp"; const command: Command = { name: "get_schedules", @@ -30,7 +31,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); if (!queueData) { @@ -43,8 +44,8 @@ const command: Command = { text += "\n\nThe following times are scheduled for `unlocking` the queue:"; text += "\n```"; for (const schedule of queueData.opening_times.map(x => new QueueSpan( - new client.utils.general.WeekTimestamp(x.begin.weekday, x.begin.hour, x.begin.minute), - new client.utils.general.WeekTimestamp(x.end.weekday, x.end.hour, x.end.minute), + new WeekTimestamp(x.begin.weekday, x.begin.hour, x.begin.minute), + new WeekTimestamp(x.end.weekday, x.end.hour, x.end.minute), x.openShift, x.closeShift, x.startDate, diff --git a/src/commands/config/queue/remove_unlock_time.ts b/src/commands/config/queue/remove_unlock_time.ts index a61311b..2d151fb 100644 --- a/src/commands/config/queue/remove_unlock_time.ts +++ b/src/commands/config/queue/remove_unlock_time.ts @@ -2,8 +2,9 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; +import { WeekTimestamp } from "../../../models/week_timestamp"; const command: Command = { name: "remove_unlock_time", @@ -36,7 +37,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const queueSpanString = interaction.options.getString("schedule", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); @@ -47,14 +48,14 @@ const command: Command = { let queueSpan: QueueSpan; try { queueSpan = QueueSpan.fromString(queueSpanString); - } catch (error: any) { - client.utils.embeds.SimpleEmbed(interaction, "Server config", `:x: Error: could not parse the QueueSpan \`${queueSpanString}\`:\n ${error.message}`); + } catch (error: unknown) { + client.utils.embeds.SimpleEmbed(interaction, "Server config", `:x: Error: could not parse the QueueSpan \`${queueSpanString}\`:\n ${(error as {message:unknown}).message}`); return; } const match = queueData.opening_times.find(x => { const span = new QueueSpan( - new client.utils.general.WeekTimestamp(x.begin.weekday, x.begin.hour, x.begin.minute), - new client.utils.general.WeekTimestamp(x.end.weekday, x.end.hour, x.end.minute), + new WeekTimestamp(x.begin.weekday, x.begin.hour, x.begin.minute), + new WeekTimestamp(x.end.weekday, x.end.hour, x.end.minute), x.openShift, x.closeShift, x.startDate, diff --git a/src/commands/config/queue/set_category.ts b/src/commands/config/queue/set_category.ts index 74ec164..5ffebe5 100644 --- a/src/commands/config/queue/set_category.ts +++ b/src/commands/config/queue/set_category.ts @@ -1,8 +1,10 @@ +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 { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; +import { mongoose } from "@typegoose/typegoose"; const command: Command = { name: "set_category", @@ -35,7 +37,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const category = interaction.options.getChannel("category", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); @@ -44,18 +46,18 @@ const command: Command = { } if (queueData.room_spawner) { - queueData.room_spawner.parent = category.id; + queueData.room_spawner.set("parent", category.id); queueData.room_spawner.max_users = 5; queueData.room_spawner.lock_initially = true; queueData.room_spawner.hide_initially = true; } else { queueData.set("room_spawner", { - permission_overwrites: [], - supervisor_roles: [], + permission_overwrites: new mongoose.Types.Array(), + supervisor_roles: new mongoose.Types.Array(), parent: category.id, max_users: 5, lock_initially: true, - } as VoiceChannelSpawner); + } as FilterOutFunctionKeys); } await guildData.save(); await guildData.postSlashCommands(client, g); diff --git a/src/commands/config/queue/set_closing_shift.ts b/src/commands/config/queue/set_closing_shift.ts index 18025d0..a16968d 100644 --- a/src/commands/config/queue/set_closing_shift.ts +++ b/src/commands/config/queue/set_closing_shift.ts @@ -2,7 +2,7 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; const command: Command = { @@ -36,7 +36,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const closeShift = interaction.options.getNumber("shift", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); diff --git a/src/commands/config/queue/set_opening_shift.ts b/src/commands/config/queue/set_opening_shift.ts index b102c8d..ea18367 100644 --- a/src/commands/config/queue/set_opening_shift.ts +++ b/src/commands/config/queue/set_opening_shift.ts @@ -2,7 +2,7 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; const command: Command = { @@ -36,7 +36,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const openShift = interaction.options.getNumber("shift", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); diff --git a/src/commands/config/queue/set_text_channel.ts b/src/commands/config/queue/set_text_channel.ts index ab18cd6..24a7548 100644 --- a/src/commands/config/queue/set_text_channel.ts +++ b/src/commands/config/queue/set_text_channel.ts @@ -3,7 +3,7 @@ import { VoiceChannelSpawner } from "../../../models/voice_channel_spawner"; import { SlashCommandPermission } from "../../../models/slash_command_permission"; import { ApplicationCommandOptionType, Message, Role } from "discord.js"; import { Command } from "../../../../typings"; -import GuildSchema from "../../../models/guilds"; +import {GuildModel} from "../../../models/guilds"; import { QueueSpan } from "../../../models/queue_span"; const command: Command = { @@ -37,7 +37,7 @@ const command: Command = { await interaction.deferReply(); const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const queueChannel = interaction.options.getChannel("channel", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); diff --git a/src/commands/create-queue.ts b/src/commands/create-queue.ts index d98d1f4..dd00d38 100644 --- a/src/commands/create-queue.ts +++ b/src/commands/create-queue.ts @@ -1,7 +1,9 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import { Command } from "../../typings"; -import GuildSchema 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"; const command: Command = { name: "create-queue", @@ -43,8 +45,8 @@ const command: Command = { const g = interaction!.guild!; - const guildData = (await GuildSchema.findById(g.id))!; - const queue: Queue = { + const guildData = (await GuildModel.findById(g.id))!; + const queue: FilterOutFunctionKeys = { name: interaction.options.getString("name", true), description: interaction.options.getString("description", true), disconnect_timeout: 60000, @@ -54,8 +56,8 @@ const command: Command = { match_found_message: "You have found a Match with ${match}. Please Join ${match_channel} if you are not moved automatically. If you don't join in ${timeout} seconds, your position in the queue is dropped.", timeout_message: "Your queue Timed out after ${timeout} seconds.", leave_message: "You Left the `${name}` queue.\nTotal Time Spent: ${time_spent}", - entries: [], - opening_times: [], + entries: new mongoose.Types.DocumentArray([]), + opening_times: new mongoose.Types.DocumentArray([]), }; guildData.queues.push(queue); await guildData.save(); diff --git a/src/commands/help.ts b/src/commands/help.ts index ec9ab68..5cf9ae1 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 GuildSchema from "../models/guilds"; +import {GuildModel} from "../models/guilds"; const command: Command = { @@ -41,7 +41,7 @@ const command: Command = { embed.setThumbnail(client.user.displayAvatarURL()); } - const guildData = await GuildSchema.findById(interaction?.guildId ?? 0); + const guildData = await GuildModel.findById(interaction?.guildId ?? 0); const guildSettings = guildData?.guild_settings; /** diff --git a/src/commands/ping.ts b/src/commands/ping.ts index 77f8ef0..310b6a5 100644 --- a/src/commands/ping.ts +++ b/src/commands/ping.ts @@ -18,6 +18,7 @@ const command: Command = { // let choices = ["Is this really my ping, it's so high...", "Is it okay? I cant look", "I hope it isnt bad"] // let response = choices[Math.floor(Math.random() * choices.length)]; const res = await interaction.reply({ content: "Pinging..." }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any let m: any; if (interaction instanceof ChatInputCommandInteraction) { m = await interaction.fetchReply(); diff --git a/src/commands/queue/info.ts b/src/commands/queue/info.ts index 19feb76..68168a2 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 GuildSchema from "../../models/guilds"; +import {GuildModel} from "../../models/guilds"; const command: Command = { name: "info", @@ -19,7 +19,7 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/queue/join.ts b/src/commands/queue/join.ts index 7a0ceb7..27c7aab 100644 --- a/src/commands/queue/join.ts +++ b/src/commands/queue/join.ts @@ -1,8 +1,8 @@ import { ApplicationCommandOptionType, Message } from "discord.js"; import path from "path"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import UserSchema from "../../models/users"; +import {GuildModel} from "../../models/guilds"; +import {UserModel} from "../../models/users"; const command: Command = { name: "join", @@ -34,7 +34,7 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } @@ -55,7 +55,7 @@ const command: Command = { } // Check if Tutor Session active - const userData = await UserSchema.findById(user.id); + const userData = await UserModel.findById(user.id); if (await userData?.hasActiveSessions()) { await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "You cannot join a queue with an active coaching session.", empheral: true }); return; diff --git a/src/commands/queue/leave.ts b/src/commands/queue/leave.ts index 787e034..ed78359 100644 --- a/src/commands/queue/leave.ts +++ b/src/commands/queue/leave.ts @@ -2,7 +2,7 @@ import { Message } from "discord.js"; import moment from "moment"; import path from "path"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; +import {GuildModel} from "../../models/guilds"; const command: Command = { name: "leave", @@ -20,7 +20,7 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } diff --git a/src/commands/queue/list.ts b/src/commands/queue/list.ts index d821cc0..e374893 100644 --- a/src/commands/queue/list.ts +++ b/src/commands/queue/list.ts @@ -1,7 +1,7 @@ -import { QueueDocument } from "./../../models/queues"; +import { Queue } from "./../../models/queues"; import { Message } from "discord.js"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; +import {GuildModel} from "../../models/guilds"; const command: Command = { name: "list", @@ -20,12 +20,12 @@ const command: Command = { } const g = interaction.guild!; - const guildData = (await GuildSchema.findById(g.id)); + const guildData = (await GuildModel.findById(g.id)); if (!guildData) { return await client.utils.embeds.SimpleEmbed(interaction, { title: "Coaching System", text: "Guild Data Could not be found.", empheral: true }); } - return await client.utils.embeds.SimpleEmbed(interaction, { title: "Queue List", fields: guildData.queues.toObject().map(x => { return { name: x.name + (x.locked ? " (locked)" : ""), value: x.description ?? "", inline: false }; }) }); + return await client.utils.embeds.SimpleEmbed(interaction, { title: "Queue List", fields: guildData.queues.map(x => { return { name: x.name + (x.locked ? " (locked)" : ""), value: x.description ?? "", inline: false }; }) }); // client.utils.embeds.SimpleEmbed(interaction, "TODO", `Command \`${path.relative(process.cwd(), __filename)}\` is not Implemented Yet.`) }, diff --git a/src/commands/setjointocreate.ts b/src/commands/setjointocreate.ts index 4beae5c..b5b7885 100644 --- a/src/commands/setjointocreate.ts +++ b/src/commands/setjointocreate.ts @@ -1,7 +1,9 @@ import { ApplicationCommandOptionType, CategoryChannel, GuildChannel, Message, VoiceChannel as dvc } from "discord.js"; import { Command } from "../../typings"; -import GuildSchema 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"; const command: Command = { name: "setjointocreate", @@ -63,7 +65,7 @@ const command: Command = { parent_id = parent.id; } - const updated = await GuildSchema.updateOne( + const updated = await GuildModel.updateOne( { _id: g.id }, { $push: { @@ -76,16 +78,16 @@ const command: Command = { managed: true, // blacklist_user_groups: [], // whitelist_user_groups: [], - permitted: [], + permitted: new mongoose.Types.Array(), afkhell: false, spawner: { owner: member.id, - supervisor_roles: [], + supervisor_roles: new mongoose.Types.Array(), permission_overwrites: [{ id: interaction!.guild!.members.me!.id, allow: ["ViewChannel", "Connect", "Speak", "MoveMembers", "ManageChannels"] }], max_users: 5, parent: parent_id, }, - } as VoiceChannel, + } as FilterOutFunctionKeys, }, }, { upsert: true, setDefaultsOnInsert: true }, diff --git a/src/commands/setqueue.ts b/src/commands/setqueue.ts index f56126b..2226e05 100644 --- a/src/commands/setqueue.ts +++ b/src/commands/setqueue.ts @@ -1,7 +1,10 @@ +import { VoiceChannelModel } from "./../models/voice_channels"; import { ApplicationCommandOptionType, GuildChannel, Message } from "discord.js"; import { Command } from "../../typings"; -import GuildSchema 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"; const command: Command = { name: "setqueue", @@ -51,7 +54,7 @@ const command: Command = { if (!(channel instanceof GuildChannel)) { return await client.utils.embeds.SimpleEmbed(interaction, "Voice Channel System", ":x: Channel is invalid."); } - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const queueName = interaction.options.getString("queue", true); const queueData = guildData.queues.find(x => x.name.toLowerCase() === queueName.toLowerCase()); if (!queueData) { @@ -62,15 +65,15 @@ const command: Command = { } const supervisor_role = interaction.options.getRole("supervisor", true); - const waitingroom_channel: VoiceChannel & { _id: string } = { + const waitingroom_channel = new VoiceChannelModel({ _id: channel.id, channel_type: 2, locked: false, managed: true, - permitted: [], + permitted: new mongoose.Types.Array(), queue: queueData._id, supervisors: [supervisor_role.id], - }; + } as FilterOutFunctionKeys); guildData.voice_channels.push(waitingroom_channel); await guildData.save(); await client.utils.embeds.SimpleEmbed(interaction, "Queue System", `:white_check_mark: The Queue was Linked to the Channel ${channel}.`); diff --git a/src/commands/verify.ts b/src/commands/verify.ts index 234229e..34146be 100644 --- a/src/commands/verify.ts +++ b/src/commands/verify.ts @@ -1,4 +1,4 @@ -import UserSchema, { User } from "./../models/users"; +import { UserModel, User } from "./../models/users"; import { ApplicationCommandOptionType, GuildMember, Interaction, Message } from "discord.js"; import { Command, RunCommand } from "../../typings"; import * as crypto from "crypto"; diff --git a/src/commands/voice/close.ts b/src/commands/voice/close.ts index ec58fcb..85e5670 100644 --- a/src/commands/voice/close.ts +++ b/src/commands/voice/close.ts @@ -1,7 +1,7 @@ import { Message } from "discord.js"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "close", @@ -36,8 +36,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = guildData!.voice_channels.find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); @@ -67,7 +67,7 @@ const command: Command = { // try { // await channel.delete(); // // remove DB entry - // const updated = await GuildSchema.updateOne( + // const updated = await GuildModel.updateOne( // { _id: g.id }, // { // $pull: { diff --git a/src/commands/voice/kick.ts b/src/commands/voice/kick.ts index 080129c..a02e91a 100644 --- a/src/commands/voice/kick.ts +++ b/src/commands/voice/kick.ts @@ -1,7 +1,7 @@ import { ApplicationCommandOptionType, GuildMember, Message } from "discord.js"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "kick", @@ -42,8 +42,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = guildData!.voice_channels.find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/commands/voice/lock.ts b/src/commands/voice/lock.ts index 4494f8a..d980653 100644 --- a/src/commands/voice/lock.ts +++ b/src/commands/voice/lock.ts @@ -1,6 +1,6 @@ import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "lock", @@ -27,8 +27,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = (guildData!.voice_channels).find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/commands/voice/permit.ts b/src/commands/voice/permit.ts index a4767d5..c139d8f 100644 --- a/src/commands/voice/permit.ts +++ b/src/commands/voice/permit.ts @@ -1,7 +1,7 @@ import { ApplicationCommandOptionType, GuildMember, Message } from "discord.js"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "permit", @@ -42,8 +42,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = guildData!.voice_channels.find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/commands/voice/togglelock.ts b/src/commands/voice/togglelock.ts index 2f3bb36..dc6ff1e 100644 --- a/src/commands/voice/togglelock.ts +++ b/src/commands/voice/togglelock.ts @@ -1,7 +1,7 @@ import { OverwriteData } from "discord.js"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "togglelock", @@ -28,8 +28,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = guildData!.voice_channels.find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/commands/voice/togglevisibility.ts b/src/commands/voice/togglevisibility.ts index 2aba5dc..56de897 100644 --- a/src/commands/voice/togglevisibility.ts +++ b/src/commands/voice/togglevisibility.ts @@ -1,6 +1,6 @@ import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "togglevisibility", @@ -22,8 +22,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = guildData!.voice_channels.find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/commands/voice/transfer.ts b/src/commands/voice/transfer.ts index 0b128fc..f58433c 100644 --- a/src/commands/voice/transfer.ts +++ b/src/commands/voice/transfer.ts @@ -1,7 +1,7 @@ import { ApplicationCommandOptionType, GuildMember, Message } from "discord.js"; import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "transfer", @@ -42,8 +42,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = (guildData!.voice_channels).find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/commands/voice/unlock.ts b/src/commands/voice/unlock.ts index 1e09be8..5df5ec1 100644 --- a/src/commands/voice/unlock.ts +++ b/src/commands/voice/unlock.ts @@ -1,6 +1,6 @@ import { Command } from "../../../typings"; -import GuildSchema from "../../models/guilds"; -import { VoiceChannelDocument } from "../../models/voice_channels"; +import {GuildModel} from "../../models/guilds"; +import { VoiceChannel } from "../../models/voice_channels"; const command: Command = { name: "unlock", @@ -27,8 +27,8 @@ const command: Command = { } // Get Channel from DB - const guildData = (await GuildSchema.findById(g.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == channel!.id); + const guildData = (await GuildModel.findById(g.id)); + const channelData = guildData!.voice_channels.find(x => x._id == channel!.id); if (!channelData?.temporary) { return await client.utils.embeds.SimpleEmbed(interaction!, "Temporary Voice Channel System", "The Voice Channel you are in is not a Temporary Voice Channel."); diff --git a/src/componentInteractions/buttons/queue/leave.ts b/src/componentInteractions/buttons/queue/leave.ts index bcb154c..cda1bbc 100644 --- a/src/componentInteractions/buttons/queue/leave.ts +++ b/src/componentInteractions/buttons/queue/leave.ts @@ -1,16 +1,17 @@ import { Collection, EmbedBuilder } from "discord.js"; import { ButtonInteraction } from "../../../../typings"; -import GuildSchema, { GuildDocument } from "../../../models/guilds"; -import { QueueDocument } from "../../../models/queues"; +import { GuildModel, Guild } from "../../../models/guilds"; +import { Queue } from "../../../models/queues"; +import { DocumentType } from "@typegoose/typegoose"; const command: ButtonInteraction = { customID: "queue_leave", description: "leave the current queue", cooldown: 2000, execute: async (client, interaction) => { - const guilds = await GuildSchema.find(); - let g: GuildDocument | undefined; - let queue: QueueDocument | undefined; + const guilds = await GuildModel.find(); + let g: DocumentType | undefined; + let queue: DocumentType | undefined; for (g of guilds) { if (!g.queues) { continue; @@ -42,7 +43,7 @@ const command: ButtonInteraction = { color = guild?.members.me?.roles.highest.color ?? 0x7289da; const member = guild?.members.cache.get(interaction.user.id); const vcData = await g.voice_channels.id(member?.voice.channelId); - if (vcData?.queue?.equals(queue._id!)) { + if (vcData?.queue?._id.equals(queue._id!)) { await member!.voice.disconnect(); } const roles = await guild?.roles.fetch(); diff --git a/src/componentInteractions/buttons/queue/refresh.ts b/src/componentInteractions/buttons/queue/refresh.ts index 52b0ff5..3d5f742 100644 --- a/src/componentInteractions/buttons/queue/refresh.ts +++ b/src/componentInteractions/buttons/queue/refresh.ts @@ -1,17 +1,18 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js"; import moment from "moment"; import { ButtonInteraction } from "../../../../typings"; -import GuildSchema, { Guild, GuildDocument } from "../../../models/guilds"; -import { Queue, QueueDocument } from "../../../models/queues"; +import { GuildModel, Guild } from "../../../models/guilds"; +import { Queue } from "../../../models/queues"; +import { DocumentType } from "@typegoose/typegoose"; const command: ButtonInteraction = { customID: "queue_refresh", description: "refresh the current queue info", // cooldown: 30000, execute: async (client, interaction) => { - const guilds = await GuildSchema.find(); - let g: GuildDocument | undefined; - let queue: QueueDocument | undefined; + const guilds = await GuildModel.find(); + let g: DocumentType | undefined; + let queue: DocumentType | undefined; for (g of guilds) { if (!g.queues) { continue; @@ -69,7 +70,7 @@ const command: ButtonInteraction = { { embeds: [ - new EmbedBuilder({ title: "Queue System", description: `--${queue.name}--\nPosition:${(queue as QueueDocument).getPosition(entry.discord_id) + 1}/${queue.entries.length}\nTime Spent: ${moment.duration(Date.now() - (+entry.joinedAt)).format("d[d ]h[h ]m[m ]s.S[s]")}`, color: client.guilds.cache.get((g as Guild & { _id: string })._id)?.members.me?.roles.highest.color || 0x7289da }), + new EmbedBuilder({ title: "Queue System", description: `--${queue.name}--\nPosition:${queue.getPosition(entry.discord_id) + 1}/${queue.entries.length}\nTime Spent: ${moment.duration(Date.now() - (+entry.joinedAt)).format("d[d ]h[h ]m[m ]s.S[s]")}`, color: client.guilds.cache.get((g as Guild & { _id: string })._id)?.members.me?.roles.highest.color || 0x7289da }), ], components: [], }); diff --git a/src/componentInteractions/buttons/queue/stay.ts b/src/componentInteractions/buttons/queue/stay.ts index 2c6a0cd..12e1cb5 100644 --- a/src/componentInteractions/buttons/queue/stay.ts +++ b/src/componentInteractions/buttons/queue/stay.ts @@ -1,16 +1,17 @@ import { ActionRowBuilder, ButtonBuilder, ButtonStyle, Collection, EmbedBuilder } from "discord.js"; import { ButtonInteraction } from "../../../../typings"; -import GuildSchema, { GuildDocument } from "../../../models/guilds"; -import { QueueDocument } from "../../../models/queues"; +import {GuildModel, Guild } from "../../../models/guilds"; +import { Queue } from "../../../models/queues"; +import { DocumentType } from "@typegoose/typegoose"; const command: ButtonInteraction = { customID: "queue_stay", description: "stays in the current queue", cooldown: 2000, execute: async (client, interaction) => { - const guilds = await GuildSchema.find(); - let g: GuildDocument | undefined; - let queue: QueueDocument | undefined; + const guilds = await GuildModel.find(); + let g: DocumentType | undefined; + let queue: DocumentType | undefined; for (g of guilds) { if (!g.queues) { continue; diff --git a/src/crypto_test.ts b/src/crypto_test.ts index f3dbe11..e8f9275 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, ConfigHandler.getInstance().get("verify_secret")).toString(); + return cryptojs.AES.encrypt(text, "mbvWKnjLFh4nHbj7Vx79rvp9kcJF9g4C").toString(); } /** @@ -65,7 +65,7 @@ type Student = { // {"moodleId":"1","token":"FOP-DiscordV1|guest|1#5630c38fb1ae39b1048e0cf802f4170dba8943f5e13510055357b3cb67a3bac1"} const newResult = result.map((student) => { const token = encryptTokenString( - "968818197824417822", + "1078710248086446120", "01", student.id_tu, student.id_moodle, diff --git a/src/events/GuildCreateEvent.ts b/src/events/GuildCreateEvent.ts index e240c12..759c989 100644 --- a/src/events/GuildCreateEvent.ts +++ b/src/events/GuildCreateEvent.ts @@ -1,10 +1,10 @@ import { ClientEventListener, ExecuteEvent } from "../../typings"; import { ApplicationCommandData, ApplicationCommandOptionChoiceData, Client, ClientEvents, Guild } from "discord.js"; -import GuildSchema from "../models/guilds"; +import {GuildModel} from "../models/guilds"; import { inspect } from "util"; export const name = "guildCreate"; export const execute: ExecuteEvent<"guildCreate"> = async (client, guild) => { - await GuildSchema.prepareGuild(client, guild); + await GuildModel.prepareGuild(client, guild); }; diff --git a/src/events/InteractionCreateEvent.ts b/src/events/InteractionCreateEvent.ts index 0624d64..b434d74 100644 --- a/src/events/InteractionCreateEvent.ts +++ b/src/events/InteractionCreateEvent.ts @@ -1,7 +1,7 @@ import { Command, ExecuteEvent } from "../../typings"; import { ChatInputCommandInteraction, Collection, CommandInteractionOption } from "discord.js"; export const name = "interactionCreate"; -import GuildSchema from "../models/guilds"; +import {GuildModel} from "../models/guilds"; export const execute: ExecuteEvent<"interactionCreate"> = async (client, interaction) => { @@ -16,7 +16,7 @@ export const execute: ExecuteEvent<"interactionCreate"> = async (client, interac let actualCommandName = commandName; if (interaction.guild) { const g = interaction.guild; - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; actualCommandName = guildData.guild_settings.getCommandByGuildName(commandName)?.internal_name ?? commandName; } const command = client.commands.get(actualCommandName) || client.commands.find(cmd => (cmd.aliases != undefined) && cmd.aliases.includes(actualCommandName)); diff --git a/src/events/MessageCreateEvent.ts b/src/events/MessageCreateEvent.ts index f71d95c..d17e746 100644 --- a/src/events/MessageCreateEvent.ts +++ b/src/events/MessageCreateEvent.ts @@ -2,7 +2,7 @@ import { ExecuteEvent } from "../../typings"; import { Collection, Guild, GuildMember, Message } from "discord.js"; export const name = "messageCreate"; import * as crypto from "crypto"; -import UserSchema from "../models/users"; +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 a49eacc..cc343ee 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 GuildSchema 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 GuildSchema.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 7c9e940..a4cf509 100644 --- a/src/events/VoiceStateUpdateEvent.ts +++ b/src/events/VoiceStateUpdateEvent.ts @@ -1,14 +1,13 @@ import { ExecuteEvent } from "../../typings"; import { Collection, ActionRow, Embed, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, APIMessageActionRowComponent } from "discord.js"; -import GuildSchema from "../models/guilds"; -import { VoiceChannelDocument } from "../models/voice_channels"; -import { QueueDocument } from "../models/queues"; +import {GuildModel} from "../models/guilds"; +import { VoiceChannel } from "../models/voice_channels"; +import { Queue } from "../models/queues"; import { QueueEntry } from "../models/queue_entry"; -import moment from "moment"; export const name = "voiceStateUpdate"; -import RoomSchema from "../models/rooms"; -import UserSchema from "../models/users"; -import EventSchema, { Event as EVT, eventType } from "../models/events"; +import {RoomModel} from "../models/rooms"; +import {UserModel} from "../models/users"; +import {EventModel, Event as EVT, eventType } from "../models/events"; export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState, newState) => { const oldUserChannel = oldState.channel; @@ -20,9 +19,9 @@ export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState const guild = newState.channel.guild; // Get Channel from DB - const guildData = (await GuildSchema.findById(guild.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == newState.channelId!); - const roomData = (await RoomSchema.findById(newState.channelId)); + const guildData = (await GuildModel.findById(guild.id)); + const channelData = (guildData!.voice_channels).find(x => x._id == newState.channelId!); + const roomData = (await RoomModel.findById(newState.channelId)); if (channelData) { // Check if Channel is Spawner @@ -42,16 +41,16 @@ export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState console.log(`Created TEMP VC: ${createdVC.name} on ${guild.name}`); } else if (channelData.queue) { - const queueId = channelData.queue; - const queue = (guildData?.queues as QueueDocument[]).find(x => x._id?.equals(queueId)); + const queueRef = channelData.queue; + const queue = guildData?.queues.find(x => x._id?.equals(queueRef)); if (!queue) { - client.logger.error(`Referenced Queue was not found in Database: ${queueId.toHexString()}`); + client.logger.error(`Referenced Queue was not found in Database: ${queueRef._id.toHexString()}`); return; } let queueEntry: QueueEntry; try { if (queue.contains(newState.member!.id)) { - if (client.queue_stays.get(newState.member!.id)?.get(queueId.toHexString()) === client.utils.general.QueueStayOptions.PENDING) { + if (client.queue_stays.get(newState.member!.id)?.get(queueRef._id.toHexString()) === client.utils.general.QueueStayOptions.PENDING) { client.queue_stays.get(newState.member!.id)!.set(queue._id!.toHexString(), client.utils.general.QueueStayOptions.STAY); await client.utils.embeds.SimpleEmbed(await newState.member!.createDM(), { title: "Queue System", text: "You stayed in the queue.", components: [ @@ -65,7 +64,7 @@ export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState } return; } - // let userData = await UserSchema.findById(newState.member!.id); + // let userData = await UserModel.findById(newState.member!.id); if (newState.member?.roles.cache.find(x => x.name.toLowerCase() === "tutor" || x.name.toLowerCase() === "orga")) { return; } @@ -147,9 +146,9 @@ export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState const guild = oldUserChannel.guild; // Get Channel from DB - const guildData = (await GuildSchema.findById(guild.id)); - const channelData = (guildData!.voice_channels as VoiceChannelDocument[]).find(x => x._id == oldState.channelId!); - const roomData = (await RoomSchema.findById(oldState.channelId)); + const guildData = (await GuildModel.findById(guild.id)); + const channelData = guildData!.voice_channels.find(x => x._id == oldState.channelId!); + const roomData = (await RoomModel.findById(oldState.channelId)); if (channelData) { if (channelData.temporary && oldUserChannel.members.size == 0) { @@ -173,7 +172,7 @@ export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState // } // remove DB entry - const updated = await GuildSchema.updateOne( + const updated = await GuildModel.updateOne( { _id: guild.id }, { $pull: { @@ -185,10 +184,10 @@ export const execute: ExecuteEvent<"voiceStateUpdate"> = async (client, oldState // console.log(updated); console.log(`deleted TEMP VC: ${cName} on ${guild.name}`); } else if (channelData.queue) { - const queueId = channelData.queue; - const queue: QueueDocument | null | undefined = guildData?.queues.id(queueId); + const queueRef = channelData.queue; + const queue = guildData?.queues.id(queueRef); if (!queue) { - client.logger.error(`Referenced Queue was not found in Database: ${queueId.toHexString()}`); + client.logger.error(`Referenced Queue was not found in Database: ${queueRef._id.toHexString()}`); return; } const member = oldState.member!; diff --git a/src/events/guildMemberAddEvent.ts b/src/events/guildMemberAddEvent.ts index 28daf4a..5894b2d 100644 --- a/src/events/guildMemberAddEvent.ts +++ b/src/events/guildMemberAddEvent.ts @@ -1,22 +1,22 @@ import { ClientEventListener, ExecuteEvent, StringReplacements } from "../../typings"; import { ApplicationCommandData, ApplicationCommandOptionChoiceData, Client, ClientEvents, Guild } from "discord.js"; -import GuildSchema from "../models/guilds"; -import UserSchema from "../models/users"; +import {GuildModel} from "../models/guilds"; +import {UserModel} from "../models/users"; import { inspect } from "util"; export const name = "guildMemberAdd"; export const execute: ExecuteEvent<"guildMemberAdd"> = async (client, member) => { const guild = member.guild; - const guildData = await GuildSchema.findById(guild.id); + const guildData = await GuildModel.findById(guild.id); // Create User Entry - let databaseUser = await UserSchema.findById(member.id); + let databaseUser = await UserModel.findById(member.id); if (!databaseUser) { - databaseUser = new UserSchema({ _id: member.id }); + databaseUser = new UserModel({ _id: member.id }); await databaseUser.save(); } // Give Roles - const guildRoles = databaseUser.server_roles.toObject().flatMap(x => { + const guildRoles = databaseUser.server_roles.flatMap(x => { const role = member.guild.roles.resolve(x); if (role) { return [role]; diff --git a/src/events/guildMemberRemoveEvent.ts b/src/events/guildMemberRemoveEvent.ts index b719a89..24a1cb7 100644 --- a/src/events/guildMemberRemoveEvent.ts +++ b/src/events/guildMemberRemoveEvent.ts @@ -1,16 +1,13 @@ -import { ClientEventListener, ExecuteEvent } from "../../typings"; -import { ApplicationCommandData, ApplicationCommandOptionChoiceData, Client, ClientEvents, Guild } from "discord.js"; -import GuildSchema from "../models/guilds"; -import UserSchema from "../models/users"; -import { inspect } from "util"; +import { ExecuteEvent } from "../../typings"; +import {UserModel} from "../models/users"; export const name = "guildMemberRemove"; export const execute: ExecuteEvent<"guildMemberRemove"> = async (client, member) => { // Backup Server Roles - let databaseUser = await UserSchema.findById(member.id); + let databaseUser = await UserModel.findById(member.id); if (!databaseUser) { - databaseUser = new UserSchema({ _id: member.id }); + databaseUser = new UserModel({ _id: member.id }); await databaseUser.save(); } databaseUser.server_roles.push(...member.roles.cache.keys()); diff --git a/src/models/bot_roles.ts b/src/models/bot_roles.ts index 6c7d42e..d611639 100644 --- a/src/models/bot_roles.ts +++ b/src/models/bot_roles.ts @@ -1,5 +1,4 @@ -import mongoose from "mongoose"; -import GuildSchema, {GuildModel} from "./guilds"; +import { getModelForClass, prop } from "@typegoose/typegoose"; export enum InternalRoles { SERVER_OWNER = "server_owner", @@ -15,49 +14,40 @@ export enum RoleScopes { SERVER = "server", } -export interface DBRole { +export class DBRole { /** * The internal Name of the Bot Role */ - internal_name: InternalRoles, + @prop({ required: true, enum: InternalRoles }) + internal_name!: InternalRoles; /** * The Scope of the Role */ - scope: RoleScopes, + @prop({ required: true, enum: RoleScopes }) + scope!: RoleScopes; /** * The ID of the server the role is in */ - server_id?: string, + @prop({ required: false, unique: true, sparse: true }) + server_id?: string; /** * The Discord Role ID */ - role_id?: string, + @prop({ required: false, unique: true, sparse: true }) + role_id?: string; /** * The Name of the role on the server (used for recovering the role if it gets deleted) */ - server_role_name?: string, + @prop({ required: false }) + server_role_name?: string; } /** * Roles that are valid for only one server */ export interface ServerRole extends DBRole { - /** - * The Scope of the Role - */ scope: RoleScopes.SERVER, - /** - * The ID of the server the role is in - */ server_id: string, - /** - * The Discord Role ID - */ - role_id?: string, - /** - * The Name of the role on the server (used for recovering the role if it gets deleted) - */ - server_role_name?: string, } /** @@ -70,42 +60,8 @@ export interface BotRole extends DBRole { scope: RoleScopes.GLOBAL, } -const DBRoleSchema = new mongoose.Schema({ - internal_name: { - type: String, - required: true, - enum: Object.values(InternalRoles), - }, - scope: { - type: String, - required: true, - enum: [RoleScopes.GLOBAL, RoleScopes.SERVER], - }, - server_id: { - type: String, - required: true, - unique: true, - sparse: true, +export const DBRoleModel = getModelForClass(DBRole, { + schemaOptions: { + autoCreate: false, }, - role_id: { - type: String, - required: false, - unique: true, - sparse: true, - }, - server_role_name: { - type: String, - required: false, - }, -}); - -export interface DBRoleDocument extends DBRole, mongoose.Document { -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DBRoleModel extends mongoose.Model { - -} - -// Default export -export default DBRoleSchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/events.ts b/src/models/events.ts index 186df31..600d211 100644 --- a/src/models/events.ts +++ b/src/models/events.ts @@ -1,4 +1,4 @@ -import mongoose from "mongoose"; +import { getModelForClass, prop } from "@typegoose/typegoose"; export enum eventType { "create_channel" = "create_channel", @@ -19,65 +19,36 @@ export enum eventType { /** * A Guild from the Database */ -export interface Event { +export class Event { /** * The Unix Time Stamp of the Event */ - timestamp: string, + @prop({required: true}) + timestamp!: string; /** * The Event Type */ - type: eventType, + @prop({required: true, enum: eventType, default: eventType.other}) + type!: eventType; /** * Client ID or "me" */ - emitted_by: string, + @prop({required: true}) + emitted_by!: string; /** * A Target that was affected */ - target?: string, + @prop({required: false}) + target?: string; /** * The Reason why the Event was Emitted */ - reason?: string, + @prop({required: false}) + reason?: string; } -/** - * A Schema For storing and Managing Guilds - */ -const EventSchema = new mongoose.Schema({ - timestamp: { - type: String, - required: true, - }, - type: { - type: String, - enum: [...Object.keys(eventType)], - default: eventType.other, - required: true, - }, - emitted_by: { - type: String, - required: true, - }, - target: { - type: String, - required: false, - }, - reason: { - type: String, - required: false, +export const EventModel = getModelForClass(Event, { + schemaOptions: { + autoCreate: false, }, -}); - -export interface EventDocument extends Event, mongoose.Document { - // List getters or non model methods here -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface EventModel extends mongoose.Model { - // List Model methods here -} - -// Default export -export default EventSchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/guild_settings.ts b/src/models/guild_settings.ts index d2991ea..7ad40cf 100644 --- a/src/models/guild_settings.ts +++ b/src/models/guild_settings.ts @@ -1,6 +1,6 @@ -import DBRoleSchema, { DBRole, DBRoleDocument } from "./bot_roles"; -import SlashCommandSettingsSchema, { SlashCommandSettings, SlashCommandSettingsDocument } from "./slash_command_settings"; -import mongoose from "mongoose"; +import { DBRole } from "./bot_roles"; +import { SlashCommandSettings } from "./slash_command_settings"; +import { ArraySubDocumentType, DocumentType, getModelForClass, mongoose, prop } from "@typegoose/typegoose"; /** * Command Listen Modes @@ -10,105 +10,78 @@ export enum CommandListenMode { BLACKLIST = 1 } -export interface GuildSettings { +export class GuildSettings { /** * @deprecated * The Bot Prefix for the Guild */ - prefix: string, + @prop({ required: true, default: "!" }) + prefix!: string; /** * @deprecated * The Command Listen Mode for The Guild */ - command_listen_mode: CommandListenMode, + @prop({ required: true, enum: CommandListenMode, default: CommandListenMode.BLACKLIST }) + command_listen_mode!: CommandListenMode; /** * The Guild Specific command Settings */ - slashCommands: SlashCommandSettings[], + @prop({ required: true, default: [], type: () => [SlashCommandSettings] }) + slashCommands!: mongoose.Types.DocumentArray>; /** * The Guild Specific role Settings */ - roles?: DBRole[], + @prop({ default: [], type: () => [DBRole] }) + roles?: mongoose.Types.DocumentArray>; /** * The User Account URL related to the guild */ - account_url?: string, -} - -const GuildSettingsSchema = new mongoose.Schema({ - command_listen_mode: { - type: Number, - enum: [0, 1], - default: 1, - required: true, - }, - prefix: { - type: String, - required: true, - default: "!", - }, - slashCommands: [{ - type: SlashCommandSettingsSchema, - required: true, - default: [], - }], - roles: [{ - type: DBRoleSchema, - required: false, - default: [], - }], -}); + @prop() + account_url?: string; -export interface GuildSettingsDocument extends GuildSettings, mongoose.Document { - slashCommands: mongoose.Types.DocumentArray, - roles: mongoose.Types.DocumentArray, - /** - * Checks whether command Settings exist - * @param name The internal Command Name - */ - hasCommandSettings(name: string): boolean, - /** - * Gets the Settings for a specified command - * @param name The internal Command Name - */ - getCommandByInternalName(name: string): SlashCommandSettingsDocument | null, /** - * Gets the Settings for a specified command - * @param name The guild Command Name - */ - getCommandByGuildName(name: string): SlashCommandSettingsDocument | null, - getOrCreateCommandByInternalName(name: string): Promise, -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface GuildSettingsModel extends mongoose.Model { - -} + * Checks whether command Settings exist + * @param name The internal Command Name + */ + public hasCommandSettings(this: DocumentType, name: string): boolean { + return this.slashCommands.some(x => x.internal_name === name); + } -GuildSettingsSchema.method("hasCommandSettings", function (name: string) { - return this.slashCommands.some(x => x.internal_name === name); -}); + /** + * Gets the Settings for a specified command + * @param name The internal Command Name + */ + public getCommandByInternalName(this: DocumentType, name: string): DocumentType | null { + return this.slashCommands.find(x => x.internal_name === name) ?? null; + } -GuildSettingsSchema.method("getCommandByInternalName", function (name: string) { - return this.slashCommands.find(x => x.internal_name === name) ?? null; -}); -GuildSettingsSchema.method("getCommandByGuildName", function (name: string) { - return this.slashCommands.find(x => x.name === name) ?? this.getCommandByInternalName(name); -}); + /** + * Gets the Settings for a specified command + * @param name The guild Command Name + */ + public getCommandByGuildName(this: DocumentType, name: string): SlashCommandSettings | null { + return this.slashCommands.find(x => x.name === name) ?? this.getCommandByInternalName(name); + } -GuildSettingsSchema.method("getOrCreateCommandByInternalName", async function (name: string) { - if (!this.hasCommandSettings(name)) { - this.slashCommands.push( - { + /** + * Gets the Settings for a specified command + * @param name The guild Command Name + */ + public async getOrCreateCommandByInternalName(this: DocumentType, name: string): Promise> { + if (!this.hasCommandSettings(name)) { + this.slashCommands.push({ internal_name: name, aliases: [], permissions: [], - } as SlashCommandSettings, - ); - await this.$parent()?.save(); + }); + await this.$parent()?.save(); + } + return this.getCommandByInternalName(name)!; } - return this.getCommandByInternalName(name)!; -}); +} -// Default export -export default GuildSettingsSchema; \ No newline at end of file +export const GuildSettingsModel = getModelForClass(GuildSettings, { + schemaOptions: { + autoCreate: false, + }, +}); \ No newline at end of file diff --git a/src/models/guilds.ts b/src/models/guilds.ts index ed95d33..2994864 100644 --- a/src/models/guilds.ts +++ b/src/models/guilds.ts @@ -1,269 +1,202 @@ -// import mongoose from 'mongoose'; -import mongoose, { AnyKeys, AnyObject } from "mongoose"; import { Bot } from "../bot"; -import GuildSettingsSchema, { GuildSettings, GuildSettingsDocument } from "./guild_settings"; -import QueueSchema, { Queue, QueueDocument } from "./queues"; -import TextChannelSchema, { TextChannel, TextChannelDocument } from "./text_channels"; -import VoiceChannelSchema, { VoiceChannel, VoiceChannelDocument } from "./voice_channels"; +import { Queue } from "./queues"; +import { TextChannel } from "./text_channels"; +import { VoiceChannel } from "./voice_channels"; import * as djs from "discord.js"; import { ApplicationCommandData, ApplicationCommandOptionChoiceData } from "discord.js"; +import { prop, getModelForClass, DocumentType, ReturnModelType, SubDocumentType, ArraySubDocumentType, mongoose } from "@typegoose/typegoose"; import { Command, SubcommandHandler } from "../../typings"; +import { GuildSettings } from "./guild_settings"; /** * A Guild from the Database */ -export interface Guild { +export class Guild { /** * The Guild ID provided by Discord */ - _id: string, + @prop({ required: true }) + _id!: string; /** * The Name of the Guild */ - name: string, + @prop({ required: true }) + name!: string; /** * The Member Count (Makes it easier to sort Guilds by member counts) */ - member_count: number, + @prop({ required: true, default: 0 }) + member_count!: number; /** * The Settings for the Guild */ - guild_settings: GuildSettings, + @prop({ required: true }) + guild_settings!: SubDocumentType; /** * The Relevant Text Channels of the Guild */ - text_channels: TextChannel[], + @prop({ required: true, default: [], type: () => [TextChannel] }) + text_channels!: mongoose.Types.DocumentArray>; /** * The Relevant Voice Channels of the Guild */ - voice_channels: VoiceChannel[], + @prop({ required: true, default: [], type: () => [VoiceChannel] }) + voice_channels!: mongoose.Types.DocumentArray>; /** * The Queues of the Guild */ - queues: Queue[], + @prop({ required: true, default: [], type: () => [Queue] }) + queues!: mongoose.Types.DocumentArray>; /** * The Welcome Message Text */ - welcome_text?: string, + @prop() + welcome_text?: string; /** * The Welcome Message Title */ - welcome_title?: string, -} - -/** - * A Schema For storing and Managing Guilds - */ -const GuildSchema = new mongoose.Schema({ - _id: { - type: String, - required: true, - }, - name: { - type: String, - required: true, - }, - member_count: { - type: Number, - required: true, - default: 0, - }, - guild_settings: { - type: GuildSettingsSchema, - required: true, - }, - text_channels: [{ - type: TextChannelSchema, - required: true, - default: [], - }], - voice_channels: [{ - type: VoiceChannelSchema, - required: true, - default: [], - }], - queues: [{ - type: QueueSchema, - required: true, - default: [], - }], - welcome_text: { - type: String, - required: false, - }, - welcome_title: { - type: String, - required: false, - }, -}); - -/** - * A Guild Document as stored in the Database - */ -export interface GuildDocument extends Guild, Omit { - // List getters or non model methods here - text_channels: mongoose.Types.DocumentArray, - voice_channels: mongoose.Types.DocumentArray, - guild_settings: GuildSettingsDocument, - queues: mongoose.Types.DocumentArray, + @prop() + welcome_title?: string; /** * Gets the actual guild object represented by this document from discord * @param client The Bot Client */ - resolve(client: Bot): Promise, + public async resolve(this:DocumentType, client: Bot): Promise{ + return await client.guilds.resolve(this._id); + } /** * Posts the Slash Commands to the Guild (using set) * @param client The Bot Client */ - postSlashCommands(client: Bot): Promise, + public async postSlashCommands(this: DocumentType, client: Bot): Promise; /** * Posts the Slash Commands to the Guild (using set) * @param client The Bot Client * @param g The resolved guild (for speed improvement) */ - postSlashCommands(client: Bot, g: djs.Guild): Promise, + public async postSlashCommands(this: DocumentType, client: Bot, g: djs.Guild): Promise; /** * Posts the Slash Commands to the Guild (using set) * @param g The resolved guild (for speed improvement) */ - postSlashCommands(client: Bot, g?: djs.Guild | null): Promise, + public async postSlashCommands(this: DocumentType, client: Bot, g?: djs.Guild | null): Promise{ + console.log("posting slash commands"); + g = g ?? await this.resolve(client); + if (!g) { + throw new Error("Guild could not be resolved!"); + } + // TODO: Per Guild Slash Command Config + const data: ApplicationCommandData[] = []; + // console.log([...client.commands.values()]) + // console.log("posting slash commands"); + for (const c of [...client.commands.values()]) { + // console.log("a"+ c); + // Check Database entry + const cmdSettings = this.guild_settings.getCommandByInternalName(c.name); + if (cmdSettings?.disabled) { + continue; + } + const commandData: ApplicationCommandData = { + name: cmdSettings?.name ?? c.name, + description: cmdSettings?.description ?? c.description, + options: c.options, + //defaultPermission: cmdSettings?.defaultPermission ?? c.defaultPermission, + }; + // Push Options to Help Commands (we do that here because all Commands are loaded at this point) + if (c.name === "help") { + const cmdChoices: ApplicationCommandOptionChoiceData[] = client.commands.map((val, key) => { + return { name: key, value: key }; + }); + (commandData.options![0] as djs.ApplicationCommandChoicesData).choices = cmdChoices; + } + data.push(commandData); + // TODO: Aliases + } + try { + const commands = await g.commands.set(data); + // // permissions + // const fullPermissions: djs.GuildApplicationCommandPermissionData[] = []; + // for (const c of [...commands.values()]) { + // const cmdSettings = this.guild_settings.getCommandByGuildName(c.name); + // fullPermissions.push({ + // id: c.id, + // permissions: [ + // // Overwrites von Settings + // ...cmdSettings?.getPostablePermissions() ?? [], + // // Bot owner + // { + // id: client.config.get("ownerID")!, + // type: ApplicationCommandOptionType.User, + // permission: true, + // }, + // ], + // }); + // } + // await g.commands.permissions.set({ + // fullPermissions: fullPermissions, + // }); + + } catch (error) { + console.log(error); + } + } /** * Gets all Command Names to append to help Command * @param commands The Command Collection of the Guild */ - getRecursiveCommandNames(commands: djs.Collection): ApplicationCommandOptionChoiceData[], + public getRecursiveCommandNames(this: DocumentType, commands: djs.Collection): ApplicationCommandOptionChoiceData[]{ + const data: ApplicationCommandOptionChoiceData[] = []; + commands.forEach((val, key) => { + data.push({ name: key, value: key }); + if ((val as SubcommandHandler).subcommands) { + data.push(...this.getRecursiveCommandNames((val as SubcommandHandler).subcommands)); + } + }); + return data; + } /** * Gets the verivied Role * @param client The Bot Client * @param g The resolved guild (for speed improvement) */ - getVerifiedRole(client: Bot, g?: djs.Guild | null | undefined): Promise, -} + public async getVerifiedRole(this: DocumentType, client: Bot, g?: djs.Guild | null | undefined): Promise{ + g = g ?? (await this.resolve(client))!; + await g.roles.fetch(); + return g.roles.cache.find(x => x.name.toLowerCase() === "verified") ?? null; + } -/** - * A Guild Model - */ -export interface GuildModel extends mongoose.Model { - // List Model methods here /** * Processes A Guild by updating the database and posting Slash Commands * @param client The Bot Client * @param g the guild Object */ - prepareGuild(client: Bot, g: djs.Guild): Promise, -} - -// --Methods-- - -// TODO Find better Names so that they don't conflict with discordjs Interfaces - -GuildSchema.method("resolve", async function (client: Bot) { - return await client.guilds.resolve(this._id); -}); - -GuildSchema.method("getRecursiveCommandNames", function (commands: djs.Collection) { - const data: ApplicationCommandOptionChoiceData[] = []; - commands.forEach((val, key) => { - data.push({ name: key, value: key }); - if ((val as SubcommandHandler).subcommands) { - data.push(...this.getRecursiveCommandNames((val as SubcommandHandler).subcommands)); - } - }); - return data; -}); - -GuildSchema.method("getVerifiedRole", async function (client: Bot, g?: djs.Guild | null) { - g = g ?? (await this.resolve(client))!; - await g.roles.fetch(); - return g.roles.cache.find(x => x.name.toLowerCase() === "verified") ?? null; -}); - - -GuildSchema.method("postSlashCommands", async function (client: Bot, g?: djs.Guild | null) { - console.log("posting slash commands"); - g = g ?? await this.resolve(client); - if (!g) { - throw new Error("Guild could not be resolved!"); - } - // TODO: Per Guild Slash Command Config - const data: ApplicationCommandData[] = []; - // console.log([...client.commands.values()]) - // console.log("posting slash commands"); - for (const c of [...client.commands.values()]) { - // console.log("a"+ c); - // Check Database entry - const cmdSettings = this.guild_settings.getCommandByInternalName(c.name); - if (cmdSettings?.disabled) { - continue; - } - const commandData: ApplicationCommandData = { - name: cmdSettings?.name ?? c.name, - description: cmdSettings?.description ?? c.description, - options: c.options, - //defaultPermission: cmdSettings?.defaultPermission ?? c.defaultPermission, - }; - // Push Options to Help Commands (we do that here because all Commands are loaded at this point) - if (c.name === "help") { - const cmdChoices: ApplicationCommandOptionChoiceData[] = client.commands.map((val, key) => { - return { name: key, value: key }; + public static async prepareGuild(this: ReturnModelType, client: Bot, g: djs.Guild): Promise{ + console.log(`Processing guild "${g.name}" (${g.id})`); + let guildData: DocumentType | null = await this.findById(g.id); + if (!guildData) { + const newGuildData = new GuildModel({ + _id: g.id, + name: g.name, + member_count: g.memberCount, + guild_settings: { + command_listen_mode: 1, + prefix: "!", + slashCommands: [], + }, + text_channels: [], + voice_channels: [], + queues: [], }); - (commandData.options![0] as djs.ApplicationCommandChoicesData).choices = cmdChoices; + await newGuildData.save(); + guildData = newGuildData; } - data.push(commandData); - // TODO: Aliases - } - try { - const commands = await g.commands.set(data); - // // permissions - // const fullPermissions: djs.GuildApplicationCommandPermissionData[] = []; - // for (const c of [...commands.values()]) { - // const cmdSettings = this.guild_settings.getCommandByGuildName(c.name); - // fullPermissions.push({ - // id: c.id, - // permissions: [ - // // Overwrites von Settings - // ...cmdSettings?.getPostablePermissions() ?? [], - // // Bot owner - // { - // id: client.config.get("ownerID")!, - // type: ApplicationCommandOptionType.User, - // permission: true, - // }, - // ], - // }); - // } - // await g.commands.permissions.set({ - // fullPermissions: fullPermissions, - // }); - - } catch (error) { - console.log(error); + // Post slash Commands + await guildData.postSlashCommands(client, g); } -}); - -GuildSchema.static("prepareGuild", async function (client: Bot, g: djs.Guild) { - console.log(`Processing guild "${g.name}" (${g.id})`); - let guildData: GuildDocument | null = await this.findById(g.id); - if (!guildData) { - const newGuildData = new this & AnyObject>({ - _id: g.id, - name: g.name, - member_count: g.memberCount, - guild_settings: { - command_listen_mode: 1, - prefix: "!", - slashCommands: [], - } as GuildSettings, - text_channels: [], - voice_channels: [], - queues: [], - }); - await newGuildData.save(); - guildData = newGuildData as unknown as GuildDocument; - } - // Post slash Commands - await guildData.postSlashCommands(client, g); -}); +} -// Default export -export default mongoose.model("Guilds", GuildSchema); \ No newline at end of file +export const GuildModel = getModelForClass(Guild, { + schemaOptions: { + autoCreate: true, + }, +}); \ No newline at end of file diff --git a/src/models/permission_overwrite_data.ts b/src/models/permission_overwrite_data.ts index 44a89ae..f839ca0 100644 --- a/src/models/permission_overwrite_data.ts +++ b/src/models/permission_overwrite_data.ts @@ -1,43 +1,17 @@ import { OverwriteData, PermissionResolvable, Snowflake } from "discord.js"; -import mongoose from "mongoose"; +import { getModelForClass, prop } from "@typegoose/typegoose"; -export interface PermissionOverwriteData extends OverwriteData { - allow?: PermissionResolvable[], - deny?: PermissionResolvable[], - id: Snowflake, +export class PermissionOverwriteData implements OverwriteData { + @prop({ required: true, type: String }) + id!: Snowflake; + @prop({ required: true, type: String, default: [] }) + allow?: PermissionResolvable[]; + @prop({ required: true, type: String, default: [] }) + deny?: PermissionResolvable[]; } -const PermissionOverwriteDataSchema = new mongoose.Schema({ - id: { - type: String, - required: true, +export const PermissionOverwriteDataModel = getModelForClass(PermissionOverwriteData, { + schemaOptions: { + autoCreate: false, }, - // allow: { - // type: [String], - // enum: ["CREATE_INSTANT_INVITE", "KICK_MEMBERS", "BAN_MEMBERS", "ADMINISTRATOR", "ManageChannels", "MANAGE_GUILD", "ADD_REACTIONS", "VIEW_AUDIT_LOG", "PRIORITY_SpeakER", "Stream", "ViewChannel", "SEND_MESSAGES", "SEND_TTS_MESSAGES", "MANAGE_MESSAGES", "EMBED_LINKS", "ATTACH_FILES", "READ_MESSAGE_HISTORY", "MENTION_EVERYONE", "USE_EXTERNAL_EMOJIS", "VIEW_GUILD_INSIGHTS", "Connect", "Speak", "MuteMembers", "DeafenMembers", "MoveMembers", "USE_VAD", "CHANGE_NICKNAME", "MANAGE_NICKNAMES", "MANAGE_ROLES", "MANAGE_WEBHOOKS", "MANAGE_EMOJIS"], - // required: false, - // }, - // deny: { - // type: [String], - // enum: ["CREATE_INSTANT_INVITE", "KICK_MEMBERS", "BAN_MEMBERS", "ADMINISTRATOR", "ManageChannels", "MANAGE_GUILD", "ADD_REACTIONS", "VIEW_AUDIT_LOG", "PRIORITY_SpeakER", "Stream", "ViewChannel", "SEND_MESSAGES", "SEND_TTS_MESSAGES", "MANAGE_MESSAGES", "EMBED_LINKS", "ATTACH_FILES", "READ_MESSAGE_HISTORY", "MENTION_EVERYONE", "USE_EXTERNAL_EMOJIS", "VIEW_GUILD_INSIGHTS", "Connect", "Speak", "MuteMembers", "DeafenMembers", "MoveMembers", "USE_VAD", "CHANGE_NICKNAME", "MANAGE_NICKNAMES", "MANAGE_ROLES", "MANAGE_WEBHOOKS", "MANAGE_EMOJIS"], - // required: false, - // }, - type: { - type: String, - enum: ["member", "role"], - required: false, - }, -}); - -export interface PermissionOverwriteDataDocument extends PermissionOverwriteData, Omit { - allow?: mongoose.Types.Array, - deny?: mongoose.Types.Array, -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PermissionOverwriteDataModel extends mongoose.Model { - -} - -// Default export -export default PermissionOverwriteDataSchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/queue_entry.ts b/src/models/queue_entry.ts index def0130..8680034 100644 --- a/src/models/queue_entry.ts +++ b/src/models/queue_entry.ts @@ -1,58 +1,33 @@ -import mongoose from "mongoose"; +import { getModelForClass, prop } from "@typegoose/typegoose"; /** * A Queue Entry */ -export interface QueueEntry { +export class QueueEntry { /** * The Discord Client ID of the queue Member */ - discord_id: string, + @prop({ required: true }) + discord_id!: string; /** * The Unix Time Stamp of the Queue entry point */ - joinedAt: string, + @prop({ required: true }) + joinedAt!: string; /** * A Multiplier for Importance (use carefully) default is 1 */ - importance?: number, + @prop({ required: false, default: 1 }) + importance?: number; /** * An intent specified by the User and can be seen by Queue Managers (the ones who accept queues) */ - intent?: string, + @prop({ required: false }) + intent?: string; } -/** - * A Schema of a Queue Entry - */ -const QueueEntrySchema = new mongoose.Schema({ - discord_id: { - type: String, - required: true, - }, - joinedAt: { - type: String, - required: true, - }, - importance: { - type: Number, - required: false, - default: 1, - }, - intent: { - type: String, - required: false, +export const QueueEntryModel = getModelForClass(QueueEntry, { + schemaOptions: { + autoCreate: false, }, -}); - -export interface QueueEntryDocument extends QueueEntry, mongoose.Document { - // List getters or non model methods here -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface QueueEntryModel extends mongoose.Model { - // List Model methods here -} - -// Default export -export default QueueEntrySchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/queue_span.ts b/src/models/queue_span.ts index 27d86e6..090099b 100644 --- a/src/models/queue_span.ts +++ b/src/models/queue_span.ts @@ -1,11 +1,49 @@ -import mongoose, { Mongoose } from "mongoose"; -import { WeekTimestamp, Weekday } from "../utils/general"; -import WeekTimestampSchema from "./week_timestamp"; +import { WeekTimestamp, Weekday } from "./week_timestamp"; +import { getModelForClass, prop } from "@typegoose/typegoose"; /** * A Queue Span - A Weekly Timespan with a start- and End Date that can be used to automate Events every week */ export class QueueSpan { + /** + * The Begin Timestamp + */ + @prop({ required: true, type: WeekTimestamp }) + begin!: WeekTimestamp; + + /** + * The End Timestamp + */ + @prop({ required: true, type: WeekTimestamp }) + end!: WeekTimestamp; + + /** + * Shift the Opening by X millixeconds + * @default 0 + */ + @prop({ required: true, default: 0 }) + openShift!: number; + + /** + * Shift the Closing by X milliseconds + * @default 0 + */ + @prop({ required: true, default: 0 }) + closeShift!: number; + + /** + * limit the span to after this date + */ + @prop({ required: false }) + startDate?: Date; + + /** + * limit the span to before this date + * @default 0 + */ + @prop({ required: false }) + endDate?: Date; + /** * Creates a Queue Span (begin.getTime() must be smaller than end.getTime()) * @param begin The Begin Timestamp @@ -15,8 +53,13 @@ export class QueueSpan { * @param startDate limit the span to after this date * @param endDate limit the span to before this date */ - constructor(public begin: WeekTimestamp, public end: WeekTimestamp, public openShift = 0, public closeShift = 0, public startDate?: Date, public endDate?: Date) { - + constructor(begin: WeekTimestamp, end: WeekTimestamp, openShift = 0, closeShift = 0, startDate?: Date, endDate?: Date) { + this.begin = begin; + this.end = end; + this.openShift = openShift; + this.closeShift = closeShift; + this.startDate = startDate; + this.endDate = endDate; } /** @@ -144,48 +187,8 @@ export class QueueSpan { } } -/** - * A Schema of a Queue Entry - */ -const QueueSpanSchema = new mongoose.Schema({ - begin: { - type: WeekTimestampSchema, - required: true, - }, - end: { - type: WeekTimestampSchema, - required: true, - }, - openShift: { - type: mongoose.Schema.Types.Number, - required: true, - default: 0, - }, - closeShift: { - type: mongoose.Schema.Types.Number, - required: true, - default: 0, +export const QueueSpanModel = getModelForClass(QueueSpan, { + schemaOptions: { + autoCreate: false, }, - startDate: { - type: mongoose.Schema.Types.Date, - required: false, - }, - endDate: { - type: mongoose.Schema.Types.Date, - required: false, - }, -}); - -export interface QueueSpanDocument extends QueueSpan, Omit,"equals"> { - // List getters or non model methods here -} - -QueueSpanSchema.loadClass(QueueSpan); - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface QueueSpanModel extends mongoose.Model { - // List Model methods here -} - -// Default export -export default QueueSpanSchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/queues.ts b/src/models/queues.ts index 3a0c0d2..ae852a1 100644 --- a/src/models/queues.ts +++ b/src/models/queues.ts @@ -1,451 +1,353 @@ -import { GuildDocument } from "./guilds"; -import { VoiceChannelDocument } from "./voice_channels"; -import mongoose from "mongoose"; -import QueueEntrySchema, { QueueEntry, QueueEntryDocument } from "./queue_entry"; -import VoiceChannelSpawnerSchema, { VoiceChannelSpawner, VoiceChannelSpawnerDocument } from "./voice_channel_spawner"; +import { prop, DocumentType, SubDocumentType, ArraySubDocumentType, mongoose, getModelForClass } from "@typegoose/typegoose"; +import { VoiceChannel } from "./voice_channels"; +import { QueueEntry } from "./queue_entry"; import * as utils from "../utils/utils"; import { StringReplacements } from "../../typings"; import moment from "moment"; -import QueueSpanSchema, { QueueSpan, QueueSpanDocument } from "./queue_span"; +import { QueueSpan } from "./queue_span"; +import { Guild } from "./guilds"; +import { VoiceChannelSpawner } from "./voice_channel_spawner"; /** * A Queue from the Database */ -export interface Queue { +export class Queue { /** * The Name Of The Queue */ - name: string, + @prop({ required: true }) + name!: string; /** * A Description of the Queue */ - description?: string, + @prop() + description?: string; /** * The max Amount of Users that the queue can handle */ - limit?: number, + @prop() + limit?: number; /** * The Timeout in Milliseconds if the User disconnects from the Queue (usefull for VC based Queues) */ - disconnect_timeout?: number, + @prop() + disconnect_timeout?: number; /** * The Timeout in Milliseconds that the user is kicked off the queue After not accepting a match */ - match_timeout?: number, + @prop() + match_timeout?: number; /** * A Custom Join Message for the Queue. Use ${pos} ${total} ${eta} ${user} and so on to create Dynamic Messages. */ - join_message?: string, + @prop() + join_message?: string; /** * A Custom Match Found Message for the Queue. Use ${pos} ${total} ${eta} ${user} ${match} ${match_channel} and so on to create Dynamic Messages. */ - match_found_message?: string, + @prop() + match_found_message?: string; /** * A Custom Timeout Message. Use ${pos} ${total} ${eta} ${user} ${timeout} and so on to create Dynamic Messages. */ - timeout_message?: string, + @prop() + timeout_message?: string; /** * A Custom Leave Message. Use ${pos} ${total} ${eta} ${user} ${timeout} and so on to create Dynamic Messages. */ - leave_message?: string, + @prop() + leave_message?: string; /** * A Custom Message that is Displayed when the Room is Left (like Please confirm ur stay) */ - leave_room_message?: string, + @prop() + leave_room_message?: string; /** * A Template for spawning in Rooms (if empty default template is used) */ - room_spawner?: VoiceChannelSpawner, + @prop({ type: () => VoiceChannel }) + room_spawner?: SubDocumentType; /** * A text Channel to use if dms are disabled */ - text_channel?: string, + @prop() + text_channel?: string; /** * Whether the queue is locked (this also disables the /queue join command for this queue) */ - locked?: boolean, + @prop({ default: false }) + locked?: boolean; /** * Whether to automatically lock and unlock the queue according to the opening_times */ - auto_lock?: boolean, + @prop({ default: false }) + auto_lock?: boolean; /** * The opening times of the Queue */ - opening_times: QueueSpan[], + @prop({ type: QueueSpan, default: [], required: true }) + opening_times!: mongoose.Types.DocumentArray>; /** * The standard time to shift the unlocking of the queue by in milliseconds */ - openShift?: number, + @prop({ default: 0 }) + openShift?: number; /** * The standard time to shift the locking of the queue by in milliseconds */ - closeShift?: number, + @prop({ default: 0 }) + closeShift?: number; /** * The Entries of the Queue */ - entries: QueueEntry[], -} - -/** - * A Schema For storing and Managing Queues - */ -const QueueSchema = new mongoose.Schema({ - name: { - type: String, - required: true, - }, - description: { - type: String, - required: false, - }, - limit: { - type: Number, - required: false, - }, - disconnect_timeout: { - type: Number, - required: false, - }, - match_timeout: { - type: Number, - required: false, - }, - join_message: { - type: String, - required: false, - }, - match_found_message: { - type: String, - required: false, - }, - timeout_message: { - type: String, - required: false, - }, - leave_room_message: { - type: String, - required: false, - }, - leave_message: { - type: String, - required: false, - }, - room_spawner: { - type: VoiceChannelSpawnerSchema, - required: false, - }, - locked: { - type: mongoose.SchemaTypes.Boolean, - required: false, - default: false, - }, - entries: [{ - type: QueueEntrySchema, - required: true, - default: [], - }], - opening_times: [{ - type: QueueSpanSchema, - required: true, - default: [], - }], - text_channel: { - type: String, - required: false, - }, - auto_lock: { - type: mongoose.SchemaTypes.Boolean, - required: false, - default: false, - }, - openShift: { - type: Number, - required: false, - default: 0, - }, - closeShift: { - type: Number, - required: false, - default: 0, - }, -}); + @prop({ type: QueueEntry, default: [], required: true }) + entries!: mongoose.Types.DocumentArray>; -export interface QueueDocument extends Queue, mongoose.Document { - room_spawner?: VoiceChannelSpawnerDocument, - entries: mongoose.Types.DocumentArray, - opening_times: mongoose.Types.DocumentArray, - // List getters or non model methods here /** * Put an Entry into the Queue * @param entry The Queue Entry */ - join(entry: QueueEntry): Promise, + public async join(this: DocumentType, entry: QueueEntry): Promise{ + if (this.entries.find(x => x.discord_id === entry.discord_id)) { + throw new Error("Dublicate Entry"); + } + this.entries.push(entry); + await this.$parent()?.save(); + return this.getEntry(entry.discord_id)!; + } + + /** + * Leaves the queue + * @param discord_id The Discord ID of the entry + */ + public async leave(this: DocumentType, discord_id: string): Promise{ + const entry = this.entries.find(x => x.discord_id === discord_id); + if (!entry) { + throw new Error("Not Found"); + } + this.entries.splice(this.entries.indexOf(entry), 1); + await this.$parent()?.save(); + return entry; + } /** * Gets the Sorted Entries with the First ones being the ones with the highest Importance * @param limit How many entries should we get at most? */ - getSortedEntries(limit?: number | undefined): QueueEntryDocument[], + public getSortedEntries(this: DocumentType, limit?: number | undefined): DocumentType[]{ + const entries = this.entries.sort((x, y) => { + const x_importance = (Date.now() - (+x.joinedAt)) * (x.importance || 1); + const y_importance = (Date.now() - (+y.joinedAt)) * (y.importance || 1); + return y_importance - x_importance; + }); + return entries.slice(0, limit); + } /** * Returns true if the ID is contained in the queue * @param discord_id the Discord ID to check if it's contained */ - contains(discord_id: string): boolean, + public contains(this:DocumentType,discord_id: string): boolean { + return this.entries.some(x => x.discord_id === discord_id); + } /** * Gets The Entry that has the given Discord ID * @param discord_id The Discord ID of the Entry */ - getEntry(discord_id: string): QueueEntryDocument | null, + public getEntry(this: DocumentType, discord_id: string): DocumentType | null { + return this.entries.find(x => x.discord_id === discord_id) ?? null; + } /** * Gets the Position in the Current Queue * @param discord_id the Discord ID of the entry */ - getPosition(discord_id: string): number, - /** - * Leaves the queue - * @param discord_id The Discord ID of the entry - */ - leave(discord_id: string): Promise, + public getPosition(this: DocumentType, discord_id: string): number{ + return this.getSortedEntries().findIndex(x => x.discord_id === discord_id); + } /** * Interpolates the Queue String * @param string The String to Interpolate */ - interpolateQueueString(string: string): string | null, + public interpolateQueueString(this: DocumentType, string: string): string | null; /** * Interpolates the Queue String * @param string The String to Interpolate * @param discord_id The Discord ID of the Entry */ - interpolateQueueString(string: string, discord_id: string): string | null, + public interpolateQueueString(this: DocumentType, string: string, discord_id: string): string | null; /** * Interpolates the Queue String * @param string The String to Interpolate * @param entry The Queue Entry */ - interpolateQueueString(string: string, entry: QueueEntry): string | null, + public interpolateQueueString(this: DocumentType, string: string, entry: QueueEntry): string | null; + /** + * Interpolates the Queue String + * @param string The String to Interpolate + * @param entry_resolvable The Entry Resolvable + */ + public interpolateQueueString(this: DocumentType, string: string, entry_resolvable?: string | QueueEntry | undefined): string | null; /** * Interpolates the Queue String * @param string The String to Interpolate * @param entry_resolvable The Entry Resolvable */ - interpolateQueueString(string: string, entry_resolvable?: string | QueueEntry | undefined): string | null, + public interpolateQueueString(this: DocumentType, string:string, entry_resolvable?: string | QueueEntry | undefined): string | null{ + try { + const replacements: StringReplacements = { + "limit": this.limit, + "name": this.name, + "description": this.description, + "eta": "null", + "timeout": (this.disconnect_timeout ?? 0) / 1000, + "total": this.entries.length, + }; + + if (entry_resolvable) { + let entry: QueueEntry | null; + if (typeof entry_resolvable === "string") { + entry = this.getEntry(entry_resolvable); + } else { + entry = entry_resolvable; + } + if (entry && this.contains(entry.discord_id)) { + const entryReplacements: StringReplacements = { + "member_id": entry.discord_id, + "user": `<@${entry.discord_id}>`, + "pos": this.getPosition(entry.discord_id) + 1, + "time_spent": moment.duration(Date.now() - (+entry.joinedAt)).format("d[d ]h[h ]m[m ]s.S[s]"), + }; + for (const [key, value] of Object.entries(entryReplacements)) { + replacements[key] = value; + } + } + } + // Interpolate String + return utils.general.interpolateString(string, replacements); + } catch (error) { + console.log(error); + return null; + } + } /** * Gets the leave Message of the Queue */ - getLeaveMessage(): string, + public getLeaveMessage(this: DocumentType): string; /** * Gets the leave Message of the Queue * @param discord_id The Discord ID of the Leaver */ - getLeaveMessage(discord_id: string): string, + public getLeaveMessage(this: DocumentType, discord_id: string): string; /** * Gets the leave Message of the Queue * @param entry The Entry that wants to leave the queue */ - getLeaveMessage(entry: QueueEntry): string, + public getLeaveMessage(this: DocumentType, entry: QueueEntry): string; /** * Gets the leave Message of the Queue * @param entry_resolvable The Entry Resolvable */ - getLeaveMessage(entry_resolvable?: string | QueueEntry | undefined): string, + public getLeaveMessage(this: DocumentType, entry_resolvable?: string | QueueEntry | undefined): string{ + const default_leave_message = "You left the Queue."; + if (this.leave_message) { + const leave_msg = this.interpolateQueueString(this.leave_message!, entry_resolvable); + return leave_msg ?? default_leave_message; + } + else { + return default_leave_message; + } + } /** * Gets the leave Room Message of the Queue */ - getLeaveRoomMessage(): string, + public getLeaveRoomMessage(this: DocumentType): string; /** * Gets the leave Room Message of the Queue * @param discord_id The Discord ID of the Leaver */ - getLeaveRoomMessage(discord_id: string): string, + public getLeaveRoomMessage(this: DocumentType, discord_id: string): string; /** * Gets the leave Room Message of the Queue * @param entry The Entry that wants to leave the queue */ - getLeaveRoomMessage(entry: QueueEntry): string, + public getLeaveRoomMessage(this: DocumentType, entry: QueueEntry): string; /** * Gets the leave Room Message of the Queue * @param entry_resolvable The Entry Resolvable */ - getLeaveRoomMessage(entry_resolvable?: string | QueueEntry | undefined): string, + public getLeaveRoomMessage(this: DocumentType, entry_resolvable?: string | QueueEntry | undefined): string{ + const default_leave_message = `You left the Room. Please confirm your stay or you will be removed from the queue after the Timeout of ${(this.disconnect_timeout ?? 0) / 1000}s.`; + if (this.leave_room_message) { + const leave_msg = this.interpolateQueueString(this.leave_room_message, entry_resolvable); + return leave_msg ?? default_leave_message; + } + else { + return default_leave_message; + } + } /** * Gets the join Message of the Queue */ - getJoinMessage(): string, + public getJoinMessage(this: DocumentType): string; /** * Gets the join Message of the Queue * @param discord_id The Discord ID of the Joiner */ - getJoinMessage(discord_id: string): string, + public getJoinMessage(this: DocumentType, discord_id: string): string; /** * Gets the join Message of the Queue * @param entry The Entry that wants to join the queue */ - getJoinMessage(entry: QueueEntry): string, + public getJoinMessage(this: DocumentType, entry: QueueEntry): string; /** * Gets the leave Message of the Queue * @param entry_resolvable The Entry Resolvable */ - getJoinMessage(entry_resolvable?: string | QueueEntry | undefined): string, + public getJoinMessage(this: DocumentType, entry_resolvable?: string | QueueEntry | undefined): string{ + const default_join_message = "You left the Queue."; + if (this.join_message) { + const join_msg = this.interpolateQueueString(this.join_message, entry_resolvable); + return join_msg ?? default_join_message; + } + else { + return default_join_message; + } + } /** * Returns `true` if the Queue is Empty */ - isEmpty(): boolean, + public isEmpty(this: DocumentType): boolean{ + return !!this.entries.length; + } /** * Locks the queue. This removes the voice Channel Permissions and disallows the queue from the /queue join command */ - lock(): Promise; + public async lock(this: DocumentType): Promise{ + this.locked = true; + await this.$parent()?.save(); + } /** * Unlocks the queue. This restores the voice Channel Permissions and allows the queue from the /queue join command */ - unlock(): Promise; + public async unlock(this: DocumentType): Promise{ + this.locked = false; + await this.$parent()?.save(); + } /** * Locks or Unlocks the queue (opposite State). */ - toggleLock(): Promise; + public async toggleLock(this: DocumentType): Promise{ + this.locked = !this.locked; + await this.$parent()?.save(); + } /** * Resolves all Waiting rooms for the current Queue */ - getWaitingRooms(guild: GuildDocument): VoiceChannelDocument[], -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface QueueModel extends mongoose.Model { - // List Model methods here -} - -// --Methods-- - -QueueSchema.method("join", async function (entry: QueueEntry) { - if (this.entries.find(x => x.discord_id === entry.discord_id)) { - throw new Error("Dublicate Entry"); - } - this.entries.push(entry); - await this.$parent()?.save(); - return this.getEntry(entry.discord_id)!; -}); - -QueueSchema.method("leave", async function (discord_id: string) { - const entry = this.entries.find(x => x.discord_id === discord_id); - if (!entry) { - throw new Error("Not Found"); + public getWaitingRooms(this: DocumentType, guild: Guild): DocumentType[]{ + return guild.voice_channels?.filter(x => x.queue?._id.equals(this._id!)) ?? []; } - this.entries.splice(this.entries.indexOf(entry), 1); - await this.$parent()?.save(); - return entry; -}); - -QueueSchema.method("getSortedEntries", function (limit?: number) { - const entries = this.entries.toObject>().sort((x, y) => { - const x_importance = (Date.now() - (+x.joinedAt)) * (x.importance || 1); - const y_importance = (Date.now() - (+y.joinedAt)) * (y.importance || 1); - return y_importance - x_importance; - }); - return entries.slice(0, limit); -}); - -QueueSchema.method("isEmpty", function (): boolean { - return this.entries.length < 1; -}); -QueueSchema.method("contains", function (discord_id: string): boolean { - return (this.entries.toObject>().find(x => x.discord_id === discord_id)) ? true : false; -}); -QueueSchema.method("getEntry", function (discord_id: string) { - return this.entries.find(x => x.discord_id === discord_id) ?? null; -}); - -QueueSchema.method("getPosition", function (discord_id: string): number { - return this.getSortedEntries().findIndex(x => x.discord_id === discord_id); -}); - -QueueSchema.method("interpolateQueueString", function (string: string, entry_resolvable?: string | QueueEntry): string | null { - try { - const replacements: StringReplacements = { - "limit": this.limit, - "name": this.name, - "description": this.description, - "eta": "null", - "timeout": (this.disconnect_timeout ?? 0) / 1000, - "total": this.entries.length, - }; - - if (entry_resolvable) { - let entry: QueueEntry | null; - if (typeof entry_resolvable === "string") { - entry = this.getEntry(entry_resolvable); - } else { - entry = entry_resolvable; - } - if (entry && this.contains(entry.discord_id)) { - const entryReplacements: StringReplacements = { - "member_id": entry.discord_id, - "user": `<@${entry.discord_id}>`, - "pos": this.getPosition(entry.discord_id) + 1, - "time_spent": moment.duration(Date.now() - (+entry.joinedAt)).format("d[d ]h[h ]m[m ]s.S[s]"), - }; - for (const [key, value] of Object.entries(entryReplacements)) { - replacements[key] = value; - } - } - } - // Interpolate String - return utils.general.interpolateString(string, replacements); - } catch (error) { - console.log(error); - return null; - } -}); - - -QueueSchema.method("getJoinMessage", function (entry_resolvable?: string | QueueEntry) { - const default_join_message = "You left the Queue."; - if (this.join_message) { - const join_msg = this.interpolateQueueString(this.join_message, entry_resolvable); - return join_msg ?? default_join_message; - } - else { - return default_join_message; - } -}); - -QueueSchema.method("getLeaveMessage", function (entry_resolvable?: string | QueueEntry) { - const default_leave_message = "You left the Queue."; - if (this.leave_message) { - const leave_msg = this.interpolateQueueString(this.leave_message, entry_resolvable); - return leave_msg ?? default_leave_message; - } - else { - return default_leave_message; - } -}); - -QueueSchema.method("getLeaveRoomMessage", function (entry_resolvable?: string | QueueEntry) { - const default_leave_message = `You left the Room. Please confirm your stay or you will be removed from the queue after the Timeout of ${(this.disconnect_timeout ?? 0) / 1000}s.`; - if (this.leave_room_message) { - const leave_msg = this.interpolateQueueString(this.leave_room_message, entry_resolvable); - return leave_msg ?? default_leave_message; - } - else { - return default_leave_message; - } -}); - -QueueSchema.method("lock", async function () { - this.locked = true; - await this.$parent()?.save(); -}); - -QueueSchema.method("unlock", async function () { - this.locked = false; - await this.$parent()?.save(); -}); - -QueueSchema.method("toggleLock", async function () { - this.locked = !this.locked; - await this.$parent()?.save(); -}); - -QueueSchema.method("getWaitingRooms", function (guild: GuildDocument) { - return guild.voice_channels?.filter(x => x.queue?.equals(this._id!)) ?? []; -}); +} -// Default export -export default QueueSchema; \ No newline at end of file +export const QueueModel = getModelForClass(Queue, { + schemaOptions: { + autoCreate: false, + }, +}); \ No newline at end of file diff --git a/src/models/rooms.ts b/src/models/rooms.ts index b8074a7..12df910 100644 --- a/src/models/rooms.ts +++ b/src/models/rooms.ts @@ -1,214 +1,180 @@ import mongoose from "mongoose"; import { EventDate } from "../../typings"; -import EventSchema, { Event, EventDocument, eventType } from "./events"; -import UserSchema from "./users"; +import { Event, eventType } from "./events"; +import { UserModel } from "./users"; import { sessionRole } from "./sessions"; -import { Channel } from "./text_channels"; import { Snowflake, User } from "discord.js"; import { filterAsync } from "../utils/general"; +import { ArraySubDocumentType, DocumentType, ReturnModelType, getModelForClass, prop } from "@typegoose/typegoose"; -export interface Room extends Channel { +export class Room { /** * The Channel ID provided by Discord */ - _id: string, + @prop({ required: true }) + _id!: string; /** * If the Channel exists, it's active */ - active: boolean, + @prop({ required: true }) + active!: boolean; /** * If Someone tampered with the Permissions/Name or Position of the Channel (or other Settings) */ - tampered: boolean, + @prop({ required: true }) + tampered!: boolean; /** * Only set to true if session had a clean exit */ - end_certain: boolean, + @prop({ required: true }) + end_certain!: boolean; /** * The Guild The Room is in */ - guild: string, + @prop({ required: true }) + guild!: string; /** * The Events that happen in the Channel */ - events: Event[], -} - -const RoomSchema = new mongoose.Schema({ - _id: { - type: String, - required: true, - }, - active: { - type: Boolean, - required: true, - }, - tampered: { - type: Boolean, - required: true, - }, - end_certain: { - type: Boolean, - required: true, - }, - guild: { - type: String, - required: true, - }, - events: [{ - type: EventSchema, - required: true, - default: [], - }], -}); + @prop({ required: true, type: () => [Event], default: [] }) + events!: mongoose.Types.DocumentArray>; -export interface RoomDocument extends Room, Omit { - events: mongoose.Types.DocumentArray, /** * Collects All User IDs that joined The Channel at least Once */ - getUsers(): string[], + public async getUsers(this: DocumentType): Promise { + return (await RoomModel.aggregate<{ _id: string, count: number }>([ + { $match: { _id: this._id } }, + { $unwind: { path: "$events" } }, + { $match: { "events.type": { $in: [eventType.user_join, eventType.move_member] } } }, + { $group: { _id: { $cond: { if: { $eq: ["$events.type", eventType.user_join] }, then: "$events.emitted_by", else: { $ifNull: ["$events.target", "NULL"] } } }, count: { $sum: 1 } } }, + { $match: { _id: { $ne: "NULL" }, count: { $gt: 0 } } }, + ])).map(x => x._id); + } + /** * Gets The First Time someone Joins and Their Discord ID */ - getFirstJoinTimes(): EventDate[], + public async getFirstJoinTimes(this: DocumentType): Promise { + // We Assume that the Role Does not change During the Rooms Lifetime + return (await RoomModel.aggregate<{ _id: string, timestamp: string }>([ + { $match: { _id: this._id } }, + { $unwind: { path: "$events" } }, + { $match: { "events.type": { $in: [eventType.user_join, eventType.move_member] } } }, + { $group: { _id: { $cond: { if: { $eq: ["$events.type", eventType.user_join] }, then: "$events.emitted_by", else: { $ifNull: ["$events.target", "NULL"] } } }, timestamp: { $min: "$events.timestamp" } } }, + { $match: { _id: { $ne: "NULL" } } }, + ])).map(x => { return { target_id: x._id, timestamp: x.timestamp } as EventDate; }); + } + /** * Gets The Users with Their Roles at Join Date */ - getUserRoles(): Promise<{ + public async getUserRoles(this: DocumentType): Promise<{ userID: string; role: sessionRole | null; - }[]>, + }[]> { + const users = await this.getFirstJoinTimes(); + const userRoles: { + userID: string; + role: sessionRole | null; + }[] = []; + for (const user of users) { + const userModel = await UserModel.findById(user.target_id); + if (!userModel) { + userRoles.push({ userID: user.target_id, role: null }); + continue; + } + const role = await userModel.getRole(this.guild, (+user.timestamp)); + const b = { + userID: user.target_id, + role: role, + }; + userRoles.push(b); + } + return userRoles; + } + /** * Gets The Participants Of the Channel */ - getParticipants(): Promise, + public async getParticipants(this: DocumentType): Promise { + const roles = await this.getUserRoles(); + return roles.filter(x => x.role == sessionRole.participant || x.role == null).map(x => x.userID); + } + /** * Returns true if a given user has joined this channel at least once * @param user The User ID to check */ - wasVisitedBy(user: User | Snowflake): boolean, + public async wasVisitedBy(this: DocumentType, user: User | Snowflake): Promise { + if (user instanceof User) { + user = user.id; + } + return (await this.getUsers()).includes(user); + } + /** * Returns true if a given user has joined this channel as a Participant at least once * @param user The User ID to check */ - wasParticipating(user: User | Snowflake): Promise, -} + public async wasParticipating(this: DocumentType, user: User | Snowflake): Promise { + if (user instanceof User) { + user = user.id; + } + return (await this.getUserRoles()).some(x => x.userID === user && x.role != sessionRole.coach); + } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface RoomModel extends mongoose.Model { /** * Gets the Rooms a User visited - * @param user The User ID to check */ - getVisitedRooms(user: User | Snowflake): Promise, + public static async getVisitedRooms(this: ReturnModelType, user: User | Snowflake): Promise { + if (user instanceof User) { + user = user.id; + } + // return (await this.find()).filter(x => x.wasVisitedBy(user)).map(x => x.toObject()); + return this.find({ + events: { + $elemMatch: { + $or: [ + { type: eventType.user_join, emitted_by: user }, + { type: eventType.move_member, target: user }, + ], + }, + }, + }); + } + /** * Gets the Amount of Rooms a User visited - * @param user The User ID to check */ - getRoomCount(user: User | Snowflake): Promise, + public static async getRoomCount(this: ReturnModelType, user: User | Snowflake): Promise { + return (await RoomModel.getVisitedRooms(user)).length; + } + /** * Gets the Rooms a User participated in * @param user The User ID to check * @param rooms All Rooms */ - getParticipantRooms(user: User | Snowflake, rooms?: (RoomDocument & { _id: string; })[]): Promise, + public static async getParticipantRooms(this: ReturnModelType, user: User | Snowflake, rooms?: (DocumentType & { _id: string; })[]): Promise { + if (user instanceof User) { + user = user.id; + } + return (await filterAsync(rooms ?? await this.find(), async x => await x.wasParticipating(user))).map(x => x.toObject()); + } + /** * Gets the Amount of Rooms a User participated in * @param user The User ID to check * @param rooms All Rooms */ - getParticipantRoomCount(user: User | Snowflake, rooms?: (RoomDocument & { _id: string; })[]): Promise, -} - -// --Methods-- - -RoomSchema.method("getUsers", function () { - return [... new Set(this.events.filter(x => [eventType.user_join, eventType.move_member].includes(x.type)).map(x => x.type === eventType.user_join ? x.emitted_by : x.target!))]; -}); - -RoomSchema.method("getUserRoles", async function () { - const users = this.getFirstJoinTimes(); - const userRoles: { - userID: string; - role: sessionRole | null; - }[] = []; - for (const user of users) { - const userSchema = await UserSchema.findById(user.target_id); - if (!userSchema) { - userRoles.push({ userID: user.target_id, role: null }); - continue; - } - const role = await userSchema.getRole(this.guild, (+user.timestamp)); - const b = { - userID: user.target_id, - role: role, - }; - userRoles.push(b); + public static async getParticipantRoomCount(this: ReturnModelType, user: User | Snowflake, rooms?: (DocumentType & { _id: string; })[]): Promise { + return (await this.getParticipantRooms(user, rooms)).length; } - return userRoles; - - // Why the fuck does the below Code not Work??? Literally the Same >:( - - // let userRoles = users.map(async (x) => { - // let userSchema = await UserSchema.findById(x.target_id); - // if(!userSchema) return { userID: x.target_id, role: null}; - // let role = await userSchema.getRole(this.guild, (+x.timestamp)); - // let b = { - // userID: x.target_id, - // role: role, - // } - // return b; - // }); - // let a = await userRoles; -}); - -RoomSchema.method("wasVisitedBy", function (user: User | Snowflake) { - if (user instanceof User) { - user = user.id; - } - return this.getUsers().includes(user); -}); - -RoomSchema.method("wasParticipating", async function (user: User | Snowflake) { - if (user instanceof User) { - user = user.id; - } - return (await this.getUserRoles()).some(x => x.userID === user && x.role != sessionRole.coach); -}); - -RoomSchema.method("getParticipants", async function () { - const roles = await this.getUserRoles(); - return roles.filter(x => x.role == sessionRole.participant || x.role == null).map(x => x.userID); -}); - -RoomSchema.method("getFirstJoinTimes", function () { - // We Assume that the Role Does not change During the Rooms Lifetime - const eventDates = this.events.filter(x => [eventType.user_join, eventType.move_member].includes(x.type)).map(x => { return { timestamp: x.timestamp, target_id: (x.type === eventType.user_join ? x.emitted_by : x.target!), event_id: (x as EventDocument)._id } as EventDate; }); - return eventDates.filter((x, pos) => eventDates.findIndex(y => y.target_id === x.target_id) === pos); -}); - -RoomSchema.static("getVisitedRooms", async function (user: User | Snowflake) { - if (user instanceof User) { - user = user.id; - } - return (await this.find()).filter(x => x.wasVisitedBy(user)).map(x => x.toObject()); -}); - -RoomSchema.static("getRoomCount", async function (user: User | Snowflake) { - return (await this.getVisitedRooms(user)).length; -}); - -RoomSchema.static("getParticipantRooms", async function (user: User | Snowflake, rooms?: (RoomDocument & { _id: string; })[]) { - if (user instanceof User) { - user = user.id; - } - return (await filterAsync(rooms ?? await this.find(), async x => await x.wasParticipating(user))).map(x => x.toObject()); -}); - -RoomSchema.static("getParticipantRoomCount", async function (user: User | Snowflake, rooms?: (RoomDocument & { _id: string; })[]) { - return (await this.getParticipantRooms(user, rooms)).length; -}); +} -// Default export -export default mongoose.model("Rooms", RoomSchema); \ No newline at end of file +export const RoomModel = getModelForClass(Room, { + schemaOptions: { + autoCreate: true, + }, +}); \ No newline at end of file diff --git a/src/models/sessions.ts b/src/models/sessions.ts index 250848e..60ac1ac 100644 --- a/src/models/sessions.ts +++ b/src/models/sessions.ts @@ -1,129 +1,82 @@ -import mongoose from "mongoose"; -import RoomSchema from "./rooms"; -import { Channel } from "./text_channels"; - +import {RoomModel} from "./rooms"; +import {DocumentType, getModelForClass, prop, mongoose} from "@typegoose/typegoose"; export enum sessionRole { "participant" = "participant", "coach" = "coach", "supervisor" = "supervisor", } -export interface Session extends Omit { +export class Session { /** * Whether the Session is currently active */ - active: boolean, + @prop({required: true}) + active!: boolean; /** * The User that the Session belongs to */ - user: string, + @prop({required: true}) + user!: string; /** * A Guild that the Session belongs to */ - guild?: string, + @prop() + guild?: string; /** * A Queue that is linked to the Session */ - queue?: mongoose.Types.ObjectId, + @prop() + queue?: mongoose.Types.ObjectId; /** * The Role that the user plays */ - role: sessionRole, + @prop({required: true, enum: sessionRole}) + role!: sessionRole; /** * The Start Timestamp */ - started_at: string, + @prop() + started_at?: string; /** * The End Timestamp (Set when active==false) */ - ended_at?: string, + @prop() + ended_at?: string; /** * Only set to true if session had a clean exit */ - end_certain: boolean, + @prop({required: true}) + end_certain!: boolean; /** * The Room IDs That Were Visited in the Session */ - rooms: string[], -} - -const SessionSchema = new mongoose.Schema({ - active: { - type: Boolean, - required: true, - }, - user: { - type: String, - required: true, - }, - guild: { - type: String, - required: false, - }, - queue: { - type: String, - required: false, - }, - role: { - type: String, - enum: Object.keys(sessionRole), - required: true, - }, - started_at: { - type: String, - required: false, - }, - ended_at: { - type: String, - required: false, - }, - end_certain: { - type: Boolean, - required: true, - }, - rooms: [{ - type: String, - required: true, - default: [], - }], -}); + @prop({required: true, type: () => [String], default: []}) + rooms!: mongoose.Types.Array; -export interface SessionDocument extends Session, mongoose.Document { - rooms: mongoose.Types.Array, /** * Gets The Number of Rooms that were Visited in the Session */ - getRoomAmount(): number, + public getRoomAmount(this: DocumentType): number { + return this.rooms.length; + } + /** * Gets The Number of Participants met in the Session (assuming the Chanels Lifetime is within the Session) */ - getParticipantAmount(): Promise, -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface SessionModel extends mongoose.Model { - -} - -// --Methods-- - -SessionSchema.method("getRoomAmount", function () { - return this.rooms.length; -}); - -SessionSchema.method("getParticipantAmount", async function () { - let amount = 0; - for (const r of this.rooms) { - const roomData = await RoomSchema.findById(r); - if (roomData) { - amount += (await roomData.getParticipants()).length; + public async getParticipantAmount(this: DocumentType): Promise { + let amount = 0; + for (const r of this.rooms) { + const roomData = await RoomModel.findById(r); + if (roomData) { + amount += (await roomData.getParticipants()).length; + } } + return amount; } - return amount; -}); -// SessionSchema.method('getParticipants', function () { -// return RoomSchema.f; -// }); +} -// Default export -export default mongoose.model("Sessions", SessionSchema); \ No newline at end of file +export const SessionModel = getModelForClass(Session, { + schemaOptions: { + autoCreate: true, + }, +}); \ No newline at end of file diff --git a/src/models/slash_command_permission.ts b/src/models/slash_command_permission.ts index ff9fe11..9cbf458 100644 --- a/src/models/slash_command_permission.ts +++ b/src/models/slash_command_permission.ts @@ -1,46 +1,20 @@ import { ApplicationCommandPermissionType } from "discord.js"; -import mongoose from "mongoose"; +import { prop } from "@typegoose/typegoose"; -export interface SlashCommandPermission { +export class SlashCommandPermission { /** * The User or Role ID */ - id: string, + @prop({ required: true }) + id!: string; /** * The ID Type (Role or User) */ - type: ApplicationCommandPermissionType; + @prop({ required: true, enum: ApplicationCommandPermissionType, default: ApplicationCommandPermissionType.User }) + type!: ApplicationCommandPermissionType; /** * Whether to permit or not permit the User Or Role */ - permission: boolean; -} - -const SlashCommandPermissionSchema = new mongoose.Schema({ - id: { - type: String, - required: true, - }, - type: { - type: Number, - enum: [1, 2], - default: 2, - required: true, - }, - permission: { - type: Boolean, - required: true, - }, -}); - -export interface SlashCommandPermissionDocument extends SlashCommandPermission, Omit { - -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface SlashCommandPermissionModel extends mongoose.Model { - -} - -// Default export -export default SlashCommandPermissionSchema; \ No newline at end of file + @prop({ required: true }) + permission!: boolean; +} \ No newline at end of file diff --git a/src/models/slash_command_settings.ts b/src/models/slash_command_settings.ts index 386c2db..e32bcf4 100644 --- a/src/models/slash_command_settings.ts +++ b/src/models/slash_command_settings.ts @@ -1,90 +1,51 @@ -import SlashCommandPermissionSchema, { SlashCommandPermission, SlashCommandPermissionDocument } from "./slash_command_permission"; -import mongoose from "mongoose"; +import { SlashCommandPermission } from "./slash_command_permission"; import { PermissionResolvable } from "discord.js"; - -export interface SlashCommandSettings { +import { ArraySubDocumentType, SubDocumentType, getModelForClass, prop, mongoose } from "@typegoose/typegoose"; +export class SlashCommandSettings { /** * The original Command name used to retrieve it from the event handler */ - internal_name: string, + @prop({ required: true,sparse: true }) + internal_name!: string; /** * The Command Name overwrite */ - name?: string, + @prop({ required: false }) + name?: string; /** * The Command Description overwrite */ - description?: string, + @prop({ required: false }) + description?: string; /** * The Default Command Permission overwrite */ - defaultPermission?: PermissionResolvable, + @prop({ required: false }) + defaultPermission?: SubDocumentType; /** * If the command should be completely removed from the slash command List */ - disabled?: boolean, + @prop({ required: false }) + disabled?: boolean; /** * All the command aliases (won't be shown general help) */ - aliases: string[], + @prop({ required: true, default: [], type: String }) + aliases!: mongoose.Types.Array; /** * The Command permissions */ - permissions: SlashCommandPermission[], -} - -const SlashCommandSettingsSchema = new mongoose.Schema({ - internal_name: { - type: String, - required: true, - sparse: true, - // unique: true, - }, - name: { - type: String, - required: false, - }, - description: { - type: String, - required: false, - }, - disabled: { - type: Boolean, - required: false, - }, - aliases: [{ - type: String, - required: true, - default: [], - }], - permissions: [SlashCommandPermissionSchema], - // defaultPermission: { - // type: String, - // required: false, - // }, -}); - -export interface SlashCommandSettingsDocument extends SlashCommandSettings, mongoose.Document < mongoose.Types.ObjectId > { - aliases: mongoose.Types.Array, - permissions: mongoose.Types.DocumentArray, - // defaultPermission: string, - /** - * Gets postable Permission objects off the settings - */ - getPostablePermissions(): SlashCommandPermission[], -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface SlashCommandSettingsModel extends mongoose.Model { + @prop({ required: true, default: [], type: () => [SlashCommandPermission] }) + permissions!: mongoose.Types.DocumentArray>; + public getPostablePermissions(): SlashCommandPermission[] { + return this.permissions + .map(x => { return { id: x.id, permission: x.permission, type: x.type } as SlashCommandPermission; }); + } } -// Methods - -SlashCommandSettingsSchema.method("getPostablePermissions", function () { - return this.permissions.toObject>() - .map(x => { return { id: x.id, permission: x.permission, type: x.type } as SlashCommandPermission; }); -}); - -// Default export -export default SlashCommandSettingsSchema; \ No newline at end of file +export const SlashCommandSettingsModel = getModelForClass(SlashCommandSettings, { + schemaOptions: { + autoCreate: false, + }, +}); \ No newline at end of file diff --git a/src/models/text_channels.ts b/src/models/text_channels.ts index 9634b04..b40ae97 100644 --- a/src/models/text_channels.ts +++ b/src/models/text_channels.ts @@ -1,5 +1,5 @@ -import { TextChannelType } from "discord.js"; -import mongoose from "mongoose"; +import { ChannelType, TextChannelType } from "discord.js"; +import {getModelForClass, prop} from "@typegoose/typegoose"; /** * Database Representation of a Discord Channel @@ -12,7 +12,7 @@ export interface Channel { /** * The Channel Type */ - channel_type: TextChannelType, + channel_type: ChannelType, /** * Whether the Channel is being managed by or relevant to the bot */ @@ -27,65 +27,36 @@ export interface Channel { owner?: string, } -export interface TextChannel extends Channel { +export class TextChannel implements Channel { + @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]}) + channel_type!: TextChannelType; + @prop({ required: true }) + managed!: boolean; + @prop() + category?: string | undefined; + @prop() + owner?: string | undefined; /** * Channel Specific Prefix, cuz why not? :D */ - prefix?: string, + @prop() + prefix?: string; /** * If the Bot is enabled in this channel */ - listen_for_commands: boolean, + @prop({ required: true }) + listen_for_commands!: boolean; /** * WHETHER THE CHANNEL IS CAPS-ONLY */ - rage_channel?: boolean, + @prop({default: false}) + rage_channel?: boolean; } -const TextChannelSchema = new mongoose.Schema({ - channel_type: { - type: Number, - enum: [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13], - required: true, +export const TextChannelModel = getModelForClass(TextChannel, { + schemaOptions: { + autoCreate: false, }, - // whitelist_user_groups: [{ - // type: String, - // required: true - // }], - // blacklist_user_groups: [{ - // type: String, - // required: true - // }], - managed: { - type: Boolean, - required: true, - }, - owner: { - type: String, - required: false, - }, - prefix: { - type: String, - required: false, - }, - listen_for_commands: { - type: Boolean, - required: true, - }, - rage_channel: { - type: String, - required: false, - }, -}); - -export interface TextChannelDocument extends TextChannel, Omit { - -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface TextChannelModel extends mongoose.Model { - -} - -// Default export -export default TextChannelSchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/users.ts b/src/models/users.ts index 56ef62e..802cfac 100644 --- a/src/models/users.ts +++ b/src/models/users.ts @@ -1,139 +1,90 @@ // import mongoose from 'mongoose'; import mongoose from "mongoose"; -import SessionSchema, { SessionDocument, sessionRole } from "./sessions"; +import { Session, SessionModel, sessionRole } from "./sessions"; +import { DocumentType, Ref, getModelForClass, prop } from "@typegoose/typegoose"; +import { DBRole } from "./bot_roles"; // TODO: User Settings, Other User Stuff /** * A User from the Database */ -export interface User { +export class User { /** * The User ID provided by Discord */ - _id: string, + @prop({ required: true }) + _id!: string; // TODO: Some Link to Moodle Account /** * The Sessions */ - sessions: mongoose.Types.ObjectId[], + @prop({ required: true, ref: () => Session, default: [] }) + sessions!: mongoose.Types.Array>; /** * Die TU-ID */ - tu_id?: string, + @prop({ required: false, unique: true, sparse: true }) + tu_id?: string; /** * Die Moodle-ID */ - moodle_id?: string, + @prop({ required: false, unique: true, sparse: true }) + moodle_id?: string; /** * Log all the Server Roles when a Member leaves */ - server_roles: string[], + @prop({ required: true, default: [] }) + server_roles!: mongoose.Types.Array; /** * The Roles that the User was assigned after verification */ - token_roles: mongoose.Types.ObjectId[], -} - -/** - * A Schema For storing and Managing Guilds - */ -const UserSchema = new mongoose.Schema({ - _id: { - type: String, - required: true, - }, - sessions: [{ - type: mongoose.Types.ObjectId, - required: true, - default: [], - }], - tu_id: { - type: String, - required: false, - unique: true, - sparse: true, - }, - moodle_id: { - type: String, - required: false, - unique: true, - sparse: true, - }, - server_roles: [{ - type: String, - required: true, - default: [], - }], - token_roles: [{ - type: mongoose.Types.ObjectId, - required: true, - default: [], - }], -}); + @prop({ required: true, default: [], ref: () => DBRole }) + token_roles!: mongoose.Types.Array>; -export interface UserDocument extends User, Omit { - sessions: mongoose.Types.Array, - token_roles: mongoose.Types.Array, - server_roles: mongoose.Types.Array, - // List getters or non model methods here /** * Checks if the User has Active Sessions */ - hasActiveSessions(): Promise, + public async hasActiveSessions(this: DocumentType): Promise{ + return !!(await SessionModel.findOne({ user: (this._id as string), active: true })); + } + /** * Gets all Sessions from a User */ - getSessions(): Promise, + public async getSessions(this: DocumentType): Promise[]>{ + return await SessionModel.find({ user: (this._id as string) }); + } + /** * Gets all Active Sessions from a User */ - getActiveSessions(): Promise, + public async getActiveSessions(this: DocumentType): Promise[]>{ + return await SessionModel.find({ user: (this._id as string), active: true }); + } + /** * Gets The Role a User had at the given Time * @param guildID The Guild That is associated With the Session * @param timestamp The Timestamp (defaults to Date.now()) */ - getRole(guildID: string, timestamp?: number | undefined): Promise, -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface UserModel extends mongoose.Model { - // List Model methods here -} - -// --Methods-- - -UserSchema.method("hasActiveSessions", async function () { - if (await SessionSchema.findOne({ user: (this._id as string), active: true })) { - return true; - } else { - return false; + public async getRole(this: DocumentType, guildID: string, timestamp?: number): Promise{ + if (!timestamp) { + timestamp = Date.now(); + } + // Get Session(s) at Timestamp + const sessions = await SessionModel.find({ guild: guildID, user: (this._id as string), started_at: { $lte: timestamp.toString() }, $or: [{ ended_at: { $exists: false } }, { ended_at: { $gte: timestamp.toString() } }] }); + // We assume that there is at most One Session per guild at a time + if (!sessions.length) { + return null; + } else { + return sessions[0].role; + } } -}); - -UserSchema.method("getSessions", async function () { - return await SessionSchema.find({ user: (this._id as string) }); -}); - -UserSchema.method("getActiveSessions", async function () { - return await SessionSchema.find({ user: (this._id as string), active: true }); -}); - - -UserSchema.method("getRole", async function (guildID: string, timestamp?: number) { - if (!timestamp) { - timestamp = Date.now(); - } - // Get Session(s) at Timestamp - const sessions = await SessionSchema.find({ guild: guildID, user: (this._id as string), started_at: { $lte: timestamp.toString() }, $or: [{ ended_at: { $exists: false } }, { ended_at: { $gte: timestamp.toString() } }] }); - // We assume that there is at most One Session per guild at a time - if (!sessions.length) { - return null; - } else { - return sessions[0].role; - } -}); +} -// Default export -export default mongoose.model("Users", UserSchema); \ No newline at end of file +export const UserModel = getModelForClass(User, { + schemaOptions: { + autoCreate: true, + }, +}); \ No newline at end of file diff --git a/src/models/voice_channel_spawner.ts b/src/models/voice_channel_spawner.ts index 495770f..aa7e766 100644 --- a/src/models/voice_channel_spawner.ts +++ b/src/models/voice_channel_spawner.ts @@ -1,96 +1,51 @@ -import mongoose from "mongoose"; -import PermissionOverwriteDataSchema, { PermissionOverwriteData, PermissionOverwriteDataDocument } from "./permission_overwrite_data"; +import { PermissionOverwriteData } from "./permission_overwrite_data"; +import {prop,mongoose, getModelForClass, ArraySubDocumentType} from "@typegoose/typegoose"; -export interface VoiceChannelSpawner { +export class VoiceChannelSpawner { /** * overwrite the VC Owner for the Bot */ - owner?: string, + @prop() + owner?: string; /** * The Roles that can moderate this channel */ - supervisor_roles: string[], + @prop({required: true, type: () => [String], default: []}) + supervisor_roles!: mongoose.Types.Array; /** * The Channel Permissions */ - permission_overwrites: PermissionOverwriteData[], + @prop({required: true, type: () => [PermissionOverwriteData], default: []}) + permission_overwrites!: mongoose.Types.DocumentArray>; /** * Limit the amount of Users that can join the channel */ - max_users?: number, + @prop() + max_users?: number; /** - * The Name of the Channel, use ${owner} and so on to create dynamic channel names + * The Name of the Channel; use ${owner} and so on to create dynamic channel names */ - name?: string, + @prop({required: true, default: "${owner_name}'s VC"}) + name!: string; /** * Whether the Channel should initially be locked or not */ - lock_initially?: boolean, + @prop({default: false}) + lock_initially?: boolean; /** * Whether the Channel should initially be hidden or not */ - hide_initially?: boolean, + @prop({default: false}) + hide_initially?: boolean; /** * The Category Channel ID */ - parent?: string, + @prop() + parent?: string; } -const VoiceChannelSpawnerSchema = new mongoose.Schema({ - owner: { - type: String, - required: false, +export const VoiceChannelSpawnerModel = getModelForClass(VoiceChannelSpawner, { + schemaOptions: { + autoCreate: false, }, - supervisor_roles: [{ - type: String, - required: true, - default: [], - }], - permission_overwrites: [{ - type: PermissionOverwriteDataSchema, - required: true, - }], - max_users: { - type: Number, - required: false, - }, - name: { - type: String, - required: true, - default: "${owner_name}'s VC", - }, - lock_initially: { - type: Boolean, - required: false, - default: false, - }, - hide_initially: { - type: Boolean, - required: false, - default: false, - }, - parent: { - type: String, - required: false, - }, -}); - -/** - * Used for creating Voice Channels - */ -export interface VoiceChannelCreateOptions extends VoiceChannelSpawner { - name: string, -} - -export interface VoiceChannelSpawnerDocument extends VoiceChannelSpawner, mongoose.Document { - supervisor_roles: mongoose.Types.Array, - permission_overwrites: mongoose.Types.DocumentArray, -} - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface VoiceChannelSpawnerModel extends mongoose.Model { - -} - -// Default export -export default VoiceChannelSpawnerSchema; \ No newline at end of file +}); \ No newline at end of file diff --git a/src/models/voice_channels.ts b/src/models/voice_channels.ts index 72b5b11..621fd38 100644 --- a/src/models/voice_channels.ts +++ b/src/models/voice_channels.ts @@ -1,179 +1,126 @@ +import { SubDocumentType, getModelForClass } from "@typegoose/typegoose"; /* eslint-disable @typescript-eslint/no-empty-interface */ -import mongoose from "mongoose"; -import { Bot } from "../bot"; import { Channel } from "./text_channels"; -import VoiceChannelSpawnerSchema, { VoiceChannelSpawner, VoiceChannelSpawnerDocument } from "./voice_channel_spawner"; +import { VoiceChannelSpawner } from "./voice_channel_spawner"; import * as djs from "discord.js"; +import { DocumentType, Ref, prop, mongoose } from "@typegoose/typegoose"; +import { Queue } from "./queues"; -export interface VoiceChannel extends Channel { - /** - * The Channel ID - */ - _id: string, +export class VoiceChannel implements Channel { + @prop({ required: true }) + _id!: string; + @prop({ required: true, type: Number, enum: [0, 1, 2, 3, 4, 5, 6, 7] }) + channel_type!: djs.ChannelType; + @prop({ required: true }) + managed!: boolean; + @prop() + category?: string | undefined; + @prop() + owner?: string | undefined; /** * If the channel is an AFK-Hell (constantly plays a Song) */ - afkhell?: boolean, + @prop({ default: false }) + afkhell?: boolean; /** * The Song Link for AFK Hell */ - song_link?: string, + @prop() + song_link?: string; /** * If the voice Channel is locked to a specific user group (used to keep track of lock icon) */ - locked: boolean, + @prop({ required: true, default: false }) + locked!: boolean; /** * The Permitted Users/Roles that can enter this channel */ - permitted: string[], + @prop({ required: true, type: String, default: [] }) + permitted!: mongoose.Types.Array; /** * Makes the Channel a spawner Channel which creates a new channel for every joined member */ - spawner?: VoiceChannelSpawner, + @prop({ required: false, type: () => VoiceChannelSpawner }) + spawner?: SubDocumentType; /** * The Channel Prefix */ - prefix?: string, + @prop() + prefix?: string; /** * A Queue that is entered with joining this Channel */ - queue?: mongoose.Types.ObjectId, + @prop({ ref: () => Queue }) + queue?: Ref; /** * If the VC is a Temporary Voice Channel */ - temporary?: boolean, + @prop({ default: false }) + temporary?: boolean; /** * The Channel Supervisor Roles/ User IDs */ - supervisors?: string[], -} - -const VoiceChannelSchema = new mongoose.Schema({ - _id: { - type: String, - required: true, - }, - channel_type: { - type: Number, - enum: [0, 1, 2, 3, 4, 5, 6, 7], - required: true, - }, - managed: { - type: Boolean, - required: true, - }, - owner: { - type: String, - required: false, - }, - prefix: { - type: String, - required: false, - }, - category: { - type: String, - required: false, - }, - afkhell: { - type: Boolean, - required: false, - }, - song_link: { - type: String, - required: false, - }, - locked: { - type: Boolean, - required: true, - default: false, - }, - permitted: [{ - type: String, - required: true, - default: [], - }], - spawner: { - type: VoiceChannelSpawnerSchema, - required: false, - }, - queue: { - type: mongoose.Types.ObjectId, - required: false, - }, - temporary: { - type: Boolean, - required: false, - default: false, - }, - supervisors: [{ - type: String, - required: false, - default: [], - }], -}); + @prop({ type: String, default: [] }) + supervisors?: mongoose.Types.Array; -export interface VoiceChannelDocument extends VoiceChannel, Omit { - permitted: mongoose.Types.Array, - spawner?: VoiceChannelSpawnerDocument, - supervisors?: mongoose.Types.Array, /** * Locks the Voice Channel + * @param channel The Channel to lock + * @param roleId The @everyone Role ID */ - lock(channel: djs.VoiceChannel, roleId?: string | undefined): Promise; + public async lock(this: DocumentType, channel: djs.VoiceChannel, roleId?: string | undefined): Promise { + this.locked = true; + await this.$parent()?.save(); + await this.syncPermissions(channel, roleId); + } + /** * Unlocks the Voice Channel + * @param channel The Channel to unlock + * @param roleId The @everyone Role ID */ - unlock(channel: djs.VoiceChannel, roleId?: string | undefined): Promise; + public async unlock(this: DocumentType, channel: djs.VoiceChannel, roleId?: string | undefined): Promise { + this.locked = false; + await this.$parent()?.save(); + await this.syncPermissions(channel, roleId); + } + /** * Locks or Unlocks the Voice Channel (opposite State). + * @param channel The Channel to toggle + * @param roleId The @everyone Role ID */ - toggleLock(channel: djs.VoiceChannel, roleId?: string | undefined): Promise; + public async toggleLock(this: DocumentType, channel: djs.VoiceChannel, roleId?: string | undefined): Promise { + this.locked ? await this.lock(channel, roleId) : await this.unlock(channel, roleId); + await this.$parent()?.save(); + } + /** * Sync The VC-Permissions with the Database * @param channel The Channel to Sync - * @param roleId The ROle ID + * @param roleId The @everyone Role ID * @returns true, if changes occured */ - syncPermissions(channel: djs.VoiceChannel, roleId?: string | undefined, lockOverwrite?: boolean): Promise; -} - -export interface VoiceChannelModel extends mongoose.Model { - -} - -VoiceChannelSchema.method("syncPermissions", async function (channel: djs.VoiceChannel, roleId?: djs.Snowflake, lockOverwrite?: boolean) { - lockOverwrite = lockOverwrite ?? this.locked; - const actual_permissions = channel.permissionOverwrites.cache.get(roleId ?? channel.guild.roles.everyone.id); - if (!actual_permissions - || (lockOverwrite && !(actual_permissions.allow.has("ViewChannel") && actual_permissions.deny.has("Connect"))) - || (!lockOverwrite && !(actual_permissions.allow.has("ViewChannel") && actual_permissions.allow.has("Connect"))) - || (!this.queue && !actual_permissions.allow.has("Speak")) - || (this.queue && !actual_permissions.deny.has("Speak")) - ) { - // Update roles - await channel.permissionOverwrites.edit(roleId ?? channel.guild.roles.everyone.id, { "ViewChannel": true, "Connect": !lockOverwrite, "Speak": !this.queue }); - return true; - } else { - return false; + public async syncPermissions(this: DocumentType, channel: djs.VoiceChannel, roleId?: string | undefined, lockOverwrite?: boolean): Promise { + lockOverwrite = lockOverwrite ?? this.locked; + const actual_permissions = channel.permissionOverwrites.cache.get(roleId ?? channel.guild.roles.everyone.id); + if (!actual_permissions + || (lockOverwrite && !(actual_permissions.allow.has("ViewChannel") && actual_permissions.deny.has("Connect"))) + || (!lockOverwrite && !(actual_permissions.allow.has("ViewChannel") && actual_permissions.allow.has("Connect"))) + || (!this.queue && !actual_permissions.allow.has("Speak")) + || (this.queue && !actual_permissions.deny.has("Speak")) + ) { + // Update roles + await channel.permissionOverwrites.edit(roleId ?? channel.guild.roles.everyone.id, { "ViewChannel": true, "Connect": !lockOverwrite, "Speak": !this.queue }); + return true; + } else { + return false; + } } -}); - -VoiceChannelSchema.method("lock", async function (channel: djs.VoiceChannel, roleId?: djs.Snowflake) { - this.locked = true; - await this.$parent()?.save(); - await this.syncPermissions(channel, roleId); -}); - -VoiceChannelSchema.method("unlock", async function (channel: djs.VoiceChannel, roleId?: djs.Snowflake) { - this.locked = false; - await this.$parent()?.save(); - await this.syncPermissions(channel, roleId); -}); - -VoiceChannelSchema.method("toggleLock", async function (channel: djs.VoiceChannel, roleId?: djs.Snowflake) { - this.locked ? await this.lock(channel, roleId) : await this.unlock(channel, roleId); - await this.$parent()?.save(); -}); +} -// Default export -export default VoiceChannelSchema; \ No newline at end of file +export const VoiceChannelModel = getModelForClass(VoiceChannel, { + schemaOptions: { + autoCreate: false, + }, +}); \ No newline at end of file diff --git a/src/models/week_timestamp.ts b/src/models/week_timestamp.ts index 7e2e6a4..f591b65 100644 --- a/src/models/week_timestamp.ts +++ b/src/models/week_timestamp.ts @@ -1,34 +1,153 @@ -import mongoose, { Mongoose } from "mongoose"; -import { Weekday, WeekTimestamp } from "../utils/general"; - +import { getModelForClass, prop } from "@typegoose/typegoose"; +export enum Weekday { + /** + * Sonntag + */ + SUNDAY = 0, + /** + * Montag + */ + MONDAY = 1, + /** + * Dienstag + */ + TUESDAY = 2, + /** + * Mittwoch + */ + WEDNESDAY = 3, + /** + * Donnerstag + */ + THURSDAY = 4, + /** + * Freitag + */ + FRIDAY = 5, + /** + * Samstag + */ + SATURDAY = 6 +} /** - * A Schema of a Queue Entry + * A Timestamp of the queue */ -const WeekTimestampSchema = new mongoose.Schema({ - weekday: { - type: mongoose.Schema.Types.Number, - enum: [0, 1, 2, 3, 4, 5, 6], - required: true, - }, - hour: { - type: mongoose.Schema.Types.Number, - required: true, - }, - minute: { - type: mongoose.Schema.Types.Number, - required: true, - }, -}); -export interface WeekTimestampDocument extends WeekTimestamp, Omit,"equals"> { - // List getters or non model methods here -} +export class WeekTimestamp { + + /** + * The Day of the Week + */ + @prop({ required: true,enum: Weekday }) + weekday!: Weekday; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WeekTimestampModel extends mongoose.Model { - // List Model methods here + /** + * The Hour of the Day + */ + @prop({ required: true }) + hour!: number; + /** + * The Minute of the Hour + */ + @prop({ required: true }) + minute!: number; + + /** + * Creates a new WeekTimestamp + * @param weekday The current day of the week + * @param hour The current hour of the day + * @param minute The current minute of the hour + */ + constructor( + /** + * The Day of the Week + */ + weekday: Weekday, + /** + * The Hour of the Day + */ + hour: number, + /** + * The Minute of the Hour + */ + minute: number, + ) { + this.weekday = weekday; + this.hour = hour; + this.minute = minute; + } + /** + * returns the weektime in ms + * @returns The WeekTime in ms + */ + public getTime(): number { + return this.minute * 1000 * 60 + this.hour * 1000 * 60 * 60 + this.weekday * 1000 * 60 * 60 * 24; + } + + /** + * Returns a Relative Weekdate + * @param date The Date to convert + * @returns The created WeekTimestamp + */ + public static fromDate(date: Date) { + return new WeekTimestamp(date.getDay(), date.getHours(), date.getMinutes()); + } + + /** + * Returns a Relative Weekdate from a given Time in ms + * @param number The Time elapsed since Sunday 00:00 in ms + * @returns The created WeekTimestamp + */ + public static fromNumber(number: number) { + return new WeekTimestamp(Math.floor(number / 1000 / 60 / 60 / 24), Math.floor(number / 1000 / 60 / 60) % 24, Math.floor(number / 1000 / 60) % 60); + } + + /** + * Returns a string representation of the WeekTimestamp. + * @param padWeekday Whether to pad the weekday to the length of the longest weekday name + * @returns The WeekTimestamp as String + * @example + * ```ts + * const weekTimestamp = new WeekTimestamp(Weekday.MONDAY, 12, 0); + * console.log(weekTimestamp.toString()); // "MONDAY 12:00" + * ``` + */ + public toString(padWeekday = false): string { + const weekdayString = padWeekday ? Weekday[this.weekday].padEnd(Object.keys(Weekday).reduce((a, b) => a.length > b.length ? a : b).length, " ") : Weekday[this.weekday]; + return `${weekdayString} ${String(this.hour).padStart(2, "0")}:${String(this.minute).padStart(2, "0")}`; + } + + /** + * Creates a new WeekTimestamp from a given string + * @param string The string to parse + * @returns The created WeekTimestamp + * @throws Throws an Error if the string is not a valid WeekTimestamp + * @example + * ```ts + * const weekTimestamp = WeekTimestamp.fromString("Monday 12:00"); + * ``` + */ + public static fromString(str: string) { + const regex = /^(?\d+) (?\d+):(?\d+)$/; + const match = regex.exec(str); + if (!match) { + throw new Error(`Invalid WeekTimestamp String: ${str}`); + } + return new WeekTimestamp( + Weekday[match.groups!.weekday.toUpperCase() as keyof typeof Weekday], + +match.groups!.hour, + +match.groups!.minute, + ); + } + + equals(other: WeekTimestamp): boolean { + return this.weekday === other.weekday && this.hour === other.hour && this.minute === other.minute; + } } -// Default export -export default WeekTimestampSchema; \ No newline at end of file +export const WeekTimestampModel = getModelForClass(WeekTimestamp, { + schemaOptions: { + autoCreate: false, + }, +}); \ No newline at end of file diff --git a/src/utils/general.ts b/src/utils/general.ts index 28d64db..13b4cd5 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -1,14 +1,15 @@ import { ConfigHandler } from "./../handlers/configHandler"; -import { DBRoleDocument, InternalRoles, RoleScopes } from "./../models/bot_roles"; +import { DBRole, InternalRoles, RoleScopes } from "./../models/bot_roles"; import ChannelType, { CommandInteraction, Guild, GuildMember, GuildMemberResolvable, GuildResolvable, Interaction, Message, RoleResolvable, UserResolvable } from "discord.js"; import moment from "moment"; import { Command, StringReplacements } from "../../typings"; import { promisify } from "util"; -import GuildSchema from "../models/guilds"; +import {GuildModel} from "../models/guilds"; import glob from "glob"; import { Bot } from "../bot"; -import UserSchema from "../models/users"; +import {UserModel} from "../models/users"; import * as cryptojs from "crypto-js"; +import { DocumentType } from "@typegoose/typegoose"; /** * Checks if a given Variable is an array[] with at least a length of one or not @@ -16,6 +17,7 @@ import * as cryptojs from "crypto-js"; * @param variable the Variable to check * @returns */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export const isArraywithContent = (variable: any) => Array.isArray(variable) && (!!variable.length) && (variable.length > 0); /** @@ -160,7 +162,7 @@ export async function hasPermission(client: Bot, mentionable: UserResolvable | R // TODO: Permissions for Global Commands return command.defaultPermission || roleoruser?.id === client.config.get("ownerID"); } - const guildData = (await GuildSchema.findById(g.id))!; + const guildData = (await GuildModel.findById(g.id))!; const commandSettings = await guildData.guild_settings.getCommandByInternalName(command.name); const permission_overwrite = commandSettings?.permissions.some(x => x.id === roleoruser?.id && x.permission) ?? false; const role_permission_overwrite = (roleoruser instanceof GuildMember) && [...roleoruser.roles.cache.values()].some(r => commandSettings?.permissions.some(x => x.id === r.id && x.permission)); @@ -240,7 +242,7 @@ export async function verifyUser(replyable: Message | CommandInteraction, tokens console.log(`Failed Verifying User ${author.tag} with message: This should not happen... Please Contact the owner of the Bot. (Guild not found)`); return await client.utils.embeds.SimpleEmbed(replyable, { title: "Server Not Found", text: "This should not happen... Please Contact the owner of the Bot.", empheral: true }); } - const dbGuild = await GuildSchema.findById(guild.id); + const dbGuild = await GuildModel.findById(guild.id); if (!dbGuild) { console.log(`Failed Verifying User ${author.tag} with message: This should not happen... Please Contact the owner of the Bot.`); return await client.utils.embeds.SimpleEmbed(replyable, { title: "Server Not Found", text: "This should not happen... Please Contact the owner of the Bot. (Database Server not found)", empheral: true }); @@ -253,17 +255,17 @@ export async function verifyUser(replyable: Message | CommandInteraction, tokens return await client.utils.embeds.SimpleEmbed(replyable, { title: "Verification System Error", text: "You are not a Member of the Guild.", empheral: true }); } - let databaseUser = await UserSchema.findById(member.id); + let databaseUser = await UserModel.findById(member.id); if (!databaseUser) { - databaseUser = new UserSchema({ _id: member.id }); + databaseUser = new UserModel({ _id: member.id }); await databaseUser.save(); } databaseUser.tu_id = tu_id; databaseUser.moodle_id = moodle_id; - const dbTokenRoles = [] as DBRoleDocument[]; + const dbTokenRoles = [] as DocumentType[]; // find roles internal_role_names.forEach(async x => { - const token_role = dbGuild.guild_settings.roles.find(r => r.internal_name.toLowerCase() === x.toLowerCase()); + const token_role = dbGuild.guild_settings.roles?.find(r => r.internal_name.toLowerCase() === x.toLowerCase()); if (!token_role) { console.log(`Failed Verifying User ${author.tag} with message: Role ${x} not found.`); return await client.utils.embeds.SimpleEmbed(replyable, { title: "Verification System Error", text: `Role ${x} not found.`, empheral: true }); @@ -275,7 +277,7 @@ export async function verifyUser(replyable: Message | CommandInteraction, tokens try { await databaseUser.save(); } catch (error) { - if ((error as any).message?.includes("duplicate key")) { + if ((error as {message:string}).message?.includes("duplicate key")) { console.log(`User ${member.displayName} tried to valid but already used token with TU-ID: "${tu_id}", Moodle-ID: "${moodle_id}"`); return await client.utils.embeds.SimpleEmbed(replyable, { title: "Verification System Error", text: "You can only Link one Discord Account.", empheral: true }); } else { @@ -286,11 +288,11 @@ export async function verifyUser(replyable: Message | CommandInteraction, tokens console.log(`Linked ${member.displayName} to TU-ID: "${tu_id}", Moodle-ID: "${moodle_id}"`); // faulty roles - const faulty_roles: DBRoleDocument[] = []; + const faulty_roles: DocumentType[] = []; // existing roles - const existing_roles: DBRoleDocument[] = []; + const existing_roles: DocumentType[] = []; // new roles - const new_roles: DBRoleDocument[] = []; + const new_roles: DocumentType[] = []; // Give Roles const guildRoles = await member.guild.roles.fetch(); for (const role of dbTokenRoles) { @@ -380,131 +382,4 @@ export enum QueueStayOptions { * Annotates user already left */ LEFT, -} - -export enum Weekday { - /** - * Sonntag - */ - SUNDAY = 0, - /** - * Montag - */ - MONDAY = 1, - /** - * Dienstag - */ - TUESDAY = 2, - /** - * Mittwoch - */ - WEDNESDAY = 3, - /** - * Donnerstag - */ - THURSDAY = 4, - /** - * Freitag - */ - FRIDAY = 5, - /** - * Samstag - */ - SATURDAY = 6, -} - -/** - * A Timestamp of the queue - */ -export class WeekTimestamp { - - /** - * Creates a new WeekTimestamp - * @param weekday The current day of the week - * @param hour The current hour of the day - * @param minute The current minute of the hour - */ - constructor( - /** - * The Day of the Week - */ - public weekday: Weekday, - /** - * The Hour of the Day - */ - public hour: number, - /** - * The Minute of the Hour - */ - public minute: number, - ) { - - } - /** - * returns the weektime in ms - * @returns The WeekTime in ms - */ - public getTime(): number { - return this.minute * 1000 * 60 + this.hour * 1000 * 60 * 60 + this.weekday * 1000 * 60 * 60 * 24; - } - - /** - * Returns a Relative Weekdate - * @param date The Date to convert - * @returns The created WeekTimestamp - */ - public static fromDate(date: Date) { - return new WeekTimestamp(date.getDay(), date.getHours(), date.getMinutes()); - } - - /** - * Returns a Relative Weekdate from a given Time in ms - * @param number The Time elapsed since Sunday 00:00 in ms - * @returns The created WeekTimestamp - */ - public static fromNumber(number: number) { - return new WeekTimestamp(Math.floor(number / 1000 / 60 / 60 / 24), Math.floor(number / 1000 / 60 / 60) % 24, Math.floor(number / 1000 / 60) % 60); - } - - /** - * Returns a string representation of the WeekTimestamp. - * @param padWeekday Whether to pad the weekday to the length of the longest weekday name - * @returns The WeekTimestamp as String - * @example - * ```ts - * const weekTimestamp = new WeekTimestamp(Weekday.MONDAY, 12, 0); - * console.log(weekTimestamp.toString()); // "MONDAY 12:00" - * ``` - */ - public toString(padWeekday = false): string { - const weekdayString = padWeekday ? Weekday[this.weekday].padEnd(Object.keys(Weekday).reduce((a, b) => a.length > b.length ? a : b).length, " ") : Weekday[this.weekday]; - return `${weekdayString} ${String(this.hour).padStart(2, "0")}:${String(this.minute).padStart(2, "0")}`; - } - - /** - * Creates a new WeekTimestamp from a given string - * @param string The string to parse - * @returns The created WeekTimestamp - * @throws Throws an Error if the string is not a valid WeekTimestamp - * @example - * ```ts - * const weekTimestamp = WeekTimestamp.fromString("Monday 12:00"); - * ``` - */ - public static fromString(str: string) { - const regex = /^(?\d+) (?\d+):(?\d+)$/; - const match = regex.exec(str); - if (!match) { - throw new Error(`Invalid WeekTimestamp String: ${str}`); - } - return new WeekTimestamp( - Weekday[match.groups!.weekday.toUpperCase() as keyof typeof Weekday], - +match.groups!.hour, - +match.groups!.minute, - ); - } - - equals(other: WeekTimestamp): boolean { - return this.weekday === other.weekday && this.hour === other.hour && this.minute === other.minute; - } } \ No newline at end of file diff --git a/src/utils/voice.ts b/src/utils/voice.ts index cb18355..ce8d066 100644 --- a/src/utils/voice.ts +++ b/src/utils/voice.ts @@ -1,15 +1,17 @@ +import { FilterOutFunctionKeys } from "@typegoose/typegoose/lib/types"; import { ChannelType, Guild, GuildMember, GuildPremiumTier, OverwriteData } from "discord.js"; import { Bot } from "../bot"; -import GuildSchema from "../models/guilds"; +import {GuildModel} from "../models/guilds"; import { VoiceChannel } from "../models/voice_channels"; -import { VoiceChannelCreateOptions, VoiceChannelSpawner } from "../models/voice_channel_spawner"; +import { VoiceChannelSpawner } from "../models/voice_channel_spawner"; +import { mongoose } from "@typegoose/typegoose"; /** * Creates a managed Voice Channel Based on a Voice Channel Spawner * @param guild The Guild to create the Channel in * @param options The Options from the Voice Channel Spawner */ -export async function createManagedVC(guild: Guild, options: VoiceChannelCreateOptions) { +export async function createManagedVC(guild: Guild, options: FilterOutFunctionKeys) { // Channel Permissions const permoverrides: OverwriteData[] = options.permission_overwrites; @@ -68,7 +70,7 @@ export async function createManagedVC(guild: Guild, options: VoiceChannelCreateO }); // Create Database Entry - const guildData = (await GuildSchema.findById(guild.id))!; + const guildData = (await GuildModel.findById(guild.id))!; guildData.voice_channels.push({ _id: createdVC.id, channel_type: 2, @@ -77,11 +79,11 @@ export async function createManagedVC(guild: Guild, options: VoiceChannelCreateO managed: true, // blacklist_user_groups: [], // whitelist_user_groups: [], - permitted: [], + permitted: new mongoose.Types.Array(), afkhell: false, category: options.parent, temporary: true, - } as VoiceChannel); + } as FilterOutFunctionKeys); await guildData.save(); return createdVC; } @@ -114,5 +116,5 @@ export async function createTempVC(member: GuildMember, spawner: VoiceChannelSpa spawner.name = name; spawner.owner = member.id; - return await createManagedVC(member.guild, spawner as VoiceChannelCreateOptions); + return await createManagedVC(member.guild, spawner); } \ No newline at end of file