diff --git a/prisma/migrations/20221019142757_modules/migration.sql b/prisma/migrations/20221019142757_modules/migration.sql new file mode 100644 index 0000000..ec153f0 --- /dev/null +++ b/prisma/migrations/20221019142757_modules/migration.sql @@ -0,0 +1,16 @@ +-- RedefineTables +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_GuildMember" ( + "userId" TEXT NOT NULL, + "guildId" TEXT NOT NULL, + "creditsEarned" INTEGER NOT NULL DEFAULT 0, + "pointsEarned" INTEGER NOT NULL DEFAULT 0, + CONSTRAINT "GuildMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "GuildMember_guildId_fkey" FOREIGN KEY ("guildId") REFERENCES "Guild" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_GuildMember" ("creditsEarned", "guildId", "pointsEarned", "userId") SELECT coalesce("creditsEarned", 0) AS "creditsEarned", "guildId", coalesce("pointsEarned", 0) AS "pointsEarned", "userId" FROM "GuildMember"; +DROP TABLE "GuildMember"; +ALTER TABLE "new_GuildMember" RENAME TO "GuildMember"; +CREATE UNIQUE INDEX "GuildMember_userId_guildId_key" ON "GuildMember"("userId", "guildId"); +PRAGMA foreign_key_check; +PRAGMA foreign_keys=ON; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a1ab624..7b621c1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -2,7 +2,8 @@ // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + previewFeatures = ["interactiveTransactions"] } datasource db { @@ -86,8 +87,8 @@ model GuildMember { // Settings // Modules - creditsEarned Int? - pointsEarned Int? + creditsEarned Int @default(0) + pointsEarned Int @default(0) // Unique Identifier @@unique([userId, guildId]) diff --git a/src/helpers/transferCredits.ts b/src/helpers/transferCredits.ts new file mode 100644 index 0000000..538eee9 --- /dev/null +++ b/src/helpers/transferCredits.ts @@ -0,0 +1,93 @@ +import { Guild, User } from "discord.js"; +import prisma from "../prisma"; + +export default async (guild: Guild, from: User, to: User, amount: number) => { + return await prisma.$transaction(async (tx) => { + // 1. Decrement amount from the sender. + const sender = await tx.guildMember.upsert({ + update: { + creditsEarned: { + decrement: amount, + }, + }, + create: { + user: { + connectOrCreate: { + create: { + id: from.id, + }, + where: { + id: from.id, + }, + }, + }, + guild: { + connectOrCreate: { + create: { + id: guild.id, + }, + where: { + id: guild.id, + }, + }, + }, + creditsEarned: -amount, + }, + where: { + userId_guildId: { + userId: from.id, + guildId: guild.id, + }, + }, + }); + + if (!sender) throw new Error("No sender available"); + + if (!sender.creditsEarned) throw new Error("No credits available"); + + // 2. Verify that the sender's balance didn't go below zero. + if (sender.creditsEarned < 0) { + throw new Error(`${from} doesn't have enough to send ${amount}`); + } + + // 3. Increment the recipient's balance by amount + const recipient = await tx.guildMember.upsert({ + update: { + creditsEarned: { + increment: amount, + }, + }, + create: { + user: { + connectOrCreate: { + create: { + id: to.id, + }, + where: { + id: to.id, + }, + }, + }, + guild: { + connectOrCreate: { + create: { + id: guild.id, + }, + where: { + id: guild.id, + }, + }, + }, + creditsEarned: +amount, + }, + where: { + userId_guildId: { + userId: to.id, + guildId: guild.id, + }, + }, + }); + + return recipient; + }); +};