diff --git a/prisma/migrations/20221021150615_shoproles_model/migration.sql b/prisma/migrations/20221021150615_shoproles_model/migration.sql new file mode 100644 index 0000000..1859cc1 --- /dev/null +++ b/prisma/migrations/20221021150615_shoproles_model/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "GuildShopRoles" ( + "guildId" TEXT NOT NULL, + "channelId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "pricePerHour" INTEGER NOT NULL DEFAULT 5, + "lastPayed" DATETIME NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "GuildShopRoles_guildId_channelId_key" ON "GuildShopRoles"("guildId", "channelId"); diff --git a/prisma/migrations/20221021151136_shoproles_model_correction/migration.sql b/prisma/migrations/20221021151136_shoproles_model_correction/migration.sql new file mode 100644 index 0000000..5631656 --- /dev/null +++ b/prisma/migrations/20221021151136_shoproles_model_correction/migration.sql @@ -0,0 +1,25 @@ +/* + Warnings: + + - You are about to drop the column `channelId` on the `GuildShopRoles` table. All the data in the column will be lost. + +*/ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_GuildShopRoles" ( + "guildId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "pricePerHour" INTEGER NOT NULL DEFAULT 5, + "lastPayed" DATETIME NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_GuildShopRoles" ("createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId") SELECT "createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId" FROM "GuildShopRoles"; +DROP TABLE "GuildShopRoles"; +ALTER TABLE "new_GuildShopRoles" RENAME TO "GuildShopRoles"; +CREATE UNIQUE INDEX "GuildShopRoles_guildId_userId_roleId_key" ON "GuildShopRoles"("guildId", "userId", "roleId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/migrations/20221021151337_shoproles_model_correction/migration.sql b/prisma/migrations/20221021151337_shoproles_model_correction/migration.sql new file mode 100644 index 0000000..8a3fe75 --- /dev/null +++ b/prisma/migrations/20221021151337_shoproles_model_correction/migration.sql @@ -0,0 +1,20 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_GuildShopRoles" ( + "guildId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "pricePerHour" INTEGER NOT NULL DEFAULT 5, + "lastPayed" DATETIME NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildShopRoles_guildId_userId_fkey" FOREIGN KEY ("guildId", "userId") REFERENCES "GuildMember" ("guildId", "userId") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_GuildShopRoles" ("createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId") SELECT "createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId" FROM "GuildShopRoles"; +DROP TABLE "GuildShopRoles"; +ALTER TABLE "new_GuildShopRoles" RENAME TO "GuildShopRoles"; +CREATE UNIQUE INDEX "GuildShopRoles_guildId_userId_roleId_key" ON "GuildShopRoles"("guildId", "userId", "roleId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/migrations/20221021153546_shoproles_model_correction/migration.sql b/prisma/migrations/20221021153546_shoproles_model_correction/migration.sql new file mode 100644 index 0000000..88d1d66 --- /dev/null +++ b/prisma/migrations/20221021153546_shoproles_model_correction/migration.sql @@ -0,0 +1,20 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_GuildShopRoles" ( + "guildId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "pricePerHour" INTEGER NOT NULL DEFAULT 5, + "lastPayed" DATETIME NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "GuildShopRoles_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildShopRoles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildShopRoles_userId_guildId_fkey" FOREIGN KEY ("userId", "guildId") REFERENCES "GuildMember" ("userId", "guildId") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_GuildShopRoles" ("createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId") SELECT "createdAt", "guildId", "lastPayed", "pricePerHour", "roleId", "updatedAt", "userId" FROM "GuildShopRoles"; +DROP TABLE "GuildShopRoles"; +ALTER TABLE "new_GuildShopRoles" RENAME TO "GuildShopRoles"; +CREATE UNIQUE INDEX "GuildShopRoles_guildId_userId_roleId_key" ON "GuildShopRoles"("guildId", "userId", "roleId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml index 6fcf33d..e5e5c47 100644 --- a/prisma/migrations/migration_lock.toml +++ b/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually # It should be added in your version-control system (i.e. Git) -provider = "sqlite" +provider = "sqlite" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5418d5b..f7923d4 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -58,8 +58,9 @@ model Guild { welcomeLeaveChannelId String? welcomeLeaveChannelMessage String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + GuildShopRoles GuildShopRoles[] } model User { @@ -72,8 +73,9 @@ model User { reputationsEarned Int @default(0) Cooldown Cooldown[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + GuildShopRoles GuildShopRoles[] } model GuildMember { @@ -89,8 +91,9 @@ model GuildMember { creditsEarned Int @default(0) pointsEarned Int @default(0) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + GuildShopRoles GuildShopRoles[] // Unique Identifier @@unique([userId, guildId]) @@ -122,3 +125,20 @@ model Cooldown { @@unique([guildId, userId, timeoutId]) } + +model GuildShopRoles { + guildId String + roleId String + userId String + pricePerHour Int @default(5) + lastPayed DateTime + + guild Guild @relation(fields: [guildId], references: [id]) + user User @relation(fields: [userId], references: [id]) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + member GuildMember? @relation(fields: [userId, guildId], references: [userId, guildId]) + + @@unique([guildId, userId, roleId]) +} diff --git a/src/commands/shop/modules/roles/index.ts b/src/commands/shop/modules/roles/index.ts index 0064d14..4d954cf 100644 --- a/src/commands/shop/modules/roles/index.ts +++ b/src/commands/shop/modules/roles/index.ts @@ -8,7 +8,7 @@ import { ChatInputCommandInteraction } from "discord.js"; import moduleBuy from "./modules/buy"; import moduleCancel from "./modules/cancel"; -import guildSchema from "../../../../models/guild"; +import prisma from "../../../../handlers/database"; export default { builder: (group: SlashCommandSubcommandGroupBuilder) => { @@ -26,13 +26,12 @@ export default { if (!interaction.guild) return; const { options, guild } = interaction; - const guildDB = await guildSchema?.findOne({ - guildId: guild?.id, + const getGuild = await prisma.guild.findUnique({ + where: { id: guild.id }, }); + if (!getGuild) throw new Error("Guild not found"); - if (guildDB === null) return; - - if (!guildDB.shop.roles.status) + if (!getGuild.shopRolesEnabled) throw new Error("This server has disabled shop roles."); if (options?.getSubcommand() === "buy") { diff --git a/src/commands/shop/modules/roles/modules/buy/index.ts b/src/commands/shop/modules/roles/modules/buy/index.ts index e1ca0b2..a6e3b0b 100644 --- a/src/commands/shop/modules/roles/modules/buy/index.ts +++ b/src/commands/shop/modules/roles/modules/buy/index.ts @@ -1,12 +1,22 @@ // Dependencies // Helpers import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import { ChatInputCommandInteraction } from "discord.js"; +import { + ChatInputCommandInteraction, + ColorResolvable, + EmbedBuilder, + GuildMemberRoleManager, +} from "discord.js"; import deferReply from "../../../../../../handlers/deferReply"; +import getEmbedData from "../../../../../../helpers/getEmbedData"; +import logger from "../../../../../../middlewares/logger"; // Configurations // import fetchUser from "../../../../../../helpers/userData"; // Models +import prisma from "../../../../../../handlers/database"; +import pluralize from "../../../../../../helpers/pluralize"; + // Function export default { builder: (command: SlashCommandSubcommandBuilder) => { @@ -29,65 +39,140 @@ export default { execute: async (interaction: ChatInputCommandInteraction) => { await deferReply(interaction, true); - // const { successColor, footerText, footerIcon } = await getEmbedConfig( - // interaction.guild - // ); - // const { options, guild, user, member } = interaction; - // const optionName = options?.getString("name"); - // const optionColor = options?.getString("color"); - // // If amount is null - // if (optionName === null) - // throw new Error("We could not read your requested name"); - // await guild?.roles - // .create({ - // name: optionName, - // color: optionColor as ColorResolvable, - // reason: `${user?.id} bought from shop`, - // }) - // .then(async (role) => { - // // Get guild object - // const guildDB = await guildSchema?.findOne({ - // guildId: guild?.id, - // }); - // const userDB = await fetchUser(user, guild); - // if (userDB === null) { - // return logger?.silly(`User is null`); - // } - // if (guildDB === null) { - // return logger?.silly(`Guild is null`); - // } - // if (guildDB.shop === null) { - // return logger?.silly(`Shop is null`); - // } - // const { pricePerHour } = guildDB.shop.roles; - // userDB.credits -= pricePerHour; - // await userDB?.save(); - // await shopRolesSchema?.create({ - // roleId: role?.id, - // userId: user?.id, - // guildId: guild?.id, - // pricePerHour, - // lastPayed: new Date(), - // }); - // await (member?.roles as GuildMemberRoleManager)?.add(role?.id); - // logger?.silly(`Role ${role?.name} was bought by ${user?.tag}`); - // const interactionEmbed = new EmbedBuilder() - // .setTitle("[:shopping_cart:] Buy") - // .setDescription( - // `You bought **${optionName}** for **${pluralize( - // pricePerHour, - // "credit" - // )}**.` - // ) - // .setTimestamp() - // .setColor(successColor) - // .setFooter({ text: footerText, iconURL: footerIcon }); - // return interaction?.editReply({ - // embeds: [interactionEmbed], - // }); - // }) - // .catch(() => { - // throw new Error("Failed creating role."); - // }); + const { successColor, footerText, footerIcon } = await getEmbedData( + interaction.guild + ); + const { options, guild, user, member } = interaction; + const optionName = options?.getString("name"); + const optionColor = options?.getString("color"); + // If amount is null + if (optionName === null) + throw new Error("We could not read your requested name"); + await guild?.roles + .create({ + name: optionName, + color: optionColor as ColorResolvable, + reason: `${user?.id} bought from shop`, + }) + .then(async (role) => { + const userId = "SNOWFLKAE"; + const guildId = "SNOWFLAKE"; + + const createGuildMember = await prisma.guildMember.upsert({ + where: { + userId_guildId: { + userId, + guildId, + }, + }, + update: {}, + create: { + user: { + connectOrCreate: { + create: { + id: userId, + }, + where: { + id: userId, + }, + }, + }, + guild: { + connectOrCreate: { + create: { + id: guildId, + }, + where: { + id: guildId, + }, + }, + }, + }, + include: { + user: true, + guild: true, + }, + }); + + logger.silly(createGuildMember); + + // Get guild object + const pricePerHour = createGuildMember.guild.shopRolesPricePerHour; + + const updateGuildMember = await prisma.guildMember.update({ + where: { + userId_guildId: { + userId, + guildId, + }, + }, + data: { + creditsEarned: { decrement: pricePerHour }, + }, + }); + + logger.silly(updateGuildMember); + + const createShopRole = await prisma.guildShopRoles.upsert({ + where: { + guildId_userId_roleId: { + guildId: guild.id, + userId: user.id, + roleId: role.id, + }, + }, + update: {}, + create: { + roleId: role.id, + lastPayed: new Date(), + user: { + connectOrCreate: { + create: { + id: user.id, + }, + where: { + id: user.id, + }, + }, + }, + guild: { + connectOrCreate: { + create: { + id: guild.id, + }, + where: { + id: guild.id, + }, + }, + }, + }, + include: { + user: true, + guild: true, + }, + }); + + logger.silly(createShopRole); + + await (member?.roles as GuildMemberRoleManager)?.add(role?.id); + logger?.silly(`Role ${role?.name} was bought by ${user?.tag}`); + const interactionEmbed = new EmbedBuilder() + .setTitle("[:shopping_cart:] Buy") + .setDescription( + `You bought **${optionName}** for **${pluralize( + pricePerHour, + "credit" + )}**.` + ) + .setTimestamp() + .setColor(successColor) + .setFooter({ text: footerText, iconURL: footerIcon }); + return interaction?.editReply({ + embeds: [interactionEmbed], + }); + }) + .catch(() => { + throw new Error("Failed creating role."); + }); }, }; diff --git a/src/commands/shop/modules/roles/modules/cancel/index.ts b/src/commands/shop/modules/roles/modules/cancel/index.ts index 4f842f9..8807ac4 100644 --- a/src/commands/shop/modules/roles/modules/cancel/index.ts +++ b/src/commands/shop/modules/roles/modules/cancel/index.ts @@ -1,11 +1,21 @@ // Dependencies // Helpers import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; -import { ChatInputCommandInteraction } from "discord.js"; +import { + ChatInputCommandInteraction, + EmbedBuilder, + GuildMemberRoleManager, +} from "discord.js"; // Configurations -// import fetchUser from "../../../../../../helpers/userData"; // Models import deferReply from "../../../../../../handlers/deferReply"; +import logger from "../../../../../../middlewares/logger"; +// Configurations +// Models + +import prisma from "../../../../../../handlers/database"; +import getEmbedData from "../../../../../../helpers/getEmbedData"; +import pluralize from "../../../../../../helpers/pluralize"; // Function export default { @@ -23,45 +33,95 @@ export default { execute: async (interaction: ChatInputCommandInteraction) => { await deferReply(interaction, true); - // const { successColor, footerText, footerIcon } = await getEmbedConfig( - // interaction.guild - // ); - // const { options, guild, user, member } = interaction; - // const optionRole = options.getRole("role"); - // if (optionRole === null) - // throw new Error("We could not read your requested role."); - // const roleExist = await shopRolesSchema?.findOne({ - // guildId: guild?.id, - // userId: user?.id, - // roleId: optionRole?.id, - // }); - // if (roleExist === null) return; - // await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id); - // await guild?.roles - // .delete(optionRole?.id, `${user?.id} canceled from shop`) - // .then(async () => { - // const userDB = await fetchUser(user, guild); - // if (userDB === null) { - // return logger?.silly(`User is null`); - // } - // await shopRolesSchema?.deleteOne({ - // roleId: optionRole?.id, - // userId: user?.id, - // guildId: guild?.id, - // }); - // const interactionEmbed = new EmbedBuilder() - // .setTitle("[:shopping_cart:] Cancel") - // .setDescription(`You have canceled ${optionRole.name}.`) - // .setTimestamp() - // .setColor(successColor) - // .addFields({ - // name: "Your balance", - // value: `${pluralize(userDB?.credits, "credit")}`, - // }) - // .setFooter({ text: footerText, iconURL: footerIcon }); - // return interaction?.editReply({ - // embeds: [interactionEmbed], - // }); - // }); + const { successColor, footerText, footerIcon } = await getEmbedData( + interaction.guild + ); + const { options, guild, user, member } = interaction; + const optionRole = options.getRole("role"); + if (optionRole === null) + throw new Error("We could not read your requested role."); + if (!guild) throw new Error("No guild specified"); + if (!user) throw new Error("No user specified"); + + const roleExist = await prisma.guildShopRoles.findUnique({ + where: { + guildId_userId_roleId: { + guildId: guild.id, + userId: user.id, + roleId: optionRole.id, + }, + }, + }); + if (roleExist === null) return; + await (member?.roles as GuildMemberRoleManager)?.remove(optionRole?.id); + await guild?.roles + .delete(optionRole?.id, `${user?.id} canceled from shop`) + .then(async () => { + const createGuildMember = await prisma.guildMember.upsert({ + where: { + userId_guildId: { + userId: user.id, + guildId: guild.id, + }, + }, + update: {}, + create: { + user: { + connectOrCreate: { + create: { + id: user.id, + }, + where: { + id: user.id, + }, + }, + }, + guild: { + connectOrCreate: { + create: { + id: guild.id, + }, + where: { + id: guild.id, + }, + }, + }, + }, + include: { + user: true, + guild: true, + }, + }); + + logger.silly(createGuildMember); + + if (!createGuildMember) throw new Error("Guild member not created"); + + const deleteShopRole = await prisma.guildShopRoles.delete({ + where: { + guildId_userId_roleId: { + guildId: guild?.id, + userId: user?.id, + roleId: optionRole?.id, + }, + }, + }); + + logger.silly(deleteShopRole); + + const interactionEmbed = new EmbedBuilder() + .setTitle("[:shopping_cart:] Cancel") + .setDescription(`You have canceled ${optionRole.name}.`) + .setTimestamp() + .setColor(successColor) + .addFields({ + name: "Your balance", + value: `${pluralize(createGuildMember.creditsEarned, "credit")}`, + }) + .setFooter({ text: footerText, iconURL: footerIcon }); + return interaction?.editReply({ + embeds: [interactionEmbed], + }); + }); }, }; diff --git a/src/events/guildMemberAdd/audits.ts b/src/events/guildMemberAdd/audits.ts index 697408d..0d61fa1 100644 --- a/src/events/guildMemberAdd/audits.ts +++ b/src/events/guildMemberAdd/audits.ts @@ -1,25 +1,28 @@ import { ChannelType, EmbedBuilder, GuildMember } from "discord.js"; +import prisma from "../../handlers/database"; import getEmbedConfig from "../../helpers/getEmbedData"; import logger from "../../middlewares/logger"; -import guildSchema from "../../models/guild"; export default { execute: async (member: GuildMember) => { const { client, guild } = member; - const guildData = await guildSchema.findOne({ guildId: member.guild.id }); - if (!guildData) { - throw new Error("Could not find guild"); - } - if (guildData.audits.status !== true) return; - if (!guildData.audits.channelId) { + const getGuild = await prisma.guild.findUnique({ + where: { id: member.guild.id }, + }); + if (!getGuild) throw new Error("Guild not found"); + + if (getGuild.auditsEnabled !== true) return; + if (!getGuild.auditsChannelId) { throw new Error("Channel not found"); } const embedConfig = await getEmbedConfig(guild); - const channel = client.channels.cache.get(guildData.audits.channelId); - if (channel?.type !== ChannelType.GuildText) { + const channel = client.channels.cache.get(getGuild.auditsChannelId); + + if (!channel) throw new Error("Channel not found"); + if (channel.type !== ChannelType.GuildText) { throw new Error("Channel must be a text channel"); } diff --git a/src/events/guildMemberRemove/audits.ts b/src/events/guildMemberRemove/audits.ts index d3967cd..ff4bb59 100644 --- a/src/events/guildMemberRemove/audits.ts +++ b/src/events/guildMemberRemove/audits.ts @@ -1,24 +1,25 @@ import { ChannelType, EmbedBuilder, GuildMember } from "discord.js"; +import prisma from "../../handlers/database"; import getEmbedConfig from "../../helpers/getEmbedData"; import logger from "../../middlewares/logger"; -import guildSchema from "../../models/guild"; export default { execute: async (member: GuildMember) => { const { client, guild } = member; - const guildData = await guildSchema.findOne({ guildId: member.guild.id }); - if (!guildData) { - throw new Error("Could not find guild"); - } - if (guildData.audits.status !== true) return; - if (!guildData.audits.channelId) { + const getGuild = await prisma.guild.findUnique({ + where: { id: member.guild.id }, + }); + if (!getGuild) throw new Error("Guild not found"); + + if (getGuild.auditsEnabled !== true) return; + if (!getGuild.auditsChannelId) { throw new Error("Channel not found"); } const embedConfig = await getEmbedConfig(guild); - const channel = client.channels.cache.get(guildData.audits.channelId); + const channel = client.channels.cache.get(getGuild.auditsChannelId); if (channel?.type !== ChannelType.GuildText) { throw new Error("Channel must be a text channel"); } diff --git a/src/events/interactionCreate/audits.ts b/src/events/interactionCreate/audits.ts index 3fc22f3..90a55e4 100644 --- a/src/events/interactionCreate/audits.ts +++ b/src/events/interactionCreate/audits.ts @@ -1,7 +1,7 @@ import { BaseInteraction, ChannelType, EmbedBuilder } from "discord.js"; +import prisma from "../../handlers/database"; import getEmbedConfig from "../../helpers/getEmbedData"; import logger from "../../middlewares/logger"; -import guildSchema from "../../models/guild"; export default { execute: async (interaction: BaseInteraction) => { @@ -9,22 +9,21 @@ export default { if (interaction.guild === null) return; + const getGuild = await prisma.guild.findUnique({ + where: { id: interaction.guild.id }, + }); + if (!getGuild) throw new Error("Guild not found"); + const { footerText, footerIcon, successColor } = await getEmbedConfig( interaction.guild ); - const guildData = await guildSchema.findOne({ - guildId: interaction.guild.id, - }); - const { client } = interaction; - if (guildData === null) return; + if (getGuild.auditsEnabled !== true) return; + if (!getGuild.auditsChannelId) return; - if (guildData.audits.status !== true) return; - if (!guildData.audits.channelId) return; - - const channel = client.channels.cache.get(`${guildData.audits.channelId}`); + const channel = client.channels.cache.get(`${getGuild.auditsChannelId}`); if (!channel) return; if (channel.type !== ChannelType.GuildText) return; diff --git a/src/events/messageDelete/audits.ts b/src/events/messageDelete/audits.ts index 9bf3ae9..56a8ac1 100644 --- a/src/events/messageDelete/audits.ts +++ b/src/events/messageDelete/audits.ts @@ -1,7 +1,7 @@ import { ChannelType, EmbedBuilder, Message } from "discord.js"; +import prisma from "../../handlers/database"; import getEmbedConfig from "../../helpers/getEmbedData"; import logger from "../../middlewares/logger"; -import guildSchema from "../../models/guild"; export default { execute: async (message: Message) => { @@ -13,18 +13,19 @@ export default { message.guild ); - const guildData = await guildSchema.findOne({ - guildId: message.guild.id, + const getGuild = await prisma.guild.findUnique({ + where: { id: message.guild.id }, }); + if (!getGuild) throw new Error("Guild not found"); const { client } = message; - if (guildData === null) return; + if (!getGuild) throw new Error("Guild not found"); - if (guildData.audits.status !== true) return; - if (!guildData.audits.channelId) return; + if (getGuild.auditsEnabled !== true) return; + if (!getGuild.auditsChannelId) return; - const channel = client.channels.cache.get(`${guildData.audits.channelId}`); + const channel = client.channels.cache.get(`${getGuild.auditsChannelId}`); if (!channel) return; if (channel.type !== ChannelType.GuildText) return; diff --git a/src/events/messageUpdate/audits.ts b/src/events/messageUpdate/audits.ts index ba35962..a498385 100644 --- a/src/events/messageUpdate/audits.ts +++ b/src/events/messageUpdate/audits.ts @@ -1,8 +1,8 @@ /* eslint-disable no-loops/no-loops */ import { ChannelType, EmbedBuilder, Message } from "discord.js"; +import prisma from "../../handlers/database"; import getEmbedConfig from "../../helpers/getEmbedData"; import logger from "../../middlewares/logger"; -import guildSchema from "../../models/guild"; export default { execute: async (oldMessage: Message, newMessage: Message) => { @@ -16,18 +16,17 @@ export default { newMessage.guild ); - const guildData = await guildSchema.findOne({ - guildId: oldMessage.guild.id, + const getGuild = await prisma.guild.findUnique({ + where: { id: oldMessage.guild.id }, }); + if (!getGuild) throw new Error("Guild not found"); const { client } = oldMessage; - if (guildData === null) return; + if (getGuild.auditsEnabled !== true) return; + if (!getGuild.auditsChannelId) return; - if (guildData.audits.status !== true) return; - if (!guildData.audits.channelId) return; - - const channel = client.channels.cache.get(`${guildData.audits.channelId}`); + const channel = client.channels.cache.get(`${getGuild.auditsChannelId}`); if (!channel) return; if (channel.type !== ChannelType.GuildText) return; diff --git a/src/models/guild.ts b/src/models/guild.ts deleted file mode 100644 index 140befa..0000000 --- a/src/models/guild.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { ColorResolvable } from "discord.js"; -import { model, Schema } from "mongoose"; - -interface IGuild { - guildId: string; - credits: { - status: boolean; - rate: number; - timeout: number; - workRate: number; - minimumLength: number; - workTimeout: number; - }; - embeds: { - successColor: ColorResolvable; - waitColor: ColorResolvable; - errorColor: ColorResolvable; - footerIcon: string; - footerText: string; - }; - shop: { roles: { status: boolean; pricePerHour: number } }; - points: { - status: boolean; - rate: number; - minimumLength: number; - timeout: number; - }; - welcome: { - status: boolean; - joinChannel: string; - leaveChannel: string; - joinChannelMessage: string; - leaveChannelMessage: string; - }; - audits: { status: boolean; channelId: string }; -} - -const guildSchema = new Schema( - { - guildId: { - type: String, - required: true, - unique: true, - index: true, - }, - credits: { - status: { - type: Boolean, - default: true, - }, - rate: { - type: Number, - default: 1, - }, - minimumLength: { - type: Number, - default: 5, - }, - timeout: { - type: Number, - default: 5, - }, - workRate: { - type: Number, - default: 15, - }, - workTimeout: { - type: Number, - default: 900, - }, - }, - embeds: { - successColor: { - type: String, - default: "#22bb33", - }, - waitColor: { - type: String, - default: "#f0ad4e", - }, - errorColor: { - type: String, - default: "#bb2124", - }, - footerText: { - type: String, - default: "https://github.com/ZynerOrg/xyter", - }, - footerIcon: { - type: String, - default: "https://github.com/ZynerOrg.png", - }, - }, - shop: { - roles: { - status: { - type: Boolean, - default: false, - }, - pricePerHour: { - type: Number, - default: 5, - }, - }, - }, - points: { - status: { - type: Boolean, - default: false, - }, - rate: { - type: Number, - default: 1, - }, - minimumLength: { - type: Number, - default: 5, - }, - timeout: { - type: Number, - default: 5, - }, - }, - welcome: { - status: { - type: Boolean, - default: false, - }, - joinChannel: { type: String }, - leaveChannel: { type: String }, - joinChannelMessage: { type: String }, - leaveChannelMessage: { type: String }, - }, - audits: { - status: { type: Boolean, default: false }, - channelId: { type: String }, - }, - }, - { timestamps: true } -); - -export default model("guild", guildSchema); diff --git a/src/models/shopRole.ts b/src/models/shopRole.ts deleted file mode 100644 index 8711760..0000000 --- a/src/models/shopRole.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Snowflake } from "discord.js"; -import { model, Schema } from "mongoose"; - -export interface IShopRole { - roleId: Snowflake; - userId: Snowflake; - guildId: Snowflake; - pricePerHour: number; - lastPayed: Date; -} - -const shopRoleSchema = new Schema( - { - roleId: { - type: String, - required: true, - unique: false, - index: true, - }, - userId: { - type: String, - required: true, - unique: false, - index: true, - }, - guildId: { - type: String, - required: true, - unique: false, - index: true, - }, - pricePerHour: { - type: Number, - required: true, - unique: false, - index: true, - default: 5, - }, - lastPayed: { - type: Date, - unique: false, - index: true, - }, - }, - { timestamps: true } -); - -export default model("shopRole", shopRoleSchema); diff --git a/src/models/timeout.ts b/src/models/timeout.ts deleted file mode 100644 index dc1ba97..0000000 --- a/src/models/timeout.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Snowflake } from "discord.js"; -import { model, Schema } from "mongoose"; - -export interface ITimeout { - userId: Snowflake; - guildId: Snowflake; - cooldown: number; - timeoutId: string; - createdAt: Date; - updatedAt: Date; -} - -const timeoutSchema = new Schema( - { - userId: { - type: String, - required: true, - unique: false, - index: true, - }, - guildId: { - type: String, - required: true, - unique: false, - index: true, - }, - cooldown: { - type: Number, - required: true, - unique: false, - index: true, - }, - timeoutId: { type: String }, - }, - { timestamps: true } -); - -export default model("timeout", timeoutSchema); diff --git a/src/models/user.ts b/src/models/user.ts deleted file mode 100644 index 8170ac6..0000000 --- a/src/models/user.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Snowflake } from "discord.js"; -import { model, Schema } from "mongoose"; - -export interface IUser { - guildId: Snowflake; - userId: Snowflake; - language: string; - reputation: number; - credits: number; - level: number; - points: number; - updatedAt: Date; - createdAt: Date; -} - -const userSchema = new Schema( - { - guildId: { - type: String, - required: true, - unique: false, - index: true, - }, - userId: { - type: String, - required: true, - unique: false, - index: true, - }, - language: { - type: String, - default: "en", - }, - reputation: { type: Number, default: 0 }, - credits: { type: Number, default: 0 }, - level: { type: Number, default: 0 }, - points: { type: Number, default: 0 }, - }, - { timestamps: true } -); - -export default model("user", userSchema); diff --git a/src/schedules/shop/modules/roles/components/dueForPayment.ts b/src/schedules/shop/modules/roles/components/dueForPayment.ts index f2b487d..f87c94c 100644 --- a/src/schedules/shop/modules/roles/components/dueForPayment.ts +++ b/src/schedules/shop/modules/roles/components/dueForPayment.ts @@ -1,9 +1,9 @@ import { Client } from "discord.js"; import logger from "../../../../../middlewares/logger"; -import { IShopRole } from "../../../../../interfaces/ShopRole"; +import { GuildShopRoles } from "@prisma/client"; -export const execute = async (_client: Client, role: IShopRole) => { +export const execute = async (_client: Client, role: GuildShopRoles) => { const { roleId } = role; logger.silly(`Shop role ${roleId} is not due for payment.`); diff --git a/src/schedules/shop/modules/roles/components/overDueForPayment.ts b/src/schedules/shop/modules/roles/components/overDueForPayment.ts index cbc5568..7020db7 100644 --- a/src/schedules/shop/modules/roles/components/overDueForPayment.ts +++ b/src/schedules/shop/modules/roles/components/overDueForPayment.ts @@ -1,22 +1,14 @@ import { Client } from "discord.js"; import logger from "../../../../../middlewares/logger"; -import { IShopRole } from "../../../../../interfaces/ShopRole"; -import guildSchema from "../../../../../models/guild"; -import shopRoleSchema from "../../../../../models/shopRole"; -import userSchema from "../../../../../models/user"; +import { GuildShopRoles } from "@prisma/client"; +import prisma from "../../../../../handlers/database"; // Execute the component -export const execute = async (client: Client, role: IShopRole) => { +export const execute = async (client: Client, role: GuildShopRoles) => { const { guildId, userId, roleId } = role; if (!userId) throw new Error("User ID not found for shop role."); - const guildData = await guildSchema.findOne({ guildId }); - if (!guildData) throw new Error("Guild not found."); - - const userData = await userSchema.findOne({ guildId, userId }); - if (!userData) throw new Error("User not found."); - const rGuild = client.guilds.cache.get(guildId); if (!rGuild) throw new Error("Guild not found."); @@ -28,26 +20,44 @@ export const execute = async (client: Client, role: IShopRole) => { logger.debug(`Shop role ${roleId} is due for payment.`); - const { pricePerHour } = guildData.shop.roles; + const getGuildMember = await prisma.guildMember.findUnique({ + where: { + userId_guildId: { + userId, + guildId, + }, + }, + include: { + user: true, + guild: true, + }, + }); - if (userData.credits < pricePerHour) { + logger.silly(getGuildMember); + + if (!getGuildMember) throw new Error("Could not find guild member."); + + const pricePerHour = getGuildMember.guild.shopRolesPricePerHour; + + if (getGuildMember.creditsEarned < pricePerHour) { await rMember.roles .remove(roleId) .then(async () => { - await shopRoleSchema - .deleteOne({ - userId, - roleId, - guildId, - }) - .then(() => { - logger.silly( - `Shop role document ${roleId} has been deleted from user ${userId}.` - ); - }) - .catch(() => { - throw new Error("Failed deleting shop role from user."); - }); + const deleteShopRole = await prisma.guildShopRoles.delete({ + where: { + guildId_userId_roleId: { + guildId, + userId, + roleId, + }, + }, + }); + + logger.silly(deleteShopRole); + + logger.silly( + `Shop role document ${roleId} has been deleted from user ${userId}.` + ); }) .catch(() => { throw new Error(`Failed removing role from user.`); @@ -56,25 +66,63 @@ export const execute = async (client: Client, role: IShopRole) => { throw new Error("User does not have enough credits."); } - userData.credits -= pricePerHour; - await userData - .save() - .then(async () => { - logger.silly(`User ${userId} has been updated.`); + const createGuildMember = await prisma.guildMember.upsert({ + where: { + userId_guildId: { + userId, + guildId, + }, + }, + update: { creditsEarned: { decrement: pricePerHour } }, + create: { + creditsEarned: -pricePerHour, + user: { + connectOrCreate: { + create: { + id: userId, + }, + where: { + id: userId, + }, + }, + }, + guild: { + connectOrCreate: { + create: { + id: guildId, + }, + where: { + id: guildId, + }, + }, + }, + }, + include: { + user: true, + guild: true, + }, + }); - role.lastPayed = new Date(); - await role - .save() - .then(() => { - logger.silly(`Shop role ${roleId} has been updated.`); - }) - .catch(() => { - throw new Error("Failed updating shop role."); - }); + logger.silly(createGuildMember); - logger.debug(`Shop role ${roleId} has been paid.`); - }) - .catch(() => { - throw new Error("Failed updating user."); - }); + logger.silly(`User ${userId} has been updated.`); + + const updateGuildShopRole = await prisma.guildShopRoles.update({ + where: { + guildId_userId_roleId: { + guildId, + userId, + roleId, + }, + }, + data: { + lastPayed: new Date(), + }, + }); + + logger.silly(updateGuildShopRole); + + logger.silly(`Shop role ${roleId} has been updated.`); + + logger.debug(`Shop role ${roleId} has been paid.`); }; diff --git a/src/schedules/shop/modules/roles/index.ts b/src/schedules/shop/modules/roles/index.ts index d4e2c21..6dea00e 100644 --- a/src/schedules/shop/modules/roles/index.ts +++ b/src/schedules/shop/modules/roles/index.ts @@ -1,32 +1,27 @@ +/* eslint-disable no-loops/no-loops */ import { Client } from "discord.js"; - -import { IShopRole } from "../../../../interfaces/ShopRole"; -import shopRoleSchema from "../../../../models/shopRole"; +import prisma from "../../../../handlers/database"; import * as dueForPayment from "./components/dueForPayment"; import * as overDueForPayment from "./components/overDueForPayment"; export const execute = async (client: Client) => { - const roles = await shopRoleSchema.find(); + const roles = await prisma.guildShopRoles.findMany(); - await Promise.all( - roles.map(async (role: IShopRole) => { - const { lastPayed } = role; - const nextPayment = new Date( - lastPayed.setHours(lastPayed.getHours() + 1) - ); + for await (const role of roles) { + const { lastPayed } = role; + const nextPayment = new Date(lastPayed.setHours(lastPayed.getHours() + 1)); - const now = new Date(); + const now = new Date(); - if (nextPayment > now) { - await dueForPayment.execute(client, role); + if (nextPayment > now) { + await dueForPayment.execute(client, role); - return; - } + return; + } - if (nextPayment < now) { - await overDueForPayment.execute(client, role); - } - }) - ); + if (nextPayment < now) { + await overDueForPayment.execute(client, role); + } + } }; diff --git a/src/schedules/timeouts/index.ts b/src/schedules/timeouts/index.ts index bcdddd1..764d825 100644 --- a/src/schedules/timeouts/index.ts +++ b/src/schedules/timeouts/index.ts @@ -1,36 +1,40 @@ +/* eslint-disable no-loops/no-loops */ import logger from "../../middlewares/logger"; -import timeoutSchema from "../../models/timeout"; - import addSeconds from "../../helpers/addSeconds"; +import prisma from "../../handlers/database"; + export const options = { schedule: "*/30 * * * *", // https://crontab.guru/ }; // Execute the job export const execute = async () => { - const timeouts = await timeoutSchema.find(); - await Promise.all( - timeouts.map(async (timeout) => { - const { guildId, userId, timeoutId, cooldown, createdAt } = timeout; + const getCooldown = await prisma.cooldown.findMany(); - const overDue = (await addSeconds(cooldown, createdAt)) < new Date(); + for await (const timeout of getCooldown) { + const { guildId, userId, timeoutId, cooldown, createdAt } = timeout; - if (overDue) { - timeoutSchema - .deleteOne({ + const overDue = (await addSeconds(cooldown, createdAt)) < new Date(); + + if (overDue) { + logger.info(timeout); + const deleteCooldown = await prisma.cooldown.delete({ + where: { + guildId_userId_timeoutId: { guildId, userId, timeoutId, - cooldown, - }) - .then(() => { - logger.debug( - `Timeout document ${timeoutId} has been deleted from user ${userId}.` - ); - }); - } - }) - ); + }, + }, + }); + + logger.silly(deleteCooldown); + + logger.debug( + `Timeout document ${timeoutId} has been deleted from user ${userId}.` + ); + } + } };