From 97abd1fceed096c018e7c2e90a70279a1f44dd13 Mon Sep 17 00:00:00 2001 From: powermaker450 Date: Thu, 16 Jan 2025 23:16:06 -0500 Subject: [PATCH] First implementation of sqlite --- .gitignore | 3 +- package.json | 2 + pnpm-lock.yaml | 74 ++++++ prisma/schema.prisma | 38 +++ src/commands/confessmod.ts | 24 +- src/commands/setup.ts | 2 +- src/commandutils/deleteConfession.ts | 6 +- src/commandutils/submitConfession.ts | 32 +-- src/contextcommands/contextdel.ts | 2 +- src/main.ts | 24 +- src/storeman/client.ts | 358 ++++++++------------------- 11 files changed, 244 insertions(+), 321 deletions(-) create mode 100644 prisma/schema.prisma diff --git a/.gitignore b/.gitignore index eebe7f2..f6c5247 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .env node_modules/ dist/ -persist/ +prisma/migrations/ +prisma/persist/ diff --git a/package.json b/package.json index 5ea0f54..1d6201c 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "author": "powermaker450", "license": "AGPLv3", "dependencies": { + "@prisma/client": "^6.2.1", "chalk": "^4.1.0", "discord.js": "^14.16.3", "dotenv": "^16.4.5", + "prisma": "^6.2.1", "zlib-sync": "^0.1.9" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b41484..e0f6da8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@prisma/client': + specifier: ^6.2.1 + version: 6.2.1(prisma@6.2.1) chalk: specifier: ^4.1.0 version: 4.1.2 @@ -17,6 +20,9 @@ importers: dotenv: specifier: ^16.4.5 version: 16.4.5 + prisma: + specifier: ^6.2.1 + version: 6.2.1 zlib-sync: specifier: ^0.1.9 version: 0.1.9 @@ -61,6 +67,30 @@ packages: resolution: {integrity: sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==} engines: {node: '>=16.11.0'} + '@prisma/client@6.2.1': + resolution: {integrity: sha512-msKY2iRLISN8t5X0Tj7hU0UWet1u0KuxSPHWuf3IRkB4J95mCvGpyQBfQ6ufcmvKNOMQSq90O2iUmJEN2e5fiA==} + engines: {node: '>=18.18'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + + '@prisma/debug@6.2.1': + resolution: {integrity: sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ==} + + '@prisma/engines-version@6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69': + resolution: {integrity: sha512-7tw1qs/9GWSX6qbZs4He09TOTg1ff3gYsB3ubaVNN0Pp1zLm9NC5C5MZShtkz7TyQjx7blhpknB7HwEhlG+PrQ==} + + '@prisma/engines@6.2.1': + resolution: {integrity: sha512-lTBNLJBCxVT9iP5I7Mn6GlwqAxTpS5qMERrhebkUhtXpGVkBNd/jHnNJBZQW4kGDCKaQg/r2vlJYkzOHnAb7ZQ==} + + '@prisma/fetch-engine@6.2.1': + resolution: {integrity: sha512-OO7O9d6Mrx2F9i+Gu1LW+DGXXyUFkP7OE5aj9iBfA/2jjDXEJjqa9X0ZmM9NZNo8Uo7ql6zKm6yjDcbAcRrw1A==} + + '@prisma/get-platform@6.2.1': + resolution: {integrity: sha512-zp53yvroPl5m5/gXYLz7tGCNG33bhG+JYCm74ohxOq1pPnrL47VQYFfF3RbTZ7TzGWCrR3EtoiYMywUBw7UK6Q==} + '@sapphire/async-queue@1.5.3': resolution: {integrity: sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w==} engines: {node: '>=v14.0.0', npm: '>=7.0.0'} @@ -118,6 +148,11 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -139,6 +174,11 @@ packages: engines: {node: '>=14'} hasBin: true + prisma@6.2.1: + resolution: {integrity: sha512-hhyM0H13pQleQ+br4CkzGizS5I0oInoeTw3JfLw1BRZduBSQxPILlJLwi+46wZzj9Je7ndyQEMGw/n5cN2fknA==} + engines: {node: '>=18.18'} + hasBin: true + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -225,6 +265,31 @@ snapshots: - bufferutil - utf-8-validate + '@prisma/client@6.2.1(prisma@6.2.1)': + optionalDependencies: + prisma: 6.2.1 + + '@prisma/debug@6.2.1': {} + + '@prisma/engines-version@6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69': {} + + '@prisma/engines@6.2.1': + dependencies: + '@prisma/debug': 6.2.1 + '@prisma/engines-version': 6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69 + '@prisma/fetch-engine': 6.2.1 + '@prisma/get-platform': 6.2.1 + + '@prisma/fetch-engine@6.2.1': + dependencies: + '@prisma/debug': 6.2.1 + '@prisma/engines-version': 6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69 + '@prisma/get-platform': 6.2.1 + + '@prisma/get-platform@6.2.1': + dependencies: + '@prisma/debug': 6.2.1 + '@sapphire/async-queue@1.5.3': {} '@sapphire/shapeshift@4.0.0': @@ -287,6 +352,9 @@ snapshots: fast-deep-equal@3.1.3: {} + fsevents@2.3.3: + optional: true + has-flag@4.0.0: {} lodash.snakecase@4.1.1: {} @@ -299,6 +367,12 @@ snapshots: prettier@3.3.3: {} + prisma@6.2.1: + dependencies: + '@prisma/engines': 6.2.1 + optionalDependencies: + fsevents: 2.3.3 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..31035dc --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,38 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +model Guild { + guildId String @id + confessChannel String @unique + modChannel String? @unique + + confessions Confession[] + bans Ban[] +} + +model Confession { + id String @id + messageId String @unique + author String + authorId String + guildId String + content String + attachment String? + + guild Guild @relation(fields: [guildId], references: [guildId]) +} + +model Ban { + authorId String @id + guildId String + confessionId String? + reason Int + + guild Guild @relation(fields: [guildId], references: [guildId]) +} diff --git a/src/commands/confessmod.ts b/src/commands/confessmod.ts index b3b614e..2cf2300 100644 --- a/src/commands/confessmod.ts +++ b/src/commands/confessmod.ts @@ -92,7 +92,7 @@ export async function execute(interaction: ChatInputCommandInteraction) { if (interaction.options.getSubcommand() === "ban") { const confessionId = interaction.options.getString("id")!; - if (dt.isBannedById(guildId, confessionId)) { + if (await dt.isBannedById(guildId, confessionId)) { return interaction .reply({ content: "That user is already banned!", @@ -101,7 +101,7 @@ export async function execute(interaction: ChatInputCommandInteraction) { .catch(err => logger.error("A ban interaction error occured:", err)); } - const result = dt.addBanById(guildId, confessionId); + const result = await dt.addBanById(guildId, confessionId); return interaction .reply({ @@ -116,7 +116,7 @@ export async function execute(interaction: ChatInputCommandInteraction) { } else if (interaction.options.getSubcommand() === "banuser") { const { id: userId } = interaction.options.getUser("user")!; - const result = dt.addBanByUser(guildId, userId); + const result = await dt.addBanByUser(guildId, userId); return interaction .reply({ @@ -127,9 +127,9 @@ export async function execute(interaction: ChatInputCommandInteraction) { // /confessmod list } else if (interaction.options.getSubcommand() === "list") { - const bannedMembers = dt.getBans(guildId); + const bannedMembers = await dt.getBans(guildId); - const determineContent = () => { + const determineContent = async () => { if (!bannedMembers.length) { return "There are no bans."; } @@ -140,11 +140,11 @@ export async function execute(interaction: ChatInputCommandInteraction) { let idHead = "\n" + heading("Confessions:", HeadingLevel.Two); let idCount = false; for (const member of bannedMembers) { - if (member.method === BanReason.ByUser) { - userHead += "\n" + `<@${member.user}>`; + if (member.reason === BanReason.ByUser) { + userHead += "\n" + `<@${member.authorId}>`; userCount = true; - } else if (member.method === BanReason.ById) { - const confession = dt.getConfession(guildId, member.confessionId!)!; + } else if (member.reason === BanReason.ById) { + const confession = (await dt.getConfession(guildId, member.confessionId!))!; idHead += `\nConfession ${inlineCode(member.confessionId!)}: ${italic(confession.content)}`; idCount = true; } @@ -160,14 +160,14 @@ export async function execute(interaction: ChatInputCommandInteraction) { return interaction .reply({ - content: determineContent(), + content: await determineContent(), ...messageOpts }) .catch(err => logger.error("A banlist interaction error occured:", err)); // /confessmod pardon } else if (interaction.options.getSubcommand() === "pardon") { - const result = dt.removeBanById( + const result = await dt.removeBanById( guildId, interaction.options.getString("id")! ); @@ -185,7 +185,7 @@ export async function execute(interaction: ChatInputCommandInteraction) { } else if (interaction.options.getSubcommand() === "pardonuser") { const { id: userId } = interaction.options.getUser("user")!; - const result = dt.removeBanByUser(guildId, userId); + const result = await dt.removeBanByUser(guildId, userId); return interaction .reply({ diff --git a/src/commands/setup.ts b/src/commands/setup.ts index d2b4bbd..afa5635 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -42,7 +42,7 @@ export async function execute(interaction: CommandInteraction) { const { id: guildId } = interaction.guild!; const { displayName: username } = interaction.user; - if (dt.checkSetup(guildId)) { + if (await dt.checkSetup(guildId)) { return interaction .reply({ content: "This guild has already been set up!", diff --git a/src/commandutils/deleteConfession.ts b/src/commandutils/deleteConfession.ts index 705297b..1382562 100644 --- a/src/commandutils/deleteConfession.ts +++ b/src/commandutils/deleteConfession.ts @@ -38,7 +38,7 @@ export async function deleteConfession( const { id: guildId } = interaction.guild!; const { id: userId } = interaction.user; - const result = dt.getConfession(guildId, idVal); + const result = await dt.getConfession(guildId, idVal); // If there is a result, and the user is either an author or has manage messages const allowedByUser = result && result.authorId === userId; const allowedByMod = @@ -48,8 +48,8 @@ export async function deleteConfession( // If a confession is found with the given ID, check if the user is the one that posted it, and delete it if they are. // Otherwise, don't let the user delete anything. if (allowedByUser || allowedByMod) { - const confession = dt.getConfession(guildId, idVal)!.messageId; - const channelId = dt.getGuildInfo(guildId)!.settings.confessChannel; + const confession = (await dt.getConfession(guildId, idVal))!.messageId; + const channelId = (await dt.getGuildInfo(guildId))!.confessChannel; const emptyEmbed = new EmbedBuilder() .setColor(getRandomColor()) .setTitle("Confession Deleted") diff --git a/src/commandutils/submitConfession.ts b/src/commandutils/submitConfession.ts index 3c0d2e1..6a5305f 100644 --- a/src/commandutils/submitConfession.ts +++ b/src/commandutils/submitConfession.ts @@ -27,7 +27,7 @@ export async function submitConfession( const { id: userId, displayName: username } = interaction.user; // If the user is banned in this guild, don't let them post - if (dt.isBannedByUser(guildId, userId)) { + if (await dt.isBannedByUser(guildId, userId)) { return interaction.reply({ content: "You are banned from confessions in this server!", ...messageOpts @@ -35,7 +35,7 @@ export async function submitConfession( } // If no guild info is present for this guild, don't let the user post - if (!dt.getGuildInfo(guildId)) { + if (!(await dt.getGuildInfo(guildId))) { return interaction.reply({ content: "The bot hasn't been set up yet! Ask the server admins to set it up.", @@ -43,8 +43,7 @@ export async function submitConfession( }); } - const confessChannel = dt.getGuildInfo(guildId)!.settings.confessChannel; - const adminChannel = dt.getGuildInfo(guildId)?.settings.modChannel; + const { confessChannel, modChannel } = (await dt.getGuildInfo(guildId))!; const isAttachment = (text: string | null) => text && (text.startsWith("http://") || text.startsWith("https://")); @@ -125,10 +124,10 @@ export async function submitConfession( components: [actionRow] }); - adminChannel && - (await (BotClient.channels.cache.get(adminChannel!) as TextChannel).send({ + modChannel && + (BotClient.channels.cache.get(modChannel!) as TextChannel).send({ embeds: [adminConfessionEmbed] - })); + }); const fields: readonly [Message, string, string, string, string] = [ message, @@ -142,23 +141,12 @@ export async function submitConfession( ? dt.addConfession(...fields, attachmentContent) : dt.addConfession(...fields); - const confessionsLength = dt.getGuildInfo(guildId)!.confessions.length; + const confessions = await dt.getConfessions(guildId); // If there are 2 or more confessions, remove the previous confession's button components - if (confessionsLength >= 2) { - (BotClient.channels.cache.get(confessChannel) as TextChannel).messages - .fetch( - dt.getGuildInfo(guildId)!.confessions[confessionsLength - 2].messageId - ) - .then(message => { - message.edit({ components: [] }); - }) - .catch(err => { - logger.error( - "An error occured removing embeds from the previous message:", - err - ); - }); + if (confessions.length >= 2) { + const previousMessage = await (BotClient.channels.cache.get(confessChannel) as TextChannel).messages.fetch(confessions[confessions.length - 2].messageId); + previousMessage.edit({ components: [] }).catch(err => logger.error("An error occured removing embeds from the previous message:", err)); } return interaction.reply({ diff --git a/src/contextcommands/contextdel.ts b/src/contextcommands/contextdel.ts index 12dfee6..aa7e65f 100644 --- a/src/contextcommands/contextdel.ts +++ b/src/contextcommands/contextdel.ts @@ -47,7 +47,7 @@ export async function execute(interaction: ContextMenuCommandInteraction) { }); } - const { id: confessionId } = dt.getConfessionById(guildId!, targetId)!; + const { id: confessionId } = (await dt.getConfessionById(guildId!, targetId))!; try { deleteConfession(interaction, confessionId); diff --git a/src/main.ts b/src/main.ts index e9db3b6..b7d459a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -29,7 +29,7 @@ import { messageOpts } from "./constants"; import { submitConfession } from "./commandutils"; import { contextCommands } from "./contextcommands"; -export const dt = new StoreMan(StoreMan.checkFile()); +export const dt = new StoreMan(); const logger = new Logger("Main"); BotClient.once("ready", client => { @@ -75,25 +75,7 @@ BotClient.on(Events.InteractionCreate, async interaction => { } }); -BotClient.on(Events.MessageDelete, async message => { - const guildId = message.guild?.id!; - if (!dt.getGuildInfo(guildId)) { - return; - } - - try { - const messageId = message.id; - const confessions = dt.getGuildInfo(guildId)?.confessions!; - - for (const confession of confessions) { - if (confession.messageId === messageId) { - dt.adminDelConfession(guildId, confession.id); - } - } - } catch (err) { - logger.error("An error occured:", err); - } -}); +// BotClient.on(Events.MessageDelete, async message => {}); // Submit Confession button BotClient.on(Events.InteractionCreate, async interaction => { @@ -108,7 +90,7 @@ BotClient.on(Events.InteractionCreate, async interaction => { if (requestSubmit) { // Check if the user is banned from confessions before showing the modal - dt.isBannedByUser(interaction.guild?.id!, interaction.user.id) + await dt.isBannedByUser(interaction.guild?.id!, interaction.user.id) ? interaction .reply({ content: "You are banned from confessions in this server!", diff --git a/src/storeman/client.ts b/src/storeman/client.ts index 9f65133..9da957f 100644 --- a/src/storeman/client.ts +++ b/src/storeman/client.ts @@ -16,341 +16,179 @@ * along with this program. If not, see . */ -import fs from "fs"; import crypto from "crypto"; import { BanReason, - Confession, - ConfessionBan, - GuildData, GuildSettings } from "./types"; -import { DATA_DIR } from "./config"; import { CommandInteraction, Message } from "discord.js"; +import { Guild, Confession, PrismaClient, Ban } from "@prisma/client"; import Logger from "../utils/Logger"; export class StoreMan { - public static readonly fullPath: string = - (DATA_DIR ?? "./persist/") + "data.json"; private static logger = new Logger("StoreMan"); - private data: GuildData[]; + private static checkResult = (result: any | null) => result ? true : false; - constructor(existingData: GuildData[] = []) { - this.data = existingData; + private client: PrismaClient; + + constructor() { + this.client = new PrismaClient(); } public static genId = () => crypto.randomBytes(2).toString("hex"); - public static toConfession( - message: Message, - id: string, - author: string, - authorId: string, - content: string, - attachment?: string - ): Confession { - return { - id: id, - messageId: message.id, - author: author, - authorId: authorId, - content: content, - attachment: attachment - }; - } - - public static checkFile(): GuildData[] { - let final: GuildData[]; - - if (fs.existsSync(StoreMan.fullPath)) { - const data = fs.readFileSync(StoreMan.fullPath); - - // Read the file if it isn't empty, else set final to an empty array - final = !data.toString().trim() ? [] : JSON.parse(data.toString()); - } else { - // If the directory doesn't exist, make it - !fs.existsSync(DATA_DIR ?? "./persist/") && - fs.mkdirSync(DATA_DIR ?? "./persist/"); - fs.createWriteStream(StoreMan.fullPath); - final = []; - } - - return final; - } - - public async saveFile(): Promise { - fs.writeFile( - StoreMan.fullPath, - JSON.stringify(this.data, null, 2), - "utf8", - err => err && StoreMan.logger.error("A write error occured:", err) - ); - } - // Checks if a guild is not set up - public checkSetup(guildId: string): boolean { - for (const guild of this.data) { - if (guild.id === guildId) { - return true; - } - } + public async checkSetup(guildId: string): Promise { + const result = await this.client.guild.findFirst({ where: { guildId } }).then(StoreMan.checkResult); - return false; + return result; } // Sets up a guild and stores it in the persistent file - public setup(guildId: string, opts: GuildSettings): void { - this.data.push({ - id: guildId, - confessions: [], - settings: opts - }); - - this.saveFile(); + public async setup(guildId: string, { confessChannel, modChannel }: GuildSettings): Promise { + await this.client.guild.create({ data: { guildId, confessChannel, modChannel } }).then(() => StoreMan.logger.log("Guild created")); } // Clear the settings for a given guild - public clearSettings(guildId: string): void { - this.data = this.data.filter(guild => { - return guild.id !== guildId; - }); - this.saveFile(); + public async clearSettings(guildId: string): Promise { + await this.client.confession.deleteMany({ where: { guildId } }); + await this.client.ban.deleteMany({ where: { guildId } }); + await this.client.guild.delete({ where: { guildId } }); } - public getGuildInfo(guildId: string): GuildData | null { - for (const guild of this.data) { - if (guild.id === guildId) { - return guild; - } - } - - return null; + public async getGuildInfo(guildId: string): Promise { + return await this.client.guild.findFirst({ where: { guildId } }); } // Attempts to add a confession. Returns true if the confession is sent, false if otherwise. - public addConfession( + public async addConfession( message: Message, id: string, author: string, authorId: string, content: string, attachment?: string - ): boolean { + ): Promise { const { id: guildId } = message.guild!; + const { id: messageId } = message; - for (const guild of this.data) { - if (guild.id === guildId) { - // If the author's user ID is in the ban list, don't let them post a confession. - if (this.isBannedByUser(guildId, author)) { - return false; - } + const ban = await this.client.ban.findFirst({ where: { guildId, authorId } }).then(StoreMan.checkResult); - guild.confessions.push( - StoreMan.toConfession( - message, - id, - author, - authorId, - content, - attachment - ) - ); - this.saveFile(); - return true; - } + if (ban) { + return false; } - throw new Error( - `No guild with id ${id} was found. Something's pretty wrong.` - ); + await this.client.confession.create({ data: { id, messageId, author, authorId, guildId, content, attachment } }) + return true; } - public getConfession( + public async getConfession( guildId: string, - confessionId: string - ): Confession | null { - for (const guild of this.data) { - if (guild.id === guildId) { - for (const confession of guild.confessions) { - if (confession.id === confessionId) { - return confession; - } - } - } - } - - return null; + id: string + ): Promise { + return await this.client.confession.findFirst({ where: { guildId, id } }); } - public getConfessionById(guildId: string, messageId: string): Confession | null { - for (const guild of this.data) { - if (guild.id === guildId) { - for (const confession of guild.confessions) { - if (confession.messageId === messageId) { - return confession; - } - } - } - } - - return null; + public async getConfessions(guildId: string): Promise { + return await this.client.confession.findMany({ where: { guildId } }); } - // Attempts to delete a confession. If it is sucessfully deleted, returns true, else false. - public delConfesssion( - { guild, user }: CommandInteraction, - confessionId: string - ): boolean { - const guildId = guild?.id; - const userId = user.id; - - for (const guild of this.data) { - if (guild.id === guildId) { - for (const confession of guild.confessions) { - if (confession.authorId === userId) { - guild.confessions = guild.confessions.filter(confession => { - return confession.id !== confessionId; - }); - - this.saveFile(); - return true; - } - } - } - } - - return false; + public async getConfessionById(guildId: string, messageId: string): Promise { + return await this.client.confession.findFirst({ where: { guildId, messageId } }); } - public adminDelConfession(guildId: string, confessionId: string): void { - for (const guild of this.data) { - if (guild.id === guildId) { - guild.confessions = guild.confessions.filter(confession => { - return confession.id !== confessionId; - }); + /** + * Delete a confession from the database. + * + * @param interaction - Used to obtain the guild and authorId + * @param id - The confession ID to delete + * + * @returns true if the confession was sucessfully deleted, false if otherwise. + */ + public async delConfesssion( + { guild, user: { id: authorId } }: CommandInteraction, + id: string + ): Promise { + const { id: guildId } = guild!; + const result = await this.client.confession.delete({ where: { guildId, authorId, id } }).then(StoreMan.checkResult); - this.saveFile(); - } - } + return result; + } + + /** + * Delete a confession from the database as an admin. + * + * @param guildId - The ID of the guild to delete from + * @param id - The ID of the confession to delete + */ + public async adminDelConfession(guildId: string, id: string): Promise { + await this.client.confession.delete({ where: { guildId, id } }); } // Check if a certain user is banned within a guild. - public isBannedByUser(guildId: string, userId: string): boolean { - for (const guild of this.data) { - if (guild.id === guildId) { - for (const ban of guild.settings.bans) { - if (ban.user === userId) { - return true; - } - } - } - } + public async isBannedByUser(guildId: string, authorId: string): Promise { + const result = await this.client.ban.findFirst({ where: { guildId, authorId } }).then(StoreMan.checkResult); - return false; + return result; } - public isBannedById(guildId: string, confessionId: string): boolean { - for (const guild of this.data) { - if (guild.id === guildId) { - for (const ban of guild.settings.bans) { - if (ban.confessionId === confessionId) { - return true; - } - } - } - } + public async isBannedById(guildId: string, confessionId: string): Promise { + const result = await this.client.ban.findFirst({ where: { guildId, confessionId } }).then(StoreMan.checkResult); - return false; + return result; } - public getBans(guildId: string): ConfessionBan[] { - for (const guild of this.data) { - if (guild.id === guildId) { - return guild.settings.bans; - } - } - - return []; + public async getBans(guildId: string): Promise { + return await this.client.ban.findMany({ where: { guildId } }); } - // Attempts to ban a user from confessions. - public addBanById(guildId: string, confessionId: string): boolean { - const confession = this.getConfession(guildId, confessionId); - - for (const guild of this.data) { - if (guild.id === guildId) { - if (confession) { - // Only add the user to the ban list if they aren't banned already - !this.isBannedByUser(guildId, confession.authorId) && - guild.settings.bans.push({ - user: confession.authorId, - confessionId: confessionId, - method: BanReason.ById - }); - - this.saveFile(); - return true; - } - } + /** + * Ban a user by confession id. + * + * @param guildId - The ID of the guild to ban from + * @param confessionId - The ID of the confession to ban for + */ + public async addBanById(guildId: string, confessionId: string): Promise { + const alreadyBanned = await this.isBannedById(guildId, confessionId); + + if (alreadyBanned) { + return false; } - return false; + const { authorId } = await this.client.confession.findFirstOrThrow({ where: { guildId, id: confessionId } }); + await this.client.ban.create({ data: { guildId, authorId, confessionId, reason: BanReason.ById } }); + return true; } - public addBanByUser(guildId: string, userId: string): boolean { - for (const guild of this.data) { - if (guild.id === guildId) { - // Only add the user to the ban list if they aren't banned already - !this.isBannedByUser(guildId, userId) && - guild.settings.bans.push({ - user: userId, - method: BanReason.ByUser - }); + public async addBanByUser(guildId: string, authorId: string): Promise { + const alreadyBanned = await this.isBannedByUser(guildId, authorId); - this.saveFile(); - return true; - } + if (alreadyBanned) { + return false; } - return false; + await this.client.ban.create({ data: { guildId, authorId, reason: BanReason.ByUser } }); + return true; } // Attempts to pardon a user from a ban. If sucessfully completed, returns true, false if otherwise. - public removeBanById(guildId: string, confessionId: string): boolean { - for (const guild of this.data) { - if (guild.id === guildId) { - if (this.getConfession(guildId, confessionId)) { - guild.settings.bans = guild.settings.bans.filter(ban => { - return ( - ban.user !== this.getConfession(guildId, confessionId)?.authorId! - ); - }); - - this.saveFile(); - return true; - } - } + public async removeBanById(guildId: string, confessionId: string): Promise { + if (await this.isBannedById(guildId, confessionId)) { + return false; } - return false; + const { authorId } = await this.client.ban.findFirstOrThrow({ where: { guildId, confessionId } }); + + await this.client.ban.delete({ where: { guildId, confessionId, authorId } }); + return true; } - public removeBanByUser(guildId: string, userId: string): boolean { - for (const guild of this.data) { - if (guild.id === guildId) { - for (const ban of guild.settings.bans) { - if (ban.method === BanReason.ByUser && ban.user === userId) { - guild.settings.bans = guild.settings.bans.filter(ban => { - return ban.user !== userId; - }); - - this.saveFile(); - return true; - } - } - } + public async removeBanByUser(guildId: string, authorId: string): Promise { + if (await this.isBannedByUser(guildId, authorId)) { + return false; } - return false; + await this.client.ban.delete({ where: { guildId, authorId } }); + return true; } }