mirror of
https://github.com/cassoule/flopobot_v2.git
synced 2026-03-18 21:40:27 +01:00
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ flopobot.db
|
|||||||
flopobot.db-shm
|
flopobot.db-shm
|
||||||
flopobot.db-wal
|
flopobot.db-wal
|
||||||
.idea
|
.idea
|
||||||
*.db
|
*.db
|
||||||
|
.claude
|
||||||
|
|||||||
960
package-lock.json
generated
960
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,15 +11,17 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node index.js",
|
"start": "node index.js",
|
||||||
"register": "node commands.js",
|
"register": "node commands.js",
|
||||||
"dev": "nodemon index.js"
|
"dev": "nodemon index.js",
|
||||||
|
"prisma:generate": "prisma generate",
|
||||||
|
"prisma:migrate": "prisma migrate dev"
|
||||||
},
|
},
|
||||||
"author": "Milo Gourvest",
|
"author": "Milo Gourvest",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@google/genai": "^1.30.0",
|
"@google/genai": "^1.30.0",
|
||||||
"@mistralai/mistralai": "^1.6.0",
|
"@mistralai/mistralai": "^1.6.0",
|
||||||
|
"@prisma/client": "^6.19.2",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"better-sqlite3": "^11.9.1",
|
|
||||||
"discord-interactions": "^4.0.0",
|
"discord-interactions": "^4.0.0",
|
||||||
"discord.js": "^14.18.0",
|
"discord.js": "^14.18.0",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
@@ -27,7 +29,9 @@
|
|||||||
"node-cron": "^3.0.3",
|
"node-cron": "^3.0.3",
|
||||||
"openai": "^4.104.0",
|
"openai": "^4.104.0",
|
||||||
"pokersolver": "^2.1.4",
|
"pokersolver": "^2.1.4",
|
||||||
|
"prisma": "^6.19.2",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
|
"stripe": "^20.3.0",
|
||||||
"unique-names-generator": "^4.7.1",
|
"unique-names-generator": "^4.7.1",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
|
|||||||
138
prisma/migrations/0_init/migration.sql
Normal file
138
prisma/migrations/0_init/migration.sql
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"globalName" TEXT,
|
||||||
|
"warned" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"warns" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"allTimeWarns" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"totalRequests" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"coins" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"dailyQueried" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"avatarUrl" TEXT,
|
||||||
|
"isAkhy" INTEGER NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "skins" (
|
||||||
|
"uuid" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"displayName" TEXT,
|
||||||
|
"contentTierUuid" TEXT,
|
||||||
|
"displayIcon" TEXT,
|
||||||
|
"user_id" TEXT,
|
||||||
|
"tierRank" TEXT,
|
||||||
|
"tierColor" TEXT,
|
||||||
|
"tierText" TEXT,
|
||||||
|
"basePrice" TEXT,
|
||||||
|
"currentLvl" INTEGER,
|
||||||
|
"currentChroma" INTEGER,
|
||||||
|
"currentPrice" INTEGER,
|
||||||
|
"maxPrice" INTEGER,
|
||||||
|
CONSTRAINT "skins_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "market_offers" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"skin_uuid" TEXT NOT NULL,
|
||||||
|
"seller_id" TEXT NOT NULL,
|
||||||
|
"starting_price" INTEGER NOT NULL,
|
||||||
|
"buyout_price" INTEGER,
|
||||||
|
"final_price" INTEGER,
|
||||||
|
"status" TEXT NOT NULL,
|
||||||
|
"posted_at" TEXT DEFAULT '',
|
||||||
|
"opening_at" TEXT NOT NULL,
|
||||||
|
"closing_at" TEXT NOT NULL,
|
||||||
|
"buyer_id" TEXT,
|
||||||
|
CONSTRAINT "market_offers_skin_uuid_fkey" FOREIGN KEY ("skin_uuid") REFERENCES "skins" ("uuid") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "market_offers_seller_id_fkey" FOREIGN KEY ("seller_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "market_offers_buyer_id_fkey" FOREIGN KEY ("buyer_id") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "bids" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"bidder_id" TEXT NOT NULL,
|
||||||
|
"market_offer_id" TEXT NOT NULL,
|
||||||
|
"offer_amount" INTEGER NOT NULL,
|
||||||
|
"offered_at" TEXT DEFAULT '',
|
||||||
|
CONSTRAINT "bids_bidder_id_fkey" FOREIGN KEY ("bidder_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "bids_market_offer_id_fkey" FOREIGN KEY ("market_offer_id") REFERENCES "market_offers" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "logs" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"user_id" TEXT NOT NULL,
|
||||||
|
"action" TEXT,
|
||||||
|
"target_user_id" TEXT,
|
||||||
|
"coins_amount" INTEGER,
|
||||||
|
"user_new_amount" INTEGER,
|
||||||
|
"created_at" TEXT DEFAULT '',
|
||||||
|
CONSTRAINT "logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "logs_target_user_id_fkey" FOREIGN KEY ("target_user_id") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "games" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"p1" TEXT NOT NULL,
|
||||||
|
"p2" TEXT,
|
||||||
|
"p1_score" INTEGER,
|
||||||
|
"p2_score" INTEGER,
|
||||||
|
"p1_elo" INTEGER,
|
||||||
|
"p2_elo" INTEGER,
|
||||||
|
"p1_new_elo" INTEGER,
|
||||||
|
"p2_new_elo" INTEGER,
|
||||||
|
"type" TEXT,
|
||||||
|
"timestamp" TEXT,
|
||||||
|
CONSTRAINT "games_p1_fkey" FOREIGN KEY ("p1") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT "games_p2_fkey" FOREIGN KEY ("p2") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "elos" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"elo" INTEGER NOT NULL,
|
||||||
|
CONSTRAINT "elos_id_fkey" FOREIGN KEY ("id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "sotd" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"tableauPiles" TEXT,
|
||||||
|
"foundationPiles" TEXT,
|
||||||
|
"stockPile" TEXT,
|
||||||
|
"wastePile" TEXT,
|
||||||
|
"isDone" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"seed" TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "sotd_stats" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"user_id" TEXT NOT NULL,
|
||||||
|
"time" INTEGER,
|
||||||
|
"moves" INTEGER,
|
||||||
|
"score" INTEGER,
|
||||||
|
CONSTRAINT "sotd_stats_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "transactions" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"session_id" TEXT NOT NULL,
|
||||||
|
"user_id" TEXT NOT NULL,
|
||||||
|
"coins_amount" INTEGER NOT NULL,
|
||||||
|
"amount_cents" INTEGER NOT NULL,
|
||||||
|
"currency" TEXT NOT NULL DEFAULT 'eur',
|
||||||
|
"customer_email" TEXT,
|
||||||
|
"customer_name" TEXT,
|
||||||
|
"payment_status" TEXT NOT NULL,
|
||||||
|
"created_at" TEXT DEFAULT '',
|
||||||
|
CONSTRAINT "transactions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "transactions_session_id_key" ON "transactions"("session_id");
|
||||||
|
|
||||||
175
prisma/schema.prisma
Normal file
175
prisma/schema.prisma
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id
|
||||||
|
username String
|
||||||
|
globalName String?
|
||||||
|
warned Int @default(0)
|
||||||
|
warns Int @default(0)
|
||||||
|
allTimeWarns Int @default(0)
|
||||||
|
totalRequests Int @default(0)
|
||||||
|
coins Int @default(0)
|
||||||
|
dailyQueried Int @default(0)
|
||||||
|
avatarUrl String?
|
||||||
|
isAkhy Int @default(0)
|
||||||
|
|
||||||
|
elo Elo?
|
||||||
|
skins Skin[]
|
||||||
|
sellerOffers MarketOffer[] @relation("Seller")
|
||||||
|
buyerOffers MarketOffer[] @relation("Buyer")
|
||||||
|
bids Bid[]
|
||||||
|
logs Log[] @relation("UserLogs")
|
||||||
|
targetLogs Log[] @relation("TargetUserLogs")
|
||||||
|
gamesAsP1 Game[] @relation("Player1")
|
||||||
|
gamesAsP2 Game[] @relation("Player2")
|
||||||
|
sotdStats SotdStat[]
|
||||||
|
transactions Transaction[]
|
||||||
|
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Skin {
|
||||||
|
uuid String @id
|
||||||
|
displayName String?
|
||||||
|
contentTierUuid String?
|
||||||
|
displayIcon String?
|
||||||
|
userId String? @map("user_id")
|
||||||
|
tierRank String?
|
||||||
|
tierColor String?
|
||||||
|
tierText String?
|
||||||
|
basePrice String?
|
||||||
|
currentLvl Int?
|
||||||
|
currentChroma Int?
|
||||||
|
currentPrice Int?
|
||||||
|
maxPrice Int?
|
||||||
|
|
||||||
|
owner User? @relation(fields: [userId], references: [id])
|
||||||
|
marketOffers MarketOffer[]
|
||||||
|
|
||||||
|
@@map("skins")
|
||||||
|
}
|
||||||
|
|
||||||
|
model MarketOffer {
|
||||||
|
id String @id
|
||||||
|
skinUuid String @map("skin_uuid")
|
||||||
|
sellerId String @map("seller_id")
|
||||||
|
startingPrice Int @map("starting_price")
|
||||||
|
buyoutPrice Int? @map("buyout_price")
|
||||||
|
finalPrice Int? @map("final_price")
|
||||||
|
status String
|
||||||
|
postedAt DateTime? @default(now()) @map("posted_at")
|
||||||
|
openingAt DateTime @map("opening_at")
|
||||||
|
closingAt DateTime @map("closing_at")
|
||||||
|
buyerId String? @map("buyer_id")
|
||||||
|
|
||||||
|
skin Skin @relation(fields: [skinUuid], references: [uuid])
|
||||||
|
seller User @relation("Seller", fields: [sellerId], references: [id])
|
||||||
|
buyer User? @relation("Buyer", fields: [buyerId], references: [id])
|
||||||
|
bids Bid[]
|
||||||
|
|
||||||
|
@@map("market_offers")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Bid {
|
||||||
|
id String @id
|
||||||
|
bidderId String @map("bidder_id")
|
||||||
|
marketOfferId String @map("market_offer_id")
|
||||||
|
offerAmount Int @map("offer_amount")
|
||||||
|
offeredAt DateTime? @default(now()) @map("offered_at")
|
||||||
|
|
||||||
|
bidder User @relation(fields: [bidderId], references: [id])
|
||||||
|
marketOffer MarketOffer @relation(fields: [marketOfferId], references: [id])
|
||||||
|
|
||||||
|
@@map("bids")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Log {
|
||||||
|
id String @id
|
||||||
|
userId String @map("user_id")
|
||||||
|
action String?
|
||||||
|
targetUserId String? @map("target_user_id")
|
||||||
|
coinsAmount Int? @map("coins_amount")
|
||||||
|
userNewAmount Int? @map("user_new_amount")
|
||||||
|
createdAt DateTime? @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
user User @relation("UserLogs", fields: [userId], references: [id])
|
||||||
|
targetUser User? @relation("TargetUserLogs", fields: [targetUserId], references: [id])
|
||||||
|
|
||||||
|
@@map("logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Game {
|
||||||
|
id String @id
|
||||||
|
p1 String
|
||||||
|
p2 String?
|
||||||
|
p1Score Int? @map("p1_score")
|
||||||
|
p2Score Int? @map("p2_score")
|
||||||
|
p1Elo Int? @map("p1_elo")
|
||||||
|
p2Elo Int? @map("p2_elo")
|
||||||
|
p1NewElo Int? @map("p1_new_elo")
|
||||||
|
p2NewElo Int? @map("p2_new_elo")
|
||||||
|
type String?
|
||||||
|
timestamp DateTime?
|
||||||
|
|
||||||
|
player1 User @relation("Player1", fields: [p1], references: [id])
|
||||||
|
player2 User? @relation("Player2", fields: [p2], references: [id])
|
||||||
|
|
||||||
|
@@map("games")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Elo {
|
||||||
|
id String @id
|
||||||
|
elo Int
|
||||||
|
|
||||||
|
user User @relation(fields: [id], references: [id])
|
||||||
|
|
||||||
|
@@map("elos")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Sotd {
|
||||||
|
id Int @id
|
||||||
|
tableauPiles String?
|
||||||
|
foundationPiles String?
|
||||||
|
stockPile String?
|
||||||
|
wastePile String?
|
||||||
|
isDone Int @default(0)
|
||||||
|
seed String?
|
||||||
|
|
||||||
|
@@map("sotd")
|
||||||
|
}
|
||||||
|
|
||||||
|
model SotdStat {
|
||||||
|
id String @id
|
||||||
|
userId String @map("user_id")
|
||||||
|
time Int?
|
||||||
|
moves Int?
|
||||||
|
score Int?
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@map("sotd_stats")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Transaction {
|
||||||
|
id String @id
|
||||||
|
sessionId String @unique @map("session_id")
|
||||||
|
userId String @map("user_id")
|
||||||
|
coinsAmount Int @map("coins_amount")
|
||||||
|
amountCents Int @map("amount_cents")
|
||||||
|
currency String @default("eur")
|
||||||
|
customerEmail String? @map("customer_email")
|
||||||
|
customerName String? @map("customer_name")
|
||||||
|
paymentStatus String @map("payment_status")
|
||||||
|
createdAt DateTime? @default(now()) @map("created_at")
|
||||||
|
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@map("transactions")
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
InteractionResponseFlags,
|
InteractionResponseFlags,
|
||||||
} from "discord-interactions";
|
} from "discord-interactions";
|
||||||
import { activeInventories, skins } from "../../game/state.js";
|
import { activeInventories, skins } from "../../game/state.js";
|
||||||
import { getUserInventory } from "../../database/index.js";
|
import * as skinService from "../../services/skin.service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the /inventory slash command.
|
* Handles the /inventory slash command.
|
||||||
@@ -33,7 +33,7 @@ export async function handleInventoryCommand(req, res, client, interactionId) {
|
|||||||
// --- 1. Fetch Data ---
|
// --- 1. Fetch Data ---
|
||||||
const guild = await client.guilds.fetch(guild_id);
|
const guild = await client.guilds.fetch(guild_id);
|
||||||
const targetMember = await guild.members.fetch(targetUserId);
|
const targetMember = await guild.members.fetch(targetUserId);
|
||||||
const inventorySkins = getUserInventory.all({ user_id: targetUserId });
|
const inventorySkins = await skinService.getUserInventory(targetUserId);
|
||||||
|
|
||||||
// --- 2. Handle Empty Inventory ---
|
// --- 2. Handle Empty Inventory ---
|
||||||
if (inventorySkins.length === 0) {
|
if (inventorySkins.length === 0) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
ButtonStyleTypes,
|
ButtonStyleTypes,
|
||||||
} from "discord-interactions";
|
} from "discord-interactions";
|
||||||
import { activeSearchs, skins } from "../../game/state.js";
|
import { activeSearchs, skins } from "../../game/state.js";
|
||||||
import { getAllSkins } from "../../database/index.js";
|
import * as skinService from "../../services/skin.service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the /search slash command.
|
* Handles the /search slash command.
|
||||||
@@ -23,7 +23,7 @@ export async function handleSearchCommand(req, res, client, interactionId) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// --- 1. Fetch and Filter Data ---
|
// --- 1. Fetch and Filter Data ---
|
||||||
const allDbSkins = getAllSkins.all();
|
const allDbSkins = await skinService.getAllSkins();
|
||||||
const resultSkins = allDbSkins.filter(
|
const resultSkins = allDbSkins.filter(
|
||||||
(skin) =>
|
(skin) =>
|
||||||
skin.displayName.toLowerCase().includes(searchValue) || skin.tierText.toLowerCase().includes(searchValue),
|
skin.displayName.toLowerCase().includes(searchValue) || skin.tierText.toLowerCase().includes(searchValue),
|
||||||
@@ -61,12 +61,12 @@ export async function handleSearchCommand(req, res, client, interactionId) {
|
|||||||
|
|
||||||
// Fetch owner details if the skin is owned
|
// Fetch owner details if the skin is owned
|
||||||
let ownerText = "";
|
let ownerText = "";
|
||||||
if (currentSkin.user_id) {
|
if (currentSkin.userId) {
|
||||||
try {
|
try {
|
||||||
const owner = await guild.members.fetch(currentSkin.user_id);
|
const owner = await guild.members.fetch(currentSkin.userId);
|
||||||
ownerText = `| **@${owner.user.globalName || owner.user.username}** ✅`;
|
ownerText = `| **@${owner.user.globalName || owner.user.username}** ✅`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`Could not fetch owner for user ID: ${currentSkin.user_id}`);
|
console.warn(`Could not fetch owner for user ID: ${currentSkin.userId}`);
|
||||||
ownerText = "| Appartenant à un utilisateur inconnu";
|
ownerText = "| Appartenant à un utilisateur inconnu";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { InteractionResponseType } from "discord-interactions";
|
import { InteractionResponseType } from "discord-interactions";
|
||||||
import { getTopSkins } from "../../database/index.js";
|
import * as skinService from "../../services/skin.service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the /skins slash command.
|
* Handles the /skins slash command.
|
||||||
@@ -13,7 +13,7 @@ export async function handleSkinsCommand(req, res, client) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// --- 1. Fetch Data ---
|
// --- 1. Fetch Data ---
|
||||||
const topSkins = getTopSkins.all();
|
const topSkins = await skinService.getTopSkins();
|
||||||
const guild = await client.guilds.fetch(guild_id);
|
const guild = await client.guilds.fetch(guild_id);
|
||||||
const fields = [];
|
const fields = [];
|
||||||
|
|
||||||
@@ -23,14 +23,14 @@ export async function handleSkinsCommand(req, res, client) {
|
|||||||
let ownerText = "Libre"; // Default text if the skin has no owner
|
let ownerText = "Libre"; // Default text if the skin has no owner
|
||||||
|
|
||||||
// If the skin has an owner, fetch their details
|
// If the skin has an owner, fetch their details
|
||||||
if (skin.user_id) {
|
if (skin.userId) {
|
||||||
try {
|
try {
|
||||||
const owner = await guild.members.fetch(skin.user_id);
|
const owner = await guild.members.fetch(skin.userId);
|
||||||
// Use globalName if available, otherwise fallback to username
|
// Use globalName if available, otherwise fallback to username
|
||||||
ownerText = `**@${owner.user.globalName || owner.user.username}** ✅`;
|
ownerText = `**@${owner.user.globalName || owner.user.username}** ✅`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// This can happen if the user has left the server
|
// This can happen if the user has left the server
|
||||||
console.warn(`Could not fetch owner for user ID: ${skin.user_id}`);
|
console.warn(`Could not fetch owner for user ID: ${skin.userId}`);
|
||||||
ownerText = "Appartient à un utilisateur inconnu";
|
ownerText = "Appartient à un utilisateur inconnu";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { formatTime, getOnlineUsersWithRole } from "../../utils/index.js";
|
|||||||
import { DiscordRequest } from "../../api/discord.js";
|
import { DiscordRequest } from "../../api/discord.js";
|
||||||
import { activePolls } from "../../game/state.js";
|
import { activePolls } from "../../game/state.js";
|
||||||
import { getSocketIo } from "../../server/socket.js";
|
import { getSocketIo } from "../../server/socket.js";
|
||||||
import { getUser } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the /timeout slash command.
|
* Handles the /timeout slash command.
|
||||||
@@ -102,12 +102,12 @@ export async function handleTimeoutCommand(req, res, client) {
|
|||||||
if (remaining === 0) {
|
if (remaining === 0) {
|
||||||
clearInterval(countdownInterval);
|
clearInterval(countdownInterval);
|
||||||
|
|
||||||
const votersList = poll.voters
|
const votersList = (await Promise.all(poll.voters
|
||||||
.map((voterId) => {
|
.map(async (voterId) => {
|
||||||
const user = getUser.get(voterId);
|
const user = await userService.getUser(voterId);
|
||||||
return `- ${user?.globalName || "Utilisateur Inconnu"}`;
|
return `- ${user?.globalName || "Utilisateur Inconnu"}`;
|
||||||
})
|
})
|
||||||
.join("\n");
|
)).join("\n");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await DiscordRequest(poll.endpoint, {
|
await DiscordRequest(poll.endpoint, {
|
||||||
@@ -143,12 +143,12 @@ export async function handleTimeoutCommand(req, res, client) {
|
|||||||
// --- Periodic Update Logic ---
|
// --- Periodic Update Logic ---
|
||||||
// Update the message every second with the new countdown
|
// Update the message every second with the new countdown
|
||||||
try {
|
try {
|
||||||
const votersList = poll.voters
|
const votersList = (await Promise.all(poll.voters
|
||||||
.map((voterId) => {
|
.map(async (voterId) => {
|
||||||
const user = getUser.get(voterId);
|
const user = await userService.getUser(voterId);
|
||||||
return `- ${user?.globalName || "Utilisateur Inconnu"}`;
|
return `- ${user?.globalName || "Utilisateur Inconnu"}`;
|
||||||
})
|
})
|
||||||
.join("\n");
|
)).join("\n");
|
||||||
|
|
||||||
await DiscordRequest(poll.endpoint, {
|
await DiscordRequest(poll.endpoint, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { InteractionResponseFlags, InteractionResponseType } from "discord-interactions";
|
import { InteractionResponseFlags, InteractionResponseType } from "discord-interactions";
|
||||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
|
||||||
import { DiscordRequest } from "../../api/discord.js";
|
import { DiscordRequest } from "../../api/discord.js";
|
||||||
import { getAllAvailableSkins, getUser, insertLog, updateSkin, updateUserCoins } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
import * as skinService from "../../services/skin.service.js";
|
||||||
|
import * as logService from "../../services/log.service.js";
|
||||||
import { skins } from "../../game/state.js";
|
import { skins } from "../../game/state.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,7 +29,7 @@ export async function handleValorantCommand(req, res, client) {
|
|||||||
try {
|
try {
|
||||||
// --- 1. Verify and process payment ---
|
// --- 1. Verify and process payment ---
|
||||||
|
|
||||||
const commandUser = getUser.get(userId);
|
const commandUser = await userService.getUser(userId);
|
||||||
if (!commandUser) {
|
if (!commandUser) {
|
||||||
return res.send({
|
return res.send({
|
||||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||||
@@ -47,18 +49,15 @@ export async function handleValorantCommand(req, res, client) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-${Date.now()}`,
|
id: `${userId}-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
action: "VALO_CASE_OPEN",
|
action: "VALO_CASE_OPEN",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: -valoPrice,
|
coinsAmount: -valoPrice,
|
||||||
user_new_amount: commandUser.coins - valoPrice,
|
userNewAmount: commandUser.coins - valoPrice,
|
||||||
});
|
|
||||||
updateUserCoins.run({
|
|
||||||
id: userId,
|
|
||||||
coins: commandUser.coins - valoPrice,
|
|
||||||
});
|
});
|
||||||
|
await userService.updateUserCoins(userId, commandUser.coins - valoPrice);
|
||||||
|
|
||||||
// --- 2. Send Initial "Opening" Response ---
|
// --- 2. Send Initial "Opening" Response ---
|
||||||
// Acknowledge the interaction immediately with a loading message.
|
// Acknowledge the interaction immediately with a loading message.
|
||||||
@@ -77,7 +76,7 @@ export async function handleValorantCommand(req, res, client) {
|
|||||||
const webhookEndpoint = `webhooks/${process.env.APP_ID}/${token}/messages/@original`;
|
const webhookEndpoint = `webhooks/${process.env.APP_ID}/${token}/messages/@original`;
|
||||||
try {
|
try {
|
||||||
// --- Skin Selection ---
|
// --- Skin Selection ---
|
||||||
const availableSkins = getAllAvailableSkins.all();
|
const availableSkins = await skinService.getAllAvailableSkins();
|
||||||
if (availableSkins.length === 0) {
|
if (availableSkins.length === 0) {
|
||||||
throw new Error("No available skins to award.");
|
throw new Error("No available skins to award.");
|
||||||
}
|
}
|
||||||
@@ -105,9 +104,9 @@ export async function handleValorantCommand(req, res, client) {
|
|||||||
const finalPrice = calculatePrice();
|
const finalPrice = calculatePrice();
|
||||||
|
|
||||||
// --- Update Database ---
|
// --- Update Database ---
|
||||||
await updateSkin.run({
|
await skinService.updateSkin({
|
||||||
uuid: randomSkinData.uuid,
|
uuid: randomSkinData.uuid,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
currentLvl: randomLevel,
|
currentLvl: randomLevel,
|
||||||
currentChroma: randomChroma,
|
currentChroma: randomChroma,
|
||||||
currentPrice: finalPrice,
|
currentPrice: finalPrice,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { InteractionResponseType, InteractionResponseFlags } from "discord-inter
|
|||||||
import { DiscordRequest } from "../../api/discord.js";
|
import { DiscordRequest } from "../../api/discord.js";
|
||||||
import { activePolls } from "../../game/state.js";
|
import { activePolls } from "../../game/state.js";
|
||||||
import { getSocketIo } from "../../server/socket.js";
|
import { getSocketIo } from "../../server/socket.js";
|
||||||
import { getUser } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles clicks on the 'Yes' or 'No' buttons of a timeout poll.
|
* Handles clicks on the 'Yes' or 'No' buttons of a timeout poll.
|
||||||
@@ -75,7 +75,10 @@ export async function handlePollVote(req, res) {
|
|||||||
|
|
||||||
io.emit("poll-update"); // Notify frontend clients of the change
|
io.emit("poll-update"); // Notify frontend clients of the change
|
||||||
|
|
||||||
const votersList = poll.voters.map((vId) => `- ${getUser.get(vId)?.globalName || "Utilisateur Inconnu"}`).join("\n");
|
const votersList = (await Promise.all(poll.voters.map(async (vId) => {
|
||||||
|
const user = await userService.getUser(vId);
|
||||||
|
return `- ${user?.globalName || "Utilisateur Inconnu"}`;
|
||||||
|
}))).join("\n");
|
||||||
|
|
||||||
// --- 4. Check for Majority ---
|
// --- 4. Check for Majority ---
|
||||||
if (isVotingFor && poll.for >= poll.requiredMajority) {
|
if (isVotingFor && poll.for >= poll.requiredMajority) {
|
||||||
|
|||||||
@@ -65,12 +65,12 @@ export async function handleSearchNav(req, res, client) {
|
|||||||
|
|
||||||
// Fetch owner details if the skin is owned
|
// Fetch owner details if the skin is owned
|
||||||
let ownerText = "";
|
let ownerText = "";
|
||||||
if (currentSkin.user_id) {
|
if (currentSkin.userId) {
|
||||||
try {
|
try {
|
||||||
const owner = await client.users.fetch(currentSkin.user_id);
|
const owner = await client.users.fetch(currentSkin.userId);
|
||||||
ownerText = `| **@${owner.globalName || owner.username}** ✅`;
|
ownerText = `| **@${owner.globalName || owner.username}** ✅`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`Could not fetch owner for user ID: ${currentSkin.user_id}`);
|
console.warn(`Could not fetch owner for user ID: ${currentSkin.userId}`);
|
||||||
ownerText = "| Appartenant à un utilisateur inconnu";
|
ownerText = "| Appartenant à un utilisateur inconnu";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import { EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "disc
|
|||||||
import { DiscordRequest } from "../../api/discord.js";
|
import { DiscordRequest } from "../../api/discord.js";
|
||||||
import { postAPOBuy } from "../../utils/index.js";
|
import { postAPOBuy } from "../../utils/index.js";
|
||||||
import { activeInventories, skins } from "../../game/state.js";
|
import { activeInventories, skins } from "../../game/state.js";
|
||||||
import { getSkin, getUser, insertLog, updateSkin, updateUserCoins } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
import * as skinService from "../../services/skin.service.js";
|
||||||
|
import * as logService from "../../services/log.service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the click of the 'Upgrade' button on a skin in the inventory.
|
* Handles the click of the 'Upgrade' button on a skin in the inventory.
|
||||||
@@ -65,7 +67,7 @@ export async function handleUpgradeSkin(req, res) {
|
|||||||
// --- 2. Handle Payment ---
|
// --- 2. Handle Payment ---
|
||||||
const upgradePrice = parseFloat(process.env.VALO_UPGRADE_PRICE) || parseFloat(skinToUpgrade.maxPrice) / 10;
|
const upgradePrice = parseFloat(process.env.VALO_UPGRADE_PRICE) || parseFloat(skinToUpgrade.maxPrice) / 10;
|
||||||
|
|
||||||
const commandUser = getUser.get(userId);
|
const commandUser = await userService.getUser(userId);
|
||||||
|
|
||||||
if (!commandUser) {
|
if (!commandUser) {
|
||||||
return res.send({
|
return res.send({
|
||||||
@@ -86,18 +88,15 @@ export async function handleUpgradeSkin(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-${Date.now()}`,
|
id: `${userId}-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
action: "VALO_SKIN_UPGRADE",
|
action: "VALO_SKIN_UPGRADE",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: -upgradePrice.toFixed(0),
|
coinsAmount: -upgradePrice.toFixed(0),
|
||||||
user_new_amount: commandUser.coins - upgradePrice.toFixed(0),
|
userNewAmount: commandUser.coins - upgradePrice.toFixed(0),
|
||||||
});
|
|
||||||
updateUserCoins.run({
|
|
||||||
id: userId,
|
|
||||||
coins: commandUser.coins - upgradePrice.toFixed(0),
|
|
||||||
});
|
});
|
||||||
|
await userService.updateUserCoins(userId, commandUser.coins - upgradePrice.toFixed(0));
|
||||||
|
|
||||||
// --- 3. Show Loading Animation ---
|
// --- 3. Show Loading Animation ---
|
||||||
// Acknowledge the click immediately and then edit the message to show a loading state.
|
// Acknowledge the click immediately and then edit the message to show a loading state.
|
||||||
@@ -151,9 +150,9 @@ export async function handleUpgradeSkin(req, res) {
|
|||||||
};
|
};
|
||||||
skinToUpgrade.currentPrice = calculatePrice();
|
skinToUpgrade.currentPrice = calculatePrice();
|
||||||
|
|
||||||
await updateSkin.run({
|
await skinService.updateSkin({
|
||||||
uuid: skinToUpgrade.uuid,
|
uuid: skinToUpgrade.uuid,
|
||||||
user_id: skinToUpgrade.user_id,
|
userId: skinToUpgrade.userId,
|
||||||
currentLvl: skinToUpgrade.currentLvl,
|
currentLvl: skinToUpgrade.currentLvl,
|
||||||
currentChroma: skinToUpgrade.currentChroma,
|
currentChroma: skinToUpgrade.currentChroma,
|
||||||
currentPrice: skinToUpgrade.currentPrice,
|
currentPrice: skinToUpgrade.currentPrice,
|
||||||
@@ -165,7 +164,7 @@ export async function handleUpgradeSkin(req, res) {
|
|||||||
// --- 6. Send Final Result ---
|
// --- 6. Send Final Result ---
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// Fetch the latest state of the skin from the database
|
// Fetch the latest state of the skin from the database
|
||||||
const finalSkinState = getSkin.get(skinToUpgrade.uuid);
|
const finalSkinState = await skinService.getSkin(skinToUpgrade.uuid);
|
||||||
const finalEmbed = buildFinalEmbed(succeeded, finalSkinState, skinData);
|
const finalEmbed = buildFinalEmbed(succeeded, finalSkinState, skinData);
|
||||||
const finalComponents = buildFinalComponents(succeeded, skinData, finalSkinState, interactionId);
|
const finalComponents = buildFinalComponents(succeeded, skinData, finalSkinState, interactionId);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { sleep } from "openai/core";
|
import { sleep } from "openai/core";
|
||||||
|
import { AttachmentBuilder } from "discord.js";
|
||||||
import {
|
import {
|
||||||
buildAiMessages,
|
buildAiMessages,
|
||||||
buildParticipantsMap,
|
buildParticipantsMap,
|
||||||
@@ -12,18 +13,10 @@ import {
|
|||||||
import { calculateBasePrice, calculateMaxPrice, formatTime, getAkhys } from "../../utils/index.js";
|
import { calculateBasePrice, calculateMaxPrice, formatTime, getAkhys } from "../../utils/index.js";
|
||||||
import { channelPointsHandler, initTodaysSOTD, randomSkinPrice, slowmodesHandler } from "../../game/points.js";
|
import { channelPointsHandler, initTodaysSOTD, randomSkinPrice, slowmodesHandler } from "../../game/points.js";
|
||||||
import { activePolls, activeSlowmodes, requestTimestamps, skins } from "../../game/state.js";
|
import { activePolls, activeSlowmodes, requestTimestamps, skins } from "../../game/state.js";
|
||||||
import {
|
import prisma from "../../prisma/client.js";
|
||||||
flopoDB,
|
import * as userService from "../../services/user.service.js";
|
||||||
getAllSkins,
|
import * as skinService from "../../services/skin.service.js";
|
||||||
getAllUsers,
|
import * as logService from "../../services/log.service.js";
|
||||||
getUser,
|
|
||||||
hardUpdateSkin,
|
|
||||||
insertLog,
|
|
||||||
updateManyUsers,
|
|
||||||
updateSkin,
|
|
||||||
updateUserAvatar,
|
|
||||||
updateUserCoins,
|
|
||||||
} from "../../database/index.js";
|
|
||||||
import { client } from "../client.js";
|
import { client } from "../client.js";
|
||||||
import { drawCaseContent, drawCaseSkin, getDummySkinUpgradeProbs } from "../../utils/caseOpening.js";
|
import { drawCaseContent, drawCaseSkin, getDummySkinUpgradeProbs } from "../../utils/caseOpening.js";
|
||||||
|
|
||||||
@@ -52,10 +45,10 @@ export async function handleMessageCreate(message, client, io) {
|
|||||||
// --- Main Guild Features (Points & Slowmode) ---
|
// --- Main Guild Features (Points & Slowmode) ---
|
||||||
if (message.guildId === process.env.GUILD_ID) {
|
if (message.guildId === process.env.GUILD_ID) {
|
||||||
// Award points for activity
|
// Award points for activity
|
||||||
const pointsAwarded = channelPointsHandler(message);
|
// const pointsAwarded = channelPointsHandler(message);
|
||||||
if (pointsAwarded) {
|
// if (pointsAwarded) {
|
||||||
io.emit("data-updated", { table: "users", action: "update" });
|
// io.emit("data-updated", { table: "users", action: "update" });
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Enforce active slowmodes
|
// Enforce active slowmodes
|
||||||
const wasSlowmoded = await slowmodesHandler(message, activeSlowmodes);
|
const wasSlowmoded = await slowmodesHandler(message, activeSlowmodes);
|
||||||
@@ -88,7 +81,7 @@ export async function handleMessageCreate(message, client, io) {
|
|||||||
// --- Sub-handler for AI Logic ---
|
// --- Sub-handler for AI Logic ---
|
||||||
async function handleAiMention(message, client, io) {
|
async function handleAiMention(message, client, io) {
|
||||||
const authorId = message.author.id;
|
const authorId = message.author.id;
|
||||||
let authorDB = getUser.get(authorId);
|
let authorDB = await userService.getUser(authorId);
|
||||||
if (!authorDB) return; // Should not happen if user is in DB, but good practice
|
if (!authorDB) return; // Should not happen if user is in DB, but good practice
|
||||||
|
|
||||||
// --- Rate Limiting ---
|
// --- Rate Limiting ---
|
||||||
@@ -104,7 +97,7 @@ async function handleAiMention(message, client, io) {
|
|||||||
authorDB.warned = 1;
|
authorDB.warned = 1;
|
||||||
authorDB.warns += 1;
|
authorDB.warns += 1;
|
||||||
authorDB.allTimeWarns += 1;
|
authorDB.allTimeWarns += 1;
|
||||||
updateManyUsers([authorDB]);
|
await userService.updateManyUsers([authorDB]);
|
||||||
|
|
||||||
// Apply timeout if warn count is too high
|
// Apply timeout if warn count is too high
|
||||||
if (authorDB.warns > (parseInt(process.env.MAX_WARNS) || 10)) {
|
if (authorDB.warns > (parseInt(process.env.MAX_WARNS) || 10)) {
|
||||||
@@ -134,7 +127,7 @@ async function handleAiMention(message, client, io) {
|
|||||||
authorDB.warned = 0;
|
authorDB.warned = 0;
|
||||||
authorDB.warns = 0;
|
authorDB.warns = 0;
|
||||||
authorDB.totalRequests += 1;
|
authorDB.totalRequests += 1;
|
||||||
updateManyUsers([authorDB]);
|
await userService.updateManyUsers([authorDB]);
|
||||||
|
|
||||||
// --- AI Processing ---
|
// --- AI Processing ---
|
||||||
try {
|
try {
|
||||||
@@ -238,14 +231,18 @@ async function handleAdminCommands(message) {
|
|||||||
message.reply("New Solitaire of the Day initialized.");
|
message.reply("New Solitaire of the Day initialized.");
|
||||||
break;
|
break;
|
||||||
case `${prefix}:users`:
|
case `${prefix}:users`:
|
||||||
console.log(getAllUsers.all());
|
console.log(await userService.getAllUsers());
|
||||||
break;
|
break;
|
||||||
case `${prefix}:sql`:
|
case `${prefix}:sql`:
|
||||||
const sqlCommand = args.join(" ");
|
const sqlCommand = args.join(" ");
|
||||||
try {
|
try {
|
||||||
const stmt = flopoDB.prepare(sqlCommand);
|
const result = sqlCommand.trim().toUpperCase().startsWith("SELECT")
|
||||||
const result = sqlCommand.trim().toUpperCase().startsWith("SELECT") ? stmt.all() : stmt.run();
|
? await prisma.$queryRawUnsafe(sqlCommand)
|
||||||
message.reply("```json\n" + JSON.stringify(result, null, 2).substring(0, 1900) + "\n```");
|
: await prisma.$executeRawUnsafe(sqlCommand);
|
||||||
|
const jsonString = JSON.stringify(result, null, 2);
|
||||||
|
const buffer = Buffer.from(jsonString, "utf-8");
|
||||||
|
const attachment = new AttachmentBuilder(buffer, { name: "sql-result.json" });
|
||||||
|
message.reply({ content: "SQL query executed successfully:", files: [attachment] });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message.reply(`SQL Error: ${e.message}`);
|
message.reply(`SQL Error: ${e.message}`);
|
||||||
}
|
}
|
||||||
@@ -263,16 +260,16 @@ async function handleAdminCommands(message) {
|
|||||||
avatarUrl: akhy.user.displayAvatarURL({ dynamic: true, size: 256 }),
|
avatarUrl: akhy.user.displayAvatarURL({ dynamic: true, size: 256 }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
usersToUpdate.forEach((user) => {
|
for (const user of usersToUpdate) {
|
||||||
try {
|
try {
|
||||||
updateUserAvatar.run(user);
|
await userService.updateUserAvatar(user.id, user.avatarUrl);
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
});
|
}
|
||||||
break;
|
break;
|
||||||
case `${prefix}:rework-skins`:
|
case `${prefix}:rework-skins`:
|
||||||
console.log("Reworking all skin prices...");
|
console.log("Reworking all skin prices...");
|
||||||
const dbSkins = getAllSkins.all();
|
const dbSkins = await skinService.getAllSkins();
|
||||||
dbSkins.forEach((skin) => {
|
for (const skin of dbSkins) {
|
||||||
const fetchedSkin = skins.find((s) => s.uuid === skin.uuid);
|
const fetchedSkin = skins.find((s) => s.uuid === skin.uuid);
|
||||||
const basePrice = calculateBasePrice(fetchedSkin, skin.tierRank)?.toFixed(0);
|
const basePrice = calculateBasePrice(fetchedSkin, skin.tierRank)?.toFixed(0);
|
||||||
const calculatePrice = () => {
|
const calculatePrice = () => {
|
||||||
@@ -283,12 +280,12 @@ async function handleAdminCommands(message) {
|
|||||||
return parseFloat(result.toFixed(0));
|
return parseFloat(result.toFixed(0));
|
||||||
};
|
};
|
||||||
const maxPrice = calculateMaxPrice(basePrice, fetchedSkin).toFixed(0);
|
const maxPrice = calculateMaxPrice(basePrice, fetchedSkin).toFixed(0);
|
||||||
hardUpdateSkin.run({
|
await skinService.hardUpdateSkin({
|
||||||
uuid: skin.uuid,
|
uuid: skin.uuid,
|
||||||
displayName: skin.displayName,
|
displayName: skin.displayName,
|
||||||
contentTierUuid: skin.contentTierUuid,
|
contentTierUuid: skin.contentTierUuid,
|
||||||
displayIcon: skin.displayIcon,
|
displayIcon: skin.displayIcon,
|
||||||
user_id: skin.user_id,
|
userId: skin.userId,
|
||||||
tierRank: skin.tierRank,
|
tierRank: skin.tierRank,
|
||||||
tierColor: skin.tierColor,
|
tierColor: skin.tierColor,
|
||||||
tierText: skin.tierText,
|
tierText: skin.tierText,
|
||||||
@@ -298,7 +295,7 @@ async function handleAdminCommands(message) {
|
|||||||
currentPrice: skin.currentPrice ? calculatePrice() : null,
|
currentPrice: skin.currentPrice ? calculatePrice() : null,
|
||||||
maxPrice: maxPrice,
|
maxPrice: maxPrice,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
console.log("Reworked", dbSkins.length, "skins.");
|
console.log("Reworked", dbSkins.length, "skins.");
|
||||||
break;
|
break;
|
||||||
case `${prefix}:cases-test`:
|
case `${prefix}:cases-test`:
|
||||||
@@ -324,7 +321,7 @@ async function handleAdminCommands(message) {
|
|||||||
|
|
||||||
for (let i = 0; i < caseCount; i++) {
|
for (let i = 0; i < caseCount; i++) {
|
||||||
const skins = await drawCaseContent(caseType);
|
const skins = await drawCaseContent(caseType);
|
||||||
const result = drawCaseSkin(skins);
|
const result = await drawCaseSkin(skins);
|
||||||
totalResValue += result.finalPrice;
|
totalResValue += result.finalPrice;
|
||||||
if (result.finalPrice > highestSkinPrice) highestSkinPrice = result.finalPrice;
|
if (result.finalPrice > highestSkinPrice) highestSkinPrice = result.finalPrice;
|
||||||
if (result.finalPrice > 0 && result.finalPrice < 100) priceTiers["0"] += 1;
|
if (result.finalPrice > 0 && result.finalPrice < 100) priceTiers["0"] += 1;
|
||||||
@@ -354,26 +351,23 @@ async function handleAdminCommands(message) {
|
|||||||
break;
|
break;
|
||||||
case `${prefix}:refund-skins`:
|
case `${prefix}:refund-skins`:
|
||||||
try {
|
try {
|
||||||
const DBskins = getAllSkins.all();
|
const DBskins = await skinService.getAllSkins();
|
||||||
for (const skin of DBskins) {
|
for (const skin of DBskins) {
|
||||||
const owner = getUser.get(skin.user_id);
|
const owner = await userService.getUser(skin.userId);
|
||||||
if (owner) {
|
if (owner) {
|
||||||
updateUserCoins.run({
|
await userService.updateUserCoins(owner.id, owner.coins + skin.currentPrice);
|
||||||
id: owner.id,
|
await logService.insertLog({
|
||||||
coins: owner.coins + skin.currentPrice,
|
|
||||||
});
|
|
||||||
insertLog.run({
|
|
||||||
id: `${skin.uuid}-skin-refund-${Date.now()}`,
|
id: `${skin.uuid}-skin-refund-${Date.now()}`,
|
||||||
user_id: owner.id,
|
userId: owner.id,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "SKIN_REFUND",
|
action: "SKIN_REFUND",
|
||||||
coins_amount: skin.currentPrice,
|
coinsAmount: skin.currentPrice,
|
||||||
user_new_amount: owner.coins + skin.currentPrice,
|
userNewAmount: owner.coins + skin.currentPrice,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateSkin.run({
|
await skinService.updateSkin({
|
||||||
uuid: skin.uuid,
|
uuid: skin.uuid,
|
||||||
user_id: null,
|
userId: null,
|
||||||
currentPrice: null,
|
currentPrice: null,
|
||||||
currentLvl: null,
|
currentLvl: null,
|
||||||
currentChroma: null,
|
currentChroma: null,
|
||||||
|
|||||||
@@ -1,863 +0,0 @@
|
|||||||
import Database from "better-sqlite3";
|
|
||||||
|
|
||||||
export const flopoDB = new Database(process.env.DB_PATH || "flopobot.db");
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
CREATE ALL TABLES FIRST
|
|
||||||
----------------------------*/
|
|
||||||
flopoDB.exec(`
|
|
||||||
CREATE TABLE IF NOT EXISTS users
|
|
||||||
(
|
|
||||||
id
|
|
||||||
TEXT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
username
|
|
||||||
TEXT
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
globalName
|
|
||||||
TEXT,
|
|
||||||
warned
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
warns
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
allTimeWarns
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
totalRequests
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
coins
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
dailyQueried
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
avatarUrl
|
|
||||||
TEXT
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
isAkhy
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
0
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS skins
|
|
||||||
(
|
|
||||||
uuid
|
|
||||||
TEXT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
displayName
|
|
||||||
TEXT,
|
|
||||||
contentTierUuid
|
|
||||||
TEXT,
|
|
||||||
displayIcon
|
|
||||||
TEXT,
|
|
||||||
user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
tierRank
|
|
||||||
TEXT,
|
|
||||||
tierColor
|
|
||||||
TEXT,
|
|
||||||
tierText
|
|
||||||
TEXT,
|
|
||||||
basePrice
|
|
||||||
TEXT,
|
|
||||||
currentLvl
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
currentChroma
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
currentPrice
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
maxPrice
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS market_offers
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
skin_uuid
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
skins,
|
|
||||||
seller_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
starting_price
|
|
||||||
INTEGER
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
buyout_price
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
final_price
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
status
|
|
||||||
TEXT
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
posted_at
|
|
||||||
TIMESTAMP
|
|
||||||
DEFAULT
|
|
||||||
CURRENT_TIMESTAMP,
|
|
||||||
opening_at
|
|
||||||
TIMESTAMP
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
closing_at
|
|
||||||
TIMESTAMP
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
buyer_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users
|
|
||||||
DEFAULT
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS bids
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
bidder_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
market_offer_id
|
|
||||||
REFERENCES
|
|
||||||
market_offers,
|
|
||||||
offer_amount
|
|
||||||
INTEGER,
|
|
||||||
offered_at
|
|
||||||
TIMESTAMP
|
|
||||||
DEFAULT
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS logs
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
action
|
|
||||||
TEXT,
|
|
||||||
target_user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
coins_amount
|
|
||||||
INTEGER,
|
|
||||||
user_new_amount
|
|
||||||
INTEGER,
|
|
||||||
created_at
|
|
||||||
TIMESTAMP
|
|
||||||
DEFAULT
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS games
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
p1
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
p2
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
p1_score
|
|
||||||
INTEGER,
|
|
||||||
p2_score
|
|
||||||
INTEGER,
|
|
||||||
p1_elo
|
|
||||||
INTEGER,
|
|
||||||
p2_elo
|
|
||||||
INTEGER,
|
|
||||||
p1_new_elo
|
|
||||||
INTEGER,
|
|
||||||
p2_new_elo
|
|
||||||
INTEGER,
|
|
||||||
type
|
|
||||||
TEXT,
|
|
||||||
timestamp
|
|
||||||
TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS elos
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
elo
|
|
||||||
INTEGER
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sotd
|
|
||||||
(
|
|
||||||
id
|
|
||||||
INT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
tableauPiles
|
|
||||||
TEXT,
|
|
||||||
foundationPiles
|
|
||||||
TEXT,
|
|
||||||
stockPile
|
|
||||||
TEXT,
|
|
||||||
wastePile
|
|
||||||
TEXT,
|
|
||||||
isDone
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
false,
|
|
||||||
seed
|
|
||||||
TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sotd_stats
|
|
||||||
(
|
|
||||||
id
|
|
||||||
TEXT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
time
|
|
||||||
INTEGER,
|
|
||||||
moves
|
|
||||||
INTEGER,
|
|
||||||
score
|
|
||||||
INTEGER
|
|
||||||
);
|
|
||||||
`);
|
|
||||||
|
|
||||||
/* -----------------------------------------------------
|
|
||||||
PREPARE ANY CREATE TABLE STATEMENT OBJECTS (kept for parity)
|
|
||||||
------------------------------------------------------*/
|
|
||||||
|
|
||||||
export const stmtUsers = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS users
|
|
||||||
(
|
|
||||||
id
|
|
||||||
TEXT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
username
|
|
||||||
TEXT
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
globalName
|
|
||||||
TEXT,
|
|
||||||
warned
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
warns
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
allTimeWarns
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
totalRequests
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
coins
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
dailyQueried
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
0,
|
|
||||||
avatarUrl
|
|
||||||
TEXT
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
isAkhy
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
0
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtUsers.run();
|
|
||||||
|
|
||||||
export const stmtSkins = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS skins
|
|
||||||
(
|
|
||||||
uuid
|
|
||||||
TEXT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
displayName
|
|
||||||
TEXT,
|
|
||||||
contentTierUuid
|
|
||||||
TEXT,
|
|
||||||
displayIcon
|
|
||||||
TEXT,
|
|
||||||
user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
tierRank
|
|
||||||
TEXT,
|
|
||||||
tierColor
|
|
||||||
TEXT,
|
|
||||||
tierText
|
|
||||||
TEXT,
|
|
||||||
basePrice
|
|
||||||
TEXT,
|
|
||||||
currentLvl
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
currentChroma
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
currentPrice
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
maxPrice
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtSkins.run();
|
|
||||||
|
|
||||||
export const stmtMarketOffers = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS market_offers
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
skin_uuid
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
skins,
|
|
||||||
seller_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
starting_price
|
|
||||||
INTEGER
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
buyout_price
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
final_price
|
|
||||||
INTEGER
|
|
||||||
DEFAULT
|
|
||||||
NULL,
|
|
||||||
status
|
|
||||||
TEXT
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
posted_at
|
|
||||||
TIMESTAMP
|
|
||||||
DEFAULT
|
|
||||||
CURRENT_TIMESTAMP,
|
|
||||||
opening_at
|
|
||||||
TIMESTAMP
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
closing_at
|
|
||||||
TIMESTAMP
|
|
||||||
NOT
|
|
||||||
NULL,
|
|
||||||
buyer_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users
|
|
||||||
DEFAULT
|
|
||||||
NULL
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtMarketOffers.run();
|
|
||||||
|
|
||||||
export const stmtBids = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS bids
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
bidder_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
market_offer_id
|
|
||||||
REFERENCES
|
|
||||||
market_offers,
|
|
||||||
offer_amount
|
|
||||||
INTEGER,
|
|
||||||
offered_at
|
|
||||||
TIMESTAMP
|
|
||||||
DEFAULT
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtBids.run();
|
|
||||||
|
|
||||||
export const stmtLogs = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS logs
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
action
|
|
||||||
TEXT,
|
|
||||||
target_user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
coins_amount
|
|
||||||
INTEGER,
|
|
||||||
user_new_amount
|
|
||||||
INTEGER,
|
|
||||||
created_at
|
|
||||||
TIMESTAMP
|
|
||||||
DEFAULT
|
|
||||||
CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtLogs.run();
|
|
||||||
|
|
||||||
export const stmtGames = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS games
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
p1
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
p2
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
p1_score
|
|
||||||
INTEGER,
|
|
||||||
p2_score
|
|
||||||
INTEGER,
|
|
||||||
p1_elo
|
|
||||||
INTEGER,
|
|
||||||
p2_elo
|
|
||||||
INTEGER,
|
|
||||||
p1_new_elo
|
|
||||||
INTEGER,
|
|
||||||
p2_new_elo
|
|
||||||
INTEGER,
|
|
||||||
type
|
|
||||||
TEXT,
|
|
||||||
timestamp
|
|
||||||
TIMESTAMP
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtGames.run();
|
|
||||||
|
|
||||||
export const stmtElos = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS elos
|
|
||||||
(
|
|
||||||
id
|
|
||||||
PRIMARY
|
|
||||||
KEY
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
elo
|
|
||||||
INTEGER
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtElos.run();
|
|
||||||
|
|
||||||
export const stmtSOTD = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS sotd
|
|
||||||
(
|
|
||||||
id
|
|
||||||
INT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
tableauPiles
|
|
||||||
TEXT,
|
|
||||||
foundationPiles
|
|
||||||
TEXT,
|
|
||||||
stockPile
|
|
||||||
TEXT,
|
|
||||||
wastePile
|
|
||||||
TEXT,
|
|
||||||
isDone
|
|
||||||
BOOLEAN
|
|
||||||
DEFAULT
|
|
||||||
false,
|
|
||||||
seed
|
|
||||||
TEXT
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtSOTD.run();
|
|
||||||
|
|
||||||
export const stmtSOTDStats = flopoDB.prepare(`
|
|
||||||
CREATE TABLE IF NOT EXISTS sotd_stats
|
|
||||||
(
|
|
||||||
id
|
|
||||||
TEXT
|
|
||||||
PRIMARY
|
|
||||||
KEY,
|
|
||||||
user_id
|
|
||||||
TEXT
|
|
||||||
REFERENCES
|
|
||||||
users,
|
|
||||||
time
|
|
||||||
INTEGER,
|
|
||||||
moves
|
|
||||||
INTEGER,
|
|
||||||
score
|
|
||||||
INTEGER
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
stmtSOTDStats.run();
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
USER statements
|
|
||||||
----------------------------*/
|
|
||||||
export const insertUser = flopoDB.prepare(
|
|
||||||
`INSERT INTO users (id, username, globalName, warned, warns, allTimeWarns, totalRequests, avatarUrl, isAkhy)
|
|
||||||
VALUES (@id, @username, @globalName, @warned, @warns, @allTimeWarns, @totalRequests, @avatarUrl, @isAkhy)`,
|
|
||||||
);
|
|
||||||
export const updateUser = flopoDB.prepare(
|
|
||||||
`UPDATE users
|
|
||||||
SET warned = @warned,
|
|
||||||
warns = @warns,
|
|
||||||
allTimeWarns = @allTimeWarns,
|
|
||||||
totalRequests = @totalRequests
|
|
||||||
WHERE id = @id`,
|
|
||||||
);
|
|
||||||
export const updateUserAvatar = flopoDB.prepare("UPDATE users SET avatarUrl = @avatarUrl WHERE id = @id");
|
|
||||||
export const queryDailyReward = flopoDB.prepare(`UPDATE users
|
|
||||||
SET dailyQueried = 1
|
|
||||||
WHERE id = ?`
|
|
||||||
);
|
|
||||||
export const resetDailyReward = flopoDB.prepare(`UPDATE users
|
|
||||||
SET dailyQueried = 0`
|
|
||||||
);
|
|
||||||
export const updateUserCoins = flopoDB.prepare("UPDATE users SET coins = @coins WHERE id = @id");
|
|
||||||
export const getUser = flopoDB.prepare(
|
|
||||||
"SELECT users.*,elos.elo FROM users LEFT JOIN elos ON elos.id = users.id WHERE users.id = ?",
|
|
||||||
);
|
|
||||||
export const getAllUsers = flopoDB.prepare(
|
|
||||||
"SELECT users.*,elos.elo FROM users LEFT JOIN elos ON elos.id = users.id ORDER BY coins DESC",
|
|
||||||
);
|
|
||||||
export const getAllAkhys = flopoDB.prepare(
|
|
||||||
"SELECT users.*,elos.elo FROM users LEFT JOIN elos ON elos.id = users.id WHERE isAkhy = 1 ORDER BY coins DESC",
|
|
||||||
);
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
SKINS statements
|
|
||||||
----------------------------*/
|
|
||||||
export const insertSkin = flopoDB.prepare(
|
|
||||||
`INSERT INTO skins (uuid, displayName, contentTierUuid, displayIcon, user_id, tierRank, tierColor, tierText,
|
|
||||||
basePrice, maxPrice)
|
|
||||||
VALUES (@uuid, @displayName, @contentTierUuid, @displayIcon, @user_id, @tierRank, @tierColor, @tierText,
|
|
||||||
@basePrice, @maxPrice)`,
|
|
||||||
);
|
|
||||||
export const updateSkin = flopoDB.prepare(
|
|
||||||
`UPDATE skins
|
|
||||||
SET user_id = @user_id,
|
|
||||||
currentLvl = @currentLvl,
|
|
||||||
currentChroma = @currentChroma,
|
|
||||||
currentPrice = @currentPrice
|
|
||||||
WHERE uuid = @uuid`,
|
|
||||||
);
|
|
||||||
export const hardUpdateSkin = flopoDB.prepare(
|
|
||||||
`UPDATE skins
|
|
||||||
SET displayName = @displayName,
|
|
||||||
contentTierUuid = @contentTierUuid,
|
|
||||||
displayIcon = @displayIcon,
|
|
||||||
tierRank = @tierRank,
|
|
||||||
tierColor = @tierColor,
|
|
||||||
tierText = @tierText,
|
|
||||||
basePrice = @basePrice,
|
|
||||||
user_id = @user_id,
|
|
||||||
currentLvl = @currentLvl,
|
|
||||||
currentChroma = @currentChroma,
|
|
||||||
currentPrice = @currentPrice,
|
|
||||||
maxPrice = @maxPrice
|
|
||||||
WHERE uuid = @uuid`,
|
|
||||||
);
|
|
||||||
export const getSkin = flopoDB.prepare("SELECT * FROM skins WHERE uuid = ?");
|
|
||||||
export const getAllSkins = flopoDB.prepare("SELECT * FROM skins ORDER BY maxPrice DESC");
|
|
||||||
export const getAllAvailableSkins = flopoDB.prepare("SELECT * FROM skins WHERE user_id IS NULL");
|
|
||||||
export const getUserInventory = flopoDB.prepare(
|
|
||||||
"SELECT * FROM skins WHERE user_id = @user_id ORDER BY currentPrice DESC",
|
|
||||||
);
|
|
||||||
export const getTopSkins = flopoDB.prepare("SELECT * FROM skins ORDER BY maxPrice DESC LIMIT 10");
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
MARKET / BIDS / OFFERS
|
|
||||||
----------------------------*/
|
|
||||||
export const getMarketOffers = flopoDB.prepare(`
|
|
||||||
SELECT *
|
|
||||||
FROM market_offers
|
|
||||||
ORDER BY market_offers.posted_at DESC
|
|
||||||
`);
|
|
||||||
export const getMarketOfferById = flopoDB.prepare(`
|
|
||||||
SELECT market_offers.*,
|
|
||||||
skins.displayName AS skinName,
|
|
||||||
skins.displayIcon AS skinIcon,
|
|
||||||
seller.username AS sellerName,
|
|
||||||
seller.globalName AS sellerGlobalName,
|
|
||||||
buyer.username AS buyerName,
|
|
||||||
buyer.globalName AS buyerGlobalName
|
|
||||||
FROM market_offers
|
|
||||||
JOIN skins ON skins.uuid = market_offers.skin_uuid
|
|
||||||
JOIN users AS seller ON seller.id = market_offers.seller_id
|
|
||||||
LEFT JOIN users AS buyer ON buyer.id = market_offers.buyer_id
|
|
||||||
WHERE market_offers.id = ?
|
|
||||||
`);
|
|
||||||
export const getMarketOffersBySkin = flopoDB.prepare(`
|
|
||||||
SELECT market_offers.*,
|
|
||||||
skins.displayName AS skinName,
|
|
||||||
skins.displayIcon AS skinIcon,
|
|
||||||
seller.username AS sellerName,
|
|
||||||
seller.globalName AS sellerGlobalName,
|
|
||||||
buyer.username AS buyerName,
|
|
||||||
buyer.globalName AS buyerGlobalName
|
|
||||||
FROM market_offers
|
|
||||||
JOIN skins ON skins.uuid = market_offers.skin_uuid
|
|
||||||
JOIN users AS seller ON seller.id = market_offers.seller_id
|
|
||||||
LEFT JOIN users AS buyer ON buyer.id = market_offers.buyer_id
|
|
||||||
WHERE market_offers.skin_uuid = ?
|
|
||||||
`);
|
|
||||||
export const insertMarketOffer = flopoDB.prepare(`
|
|
||||||
INSERT INTO market_offers (id, skin_uuid, seller_id, starting_price, buyout_price, status, opening_at, closing_at)
|
|
||||||
VALUES (@id, @skin_uuid, @seller_id, @starting_price, @buyout_price, @status, @opening_at, @closing_at)
|
|
||||||
`);
|
|
||||||
export const updateMarketOffer = flopoDB.prepare(`
|
|
||||||
UPDATE market_offers
|
|
||||||
SET final_price = @final_price,
|
|
||||||
status = @status,
|
|
||||||
buyer_id = @buyer_id
|
|
||||||
WHERE id = @id
|
|
||||||
`);
|
|
||||||
export const deleteMarketOffer = flopoDB.prepare(`
|
|
||||||
DELETE
|
|
||||||
FROM market_offers
|
|
||||||
WHERE id = ?
|
|
||||||
`);
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
BIDS
|
|
||||||
----------------------------*/
|
|
||||||
export const getBids = flopoDB.prepare(`
|
|
||||||
SELECT bids.*,
|
|
||||||
bidder.username AS bidderName,
|
|
||||||
bidder.globalName AS bidderGlobalName
|
|
||||||
FROM bids
|
|
||||||
JOIN users AS bidder ON bidder.id = bids.bidder_id
|
|
||||||
ORDER BY bids.offer_amount DESC, bids.offered_at ASC
|
|
||||||
`);
|
|
||||||
export const getBidById = flopoDB.prepare(`
|
|
||||||
SELECT bids.*
|
|
||||||
FROM bids
|
|
||||||
WHERE bids.id = ?
|
|
||||||
`);
|
|
||||||
export const getOfferBids = flopoDB.prepare(`
|
|
||||||
SELECT bids.*
|
|
||||||
FROM bids
|
|
||||||
WHERE bids.market_offer_id = ?
|
|
||||||
ORDER BY bids.offer_amount DESC, bids.offered_at ASC
|
|
||||||
`);
|
|
||||||
export const insertBid = flopoDB.prepare(`
|
|
||||||
INSERT INTO bids (id, bidder_id, market_offer_id, offer_amount)
|
|
||||||
VALUES (@id, @bidder_id, @market_offer_id, @offer_amount)
|
|
||||||
`);
|
|
||||||
export const deleteBid = flopoDB.prepare(`
|
|
||||||
DELETE
|
|
||||||
FROM bids
|
|
||||||
WHERE id = ?
|
|
||||||
`);
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
BULK TRANSACTIONS (synchronous)
|
|
||||||
----------------------------*/
|
|
||||||
export const insertManyUsers = flopoDB.transaction((users) => {
|
|
||||||
for (const user of users)
|
|
||||||
try {
|
|
||||||
insertUser.run(user);
|
|
||||||
} catch (e) {}
|
|
||||||
});
|
|
||||||
export const updateManyUsers = flopoDB.transaction((users) => {
|
|
||||||
for (const user of users)
|
|
||||||
try {
|
|
||||||
updateUser.run(user);
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`User update failed`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
export const insertManySkins = flopoDB.transaction((skins) => {
|
|
||||||
for (const skin of skins)
|
|
||||||
try {
|
|
||||||
insertSkin.run(skin);
|
|
||||||
} catch (e) {}
|
|
||||||
});
|
|
||||||
export const updateManySkins = flopoDB.transaction((skins) => {
|
|
||||||
for (const skin of skins)
|
|
||||||
try {
|
|
||||||
updateSkin.run(skin);
|
|
||||||
} catch (e) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
LOGS
|
|
||||||
----------------------------*/
|
|
||||||
export const insertLog = flopoDB.prepare(
|
|
||||||
`INSERT INTO logs (id, user_id, action, target_user_id, coins_amount, user_new_amount)
|
|
||||||
VALUES (@id, @user_id, @action, @target_user_id, @coins_amount, @user_new_amount)`,
|
|
||||||
);
|
|
||||||
export const getLogs = flopoDB.prepare("SELECT * FROM logs");
|
|
||||||
export const getUserLogs = flopoDB.prepare("SELECT * FROM logs WHERE user_id = @user_id");
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
GAMES
|
|
||||||
----------------------------*/
|
|
||||||
export const insertGame = flopoDB.prepare(
|
|
||||||
`INSERT INTO games (id, p1, p2, p1_score, p2_score, p1_elo, p2_elo, p1_new_elo, p2_new_elo, type, timestamp)
|
|
||||||
VALUES (@id, @p1, @p2, @p1_score, @p2_score, @p1_elo, @p2_elo, @p1_new_elo, @p2_new_elo, @type, @timestamp)`,
|
|
||||||
);
|
|
||||||
export const getGames = flopoDB.prepare("SELECT * FROM games");
|
|
||||||
export const getUserGames = flopoDB.prepare(
|
|
||||||
"SELECT * FROM games WHERE p1 = @user_id OR p2 = @user_id ORDER BY timestamp",
|
|
||||||
);
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
ELOS
|
|
||||||
----------------------------*/
|
|
||||||
export const insertElos = flopoDB.prepare(`INSERT INTO elos (id, elo)
|
|
||||||
VALUES (@id, @elo)`
|
|
||||||
);
|
|
||||||
export const getElos = flopoDB.prepare(`SELECT *
|
|
||||||
FROM elos`
|
|
||||||
);
|
|
||||||
export const getUserElo = flopoDB.prepare(`SELECT *
|
|
||||||
FROM elos
|
|
||||||
WHERE id = @id`
|
|
||||||
);
|
|
||||||
export const updateElo = flopoDB.prepare("UPDATE elos SET elo = @elo WHERE id = @id");
|
|
||||||
export const getUsersByElo = flopoDB.prepare(
|
|
||||||
"SELECT * FROM users JOIN elos ON elos.id = users.id ORDER BY elos.elo DESC",
|
|
||||||
);
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
SOTD
|
|
||||||
----------------------------*/
|
|
||||||
export const getSOTD = flopoDB.prepare(`SELECT *
|
|
||||||
FROM sotd
|
|
||||||
WHERE id = '0'`
|
|
||||||
);
|
|
||||||
export const insertSOTD =
|
|
||||||
flopoDB.prepare(`INSERT INTO sotd (id, tableauPiles, foundationPiles, stockPile, wastePile, seed)
|
|
||||||
VALUES (0, @tableauPiles, @foundationPiles, @stockPile, @wastePile, @seed)`);
|
|
||||||
export const deleteSOTD = flopoDB.prepare(`DELETE
|
|
||||||
FROM sotd
|
|
||||||
WHERE id = '0'`
|
|
||||||
);
|
|
||||||
export const getAllSOTDStats = flopoDB.prepare(`SELECT sotd_stats.*, users.globalName
|
|
||||||
FROM sotd_stats
|
|
||||||
JOIN users ON users.id = sotd_stats.user_id
|
|
||||||
ORDER BY score DESC, moves ASC, time ASC`);
|
|
||||||
export const getUserSOTDStats = flopoDB.prepare(`SELECT *
|
|
||||||
FROM sotd_stats
|
|
||||||
WHERE user_id = ?`);
|
|
||||||
export const insertSOTDStats = flopoDB.prepare(`INSERT INTO sotd_stats (id, user_id, time, moves, score)
|
|
||||||
VALUES (@id, @user_id, @time, @moves, @score)`);
|
|
||||||
export const clearSOTDStats = flopoDB.prepare(`DELETE
|
|
||||||
FROM sotd_stats`);
|
|
||||||
export const deleteUserSOTDStats = flopoDB.prepare(`DELETE
|
|
||||||
FROM sotd_stats
|
|
||||||
WHERE user_id = ?`);
|
|
||||||
|
|
||||||
/* -------------------------
|
|
||||||
pruneOldLogs
|
|
||||||
----------------------------*/
|
|
||||||
export async function pruneOldLogs() {
|
|
||||||
const users = flopoDB
|
|
||||||
.prepare(
|
|
||||||
`
|
|
||||||
SELECT user_id
|
|
||||||
FROM logs
|
|
||||||
GROUP BY user_id
|
|
||||||
HAVING COUNT(*) > ${process.env.LOGS_BY_USER}
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.all();
|
|
||||||
|
|
||||||
const transaction = flopoDB.transaction(() => {
|
|
||||||
for (const { user_id } of users) {
|
|
||||||
flopoDB
|
|
||||||
.prepare(
|
|
||||||
`
|
|
||||||
DELETE
|
|
||||||
FROM logs
|
|
||||||
WHERE id IN (SELECT id
|
|
||||||
FROM (SELECT id,
|
|
||||||
ROW_NUMBER() OVER (ORDER BY created_at DESC) AS rn
|
|
||||||
FROM logs
|
|
||||||
WHERE user_id = ?)
|
|
||||||
WHERE rn > ${process.env.LOGS_BY_USER})
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.run(user_id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
transaction();
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
// Inspired by your poker helpers API style.
|
// Inspired by your poker helpers API style.
|
||||||
|
|
||||||
import { emitToast } from "../server/socket.js";
|
import { emitToast } from "../server/socket.js";
|
||||||
import { getUser, insertLog, updateUserCoins } from "../database/index.js";
|
import * as userService from "../services/user.service.js";
|
||||||
|
import * as logService from "../services/log.service.js";
|
||||||
import { client } from "../bot/client.js";
|
import { client } from "../bot/client.js";
|
||||||
import { EmbedBuilder } from "discord.js";
|
import { EmbedBuilder } from "discord.js";
|
||||||
|
|
||||||
@@ -299,21 +300,18 @@ export async function settleAll(room) {
|
|||||||
p.totalDelta += res.delta;
|
p.totalDelta += res.delta;
|
||||||
p.totalBets++;
|
p.totalBets++;
|
||||||
if (res.result === "win" || res.result === "push" || res.result === "blackjack") {
|
if (res.result === "win" || res.result === "push" || res.result === "blackjack") {
|
||||||
const userDB = getUser.get(p.id);
|
const userDB = await userService.getUser(p.id);
|
||||||
if (userDB) {
|
if (userDB) {
|
||||||
const coins = userDB.coins;
|
const coins = userDB.coins;
|
||||||
try {
|
try {
|
||||||
updateUserCoins.run({
|
await userService.updateUserCoins(p.id, coins + hand.bet + res.delta);
|
||||||
id: p.id,
|
await logService.insertLog({
|
||||||
coins: coins + hand.bet + res.delta,
|
|
||||||
});
|
|
||||||
insertLog.run({
|
|
||||||
id: `${p.id}-blackjack-${Date.now()}`,
|
id: `${p.id}-blackjack-${Date.now()}`,
|
||||||
user_id: p.id,
|
userId: p.id,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "BLACKJACK_PAYOUT",
|
action: "BLACKJACK_PAYOUT",
|
||||||
coins_amount: res.delta + hand.bet,
|
coinsAmount: res.delta + hand.bet,
|
||||||
user_new_amount: coins + hand.bet + res.delta,
|
userNewAmount: coins + hand.bet + res.delta,
|
||||||
});
|
});
|
||||||
p.bank = coins + hand.bet + res.delta;
|
p.bank = coins + hand.bet + res.delta;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getUser, getUserElo, insertElos, insertGame, updateElo } from "../database/index.js";
|
import * as userService from "../services/user.service.js";
|
||||||
|
import * as gameService from "../services/game.service.js";
|
||||||
import { ButtonStyle, EmbedBuilder } from "discord.js";
|
import { ButtonStyle, EmbedBuilder } from "discord.js";
|
||||||
import { client } from "../bot/client.js";
|
import { client } from "../bot/client.js";
|
||||||
|
|
||||||
@@ -12,23 +13,23 @@ import { client } from "../bot/client.js";
|
|||||||
*/
|
*/
|
||||||
export async function eloHandler(p1Id, p2Id, p1Score, p2Score, type, scores = null) {
|
export async function eloHandler(p1Id, p2Id, p1Score, p2Score, type, scores = null) {
|
||||||
// --- 1. Fetch Player Data ---
|
// --- 1. Fetch Player Data ---
|
||||||
const p1DB = getUser.get(p1Id);
|
const p1DB = await userService.getUser(p1Id);
|
||||||
const p2DB = getUser.get(p2Id);
|
const p2DB = await userService.getUser(p2Id);
|
||||||
if (!p1DB || !p2DB) {
|
if (!p1DB || !p2DB) {
|
||||||
console.error(`Elo Handler: Could not find user data for ${p1Id} or ${p2Id}.`);
|
console.error(`Elo Handler: Could not find user data for ${p1Id} or ${p2Id}.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let p1EloData = getUserElo.get({ id: p1Id });
|
let p1EloData = await gameService.getUserElo(p1Id);
|
||||||
let p2EloData = getUserElo.get({ id: p2Id });
|
let p2EloData = await gameService.getUserElo(p2Id);
|
||||||
|
|
||||||
// --- 2. Initialize Elo if it doesn't exist ---
|
// --- 2. Initialize Elo if it doesn't exist ---
|
||||||
if (!p1EloData) {
|
if (!p1EloData) {
|
||||||
await insertElos.run({ id: p1Id, elo: 1000 });
|
await gameService.insertElo(p1Id, 1000);
|
||||||
p1EloData = { id: p1Id, elo: 1000 };
|
p1EloData = { id: p1Id, elo: 1000 };
|
||||||
}
|
}
|
||||||
if (!p2EloData) {
|
if (!p2EloData) {
|
||||||
await insertElos.run({ id: p2Id, elo: 1000 });
|
await gameService.insertElo(p2Id, 1000);
|
||||||
p2EloData = { id: p2Id, elo: 1000 };
|
p2EloData = { id: p2Id, elo: 1000 };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,34 +92,34 @@ export async function eloHandler(p1Id, p2Id, p1Score, p2Score, type, scores = nu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. Update Database ---
|
// --- 4. Update Database ---
|
||||||
updateElo.run({ id: p1Id, elo: finalP1Elo });
|
await gameService.updateElo(p1Id, finalP1Elo);
|
||||||
updateElo.run({ id: p2Id, elo: finalP2Elo });
|
await gameService.updateElo(p2Id, finalP2Elo);
|
||||||
|
|
||||||
if (scores) {
|
if (scores) {
|
||||||
insertGame.run({
|
await gameService.insertGame({
|
||||||
id: `${p1Id}-${p2Id}-${Date.now()}`,
|
id: `${p1Id}-${p2Id}-${Date.now()}`,
|
||||||
p1: p1Id,
|
p1: p1Id,
|
||||||
p2: p2Id,
|
p2: p2Id,
|
||||||
p1_score: scores.p1,
|
p1Score: scores.p1,
|
||||||
p2_score: scores.p2,
|
p2Score: scores.p2,
|
||||||
p1_elo: p1CurrentElo,
|
p1Elo: p1CurrentElo,
|
||||||
p2_elo: p2CurrentElo,
|
p2Elo: p2CurrentElo,
|
||||||
p1_new_elo: finalP1Elo,
|
p1NewElo: finalP1Elo,
|
||||||
p2_new_elo: finalP2Elo,
|
p2NewElo: finalP2Elo,
|
||||||
type: type,
|
type: type,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
insertGame.run({
|
await gameService.insertGame({
|
||||||
id: `${p1Id}-${p2Id}-${Date.now()}`,
|
id: `${p1Id}-${p2Id}-${Date.now()}`,
|
||||||
p1: p1Id,
|
p1: p1Id,
|
||||||
p2: p2Id,
|
p2: p2Id,
|
||||||
p1_score: p1Score,
|
p1Score: p1Score,
|
||||||
p2_score: p2Score,
|
p2Score: p2Score,
|
||||||
p1_elo: p1CurrentElo,
|
p1Elo: p1CurrentElo,
|
||||||
p2_elo: p2CurrentElo,
|
p2Elo: p2CurrentElo,
|
||||||
p1_new_elo: finalP1Elo,
|
p1NewElo: finalP1Elo,
|
||||||
p2_new_elo: finalP2Elo,
|
p2NewElo: finalP2Elo,
|
||||||
type: type,
|
type: type,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
@@ -141,11 +142,12 @@ export async function pokerEloHandler(room) {
|
|||||||
if (playerIds.length < 2) return; // Not enough players to calculate Elo
|
if (playerIds.length < 2) return; // Not enough players to calculate Elo
|
||||||
|
|
||||||
// Fetch all players' Elo data at once
|
// Fetch all players' Elo data at once
|
||||||
const dbPlayers = playerIds.map((id) => {
|
const dbPlayers = await Promise.all(playerIds.map(async (id) => {
|
||||||
const user = getUser.get(id);
|
const user = await userService.getUser(id);
|
||||||
const elo = getUserElo.get({ id })?.elo || 1000;
|
const eloData = await gameService.getUserElo(id);
|
||||||
|
const elo = eloData?.elo || 1000;
|
||||||
return { ...user, elo };
|
return { ...user, elo };
|
||||||
});
|
}));
|
||||||
|
|
||||||
const winnerIds = new Set(room.winners);
|
const winnerIds = new Set(room.winners);
|
||||||
const playerCount = dbPlayers.length;
|
const playerCount = dbPlayers.length;
|
||||||
@@ -153,7 +155,7 @@ export async function pokerEloHandler(room) {
|
|||||||
|
|
||||||
const averageElo = dbPlayers.reduce((sum, p) => sum + p.elo, 0) / playerCount;
|
const averageElo = dbPlayers.reduce((sum, p) => sum + p.elo, 0) / playerCount;
|
||||||
|
|
||||||
dbPlayers.forEach((player) => {
|
for (const player of dbPlayers) {
|
||||||
// Expected score is the chance of winning against an "average" player from the field
|
// Expected score is the chance of winning against an "average" player from the field
|
||||||
const expectedScore = 1 / (1 + Math.pow(10, (averageElo - player.elo) / 400));
|
const expectedScore = 1 / (1 + Math.pow(10, (averageElo - player.elo) / 400));
|
||||||
|
|
||||||
@@ -175,23 +177,23 @@ export async function pokerEloHandler(room) {
|
|||||||
console.log(
|
console.log(
|
||||||
`Elo Update (POKER) for ${player.globalName}: ${player.elo} -> ${newElo} (Δ: ${eloChange.toFixed(2)})`,
|
`Elo Update (POKER) for ${player.globalName}: ${player.elo} -> ${newElo} (Δ: ${eloChange.toFixed(2)})`,
|
||||||
);
|
);
|
||||||
updateElo.run({ id: player.id, elo: newElo });
|
await gameService.updateElo(player.id, newElo);
|
||||||
|
|
||||||
insertGame.run({
|
await gameService.insertGame({
|
||||||
id: `${player.id}-poker-${Date.now()}`,
|
id: `${player.id}-poker-${Date.now()}`,
|
||||||
p1: player.id,
|
p1: player.id,
|
||||||
p2: null, // No single opponent
|
p2: null, // No single opponent
|
||||||
p1_score: actualScore,
|
p1Score: actualScore,
|
||||||
p2_score: null,
|
p2Score: null,
|
||||||
p1_elo: player.elo,
|
p1Elo: player.elo,
|
||||||
p2_elo: Math.round(averageElo), // Log the average opponent Elo for context
|
p2Elo: Math.round(averageElo), // Log the average opponent Elo for context
|
||||||
p1_new_elo: newElo,
|
p1NewElo: newElo,
|
||||||
p2_new_elo: null,
|
p2NewElo: null,
|
||||||
type: "POKER_ROUND",
|
type: "POKER_ROUND",
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error(`Error calculating new Elo for ${player.globalName}.`);
|
console.error(`Error calculating new Elo for ${player.globalName}.`);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
import {
|
import * as userService from "../services/user.service.js";
|
||||||
clearSOTDStats,
|
import * as skinService from "../services/skin.service.js";
|
||||||
deleteSOTD,
|
import * as logService from "../services/log.service.js";
|
||||||
getAllSkins,
|
import * as solitaireService from "../services/solitaire.service.js";
|
||||||
getAllSOTDStats,
|
|
||||||
getUser,
|
|
||||||
insertGame,
|
|
||||||
insertLog,
|
|
||||||
insertSOTD,
|
|
||||||
pruneOldLogs,
|
|
||||||
updateUserCoins
|
|
||||||
} from "../database/index.js";
|
|
||||||
import { activeSlowmodes, activeSolitaireGames, messagesTimestamps, skins } from "./state.js";
|
import { activeSlowmodes, activeSolitaireGames, messagesTimestamps, skins } from "./state.js";
|
||||||
import { createDeck, createSeededRNG, deal, seededShuffle } from "./solitaire.js";
|
import { createDeck, createSeededRNG, deal, seededShuffle } from "./solitaire.js";
|
||||||
import { emitSolitaireUpdate } from "../server/socket.js";
|
import { emitSolitaireUpdate } from "../server/socket.js";
|
||||||
@@ -22,7 +14,7 @@ import { emitSolitaireUpdate } from "../server/socket.js";
|
|||||||
*/
|
*/
|
||||||
export async function channelPointsHandler(message) {
|
export async function channelPointsHandler(message) {
|
||||||
const author = message.author;
|
const author = message.author;
|
||||||
const authorDB = getUser.get(author.id);
|
const authorDB = await userService.getUser(author.id);
|
||||||
|
|
||||||
if (!authorDB) {
|
if (!authorDB) {
|
||||||
// User not in our database, do nothing.
|
// User not in our database, do nothing.
|
||||||
@@ -53,21 +45,18 @@ export async function channelPointsHandler(message) {
|
|||||||
const coinsToAdd = recentTimestamps.length === 10 ? 50 : 10;
|
const coinsToAdd = recentTimestamps.length === 10 ? 50 : 10;
|
||||||
const newCoinTotal = authorDB.coins + coinsToAdd;
|
const newCoinTotal = authorDB.coins + coinsToAdd;
|
||||||
|
|
||||||
updateUserCoins.run({
|
await userService.updateUserCoins(author.id, newCoinTotal);
|
||||||
id: author.id,
|
|
||||||
coins: newCoinTotal,
|
|
||||||
});
|
|
||||||
|
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${author.id}-${now}`,
|
id: `${author.id}-${now}`,
|
||||||
user_id: author.id,
|
userId: author.id,
|
||||||
action: "AUTO_COINS",
|
action: "AUTO_COINS",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: coinsToAdd,
|
coinsAmount: coinsToAdd,
|
||||||
user_new_amount: newCoinTotal,
|
userNewAmount: newCoinTotal,
|
||||||
});
|
});
|
||||||
|
|
||||||
await pruneOldLogs();
|
await logService.pruneOldLogs();
|
||||||
|
|
||||||
return true; // Indicate that points were awarded
|
return true; // Indicate that points were awarded
|
||||||
}
|
}
|
||||||
@@ -116,8 +105,8 @@ export async function slowmodesHandler(message) {
|
|||||||
* Used for testing and simulations.
|
* Used for testing and simulations.
|
||||||
* @returns {string} The calculated random price as a string.
|
* @returns {string} The calculated random price as a string.
|
||||||
*/
|
*/
|
||||||
export function randomSkinPrice() {
|
export async function randomSkinPrice() {
|
||||||
const dbSkins = getAllSkins.all();
|
const dbSkins = await skinService.getAllSkins();
|
||||||
if (dbSkins.length === 0) return "0.00";
|
if (dbSkins.length === 0) return "0.00";
|
||||||
|
|
||||||
const randomDbSkin = dbSkins[Math.floor(Math.random() * dbSkins.length)];
|
const randomDbSkin = dbSkins[Math.floor(Math.random() * dbSkins.length)];
|
||||||
@@ -144,30 +133,30 @@ export function randomSkinPrice() {
|
|||||||
* Initializes the Solitaire of the Day.
|
* Initializes the Solitaire of the Day.
|
||||||
* This function clears previous stats, awards the winner, and generates a new daily seed.
|
* This function clears previous stats, awards the winner, and generates a new daily seed.
|
||||||
*/
|
*/
|
||||||
export function initTodaysSOTD() {
|
export async function initTodaysSOTD() {
|
||||||
console.log(`Initializing new Solitaire of the Day...`);
|
console.log(`Initializing new Solitaire of the Day...`);
|
||||||
|
|
||||||
// 1. Award previous day's winner
|
// 1. Award previous day's winner
|
||||||
const rankings = getAllSOTDStats.all();
|
const rankings = await solitaireService.getAllSOTDStats();
|
||||||
if (rankings.length > 0) {
|
if (rankings.length > 0) {
|
||||||
const winnerId = rankings[0].user_id;
|
const winnerId = rankings[0].userId;
|
||||||
const secondPlaceId = rankings[1] ? rankings[1].user_id : null;
|
const secondPlaceId = rankings[1] ? rankings[1].userId : null;
|
||||||
const thirdPlaceId = rankings[2] ? rankings[2].user_id : null;
|
const thirdPlaceId = rankings[2] ? rankings[2].userId : null;
|
||||||
const winnerUser = getUser.get(winnerId);
|
const winnerUser = await userService.getUser(winnerId);
|
||||||
const secondPlaceUser = secondPlaceId ? getUser.get(secondPlaceId) : null;
|
const secondPlaceUser = secondPlaceId ? await userService.getUser(secondPlaceId) : null;
|
||||||
const thirdPlaceUser = thirdPlaceId ? getUser.get(thirdPlaceId) : null;
|
const thirdPlaceUser = thirdPlaceId ? await userService.getUser(thirdPlaceId) : null;
|
||||||
|
|
||||||
if (winnerUser) {
|
if (winnerUser) {
|
||||||
const reward = 2500;
|
const reward = 2500;
|
||||||
const newCoinTotal = winnerUser.coins + reward;
|
const newCoinTotal = winnerUser.coins + reward;
|
||||||
updateUserCoins.run({ id: winnerId, coins: newCoinTotal });
|
await userService.updateUserCoins(winnerId, newCoinTotal);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${winnerId}-sotd-win-${Date.now()}`,
|
id: `${winnerId}-sotd-win-${Date.now()}`,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
user_id: winnerId,
|
userId: winnerId,
|
||||||
action: "SOTD_FIRST_PLACE",
|
action: "SOTD_FIRST_PLACE",
|
||||||
coins_amount: reward,
|
coinsAmount: reward,
|
||||||
user_new_amount: newCoinTotal,
|
userNewAmount: newCoinTotal,
|
||||||
});
|
});
|
||||||
console.log(
|
console.log(
|
||||||
`${winnerUser.globalName || winnerUser.username} won the previous SOTD and received ${reward} coins.`,
|
`${winnerUser.globalName || winnerUser.username} won the previous SOTD and received ${reward} coins.`,
|
||||||
@@ -176,14 +165,14 @@ export function initTodaysSOTD() {
|
|||||||
if (secondPlaceUser) {
|
if (secondPlaceUser) {
|
||||||
const reward = 1500;
|
const reward = 1500;
|
||||||
const newCoinTotal = secondPlaceUser.coins + reward;
|
const newCoinTotal = secondPlaceUser.coins + reward;
|
||||||
updateUserCoins.run({ id: secondPlaceId, coins: newCoinTotal });
|
await userService.updateUserCoins(secondPlaceId, newCoinTotal);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${secondPlaceId}-sotd-second-${Date.now()}`,
|
id: `${secondPlaceId}-sotd-second-${Date.now()}`,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
user_id: secondPlaceId,
|
userId: secondPlaceId,
|
||||||
action: "SOTD_SECOND_PLACE",
|
action: "SOTD_SECOND_PLACE",
|
||||||
coins_amount: reward,
|
coinsAmount: reward,
|
||||||
user_new_amount: newCoinTotal,
|
userNewAmount: newCoinTotal,
|
||||||
});
|
});
|
||||||
console.log(
|
console.log(
|
||||||
`${secondPlaceUser.globalName || secondPlaceUser.username} got second place in the previous SOTD and received ${reward} coins.`,
|
`${secondPlaceUser.globalName || secondPlaceUser.username} got second place in the previous SOTD and received ${reward} coins.`,
|
||||||
@@ -192,14 +181,14 @@ export function initTodaysSOTD() {
|
|||||||
if (thirdPlaceUser) {
|
if (thirdPlaceUser) {
|
||||||
const reward = 750;
|
const reward = 750;
|
||||||
const newCoinTotal = thirdPlaceUser.coins + reward;
|
const newCoinTotal = thirdPlaceUser.coins + reward;
|
||||||
updateUserCoins.run({ id: thirdPlaceId, coins: newCoinTotal });
|
await userService.updateUserCoins(thirdPlaceId, newCoinTotal);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${thirdPlaceId}-sotd-third-${Date.now()}`,
|
id: `${thirdPlaceId}-sotd-third-${Date.now()}`,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
user_id: thirdPlaceId,
|
userId: thirdPlaceId,
|
||||||
action: "SOTD_THIRD_PLACE",
|
action: "SOTD_THIRD_PLACE",
|
||||||
coins_amount: reward,
|
coinsAmount: reward,
|
||||||
user_new_amount: newCoinTotal,
|
userNewAmount: newCoinTotal,
|
||||||
});
|
});
|
||||||
console.log(
|
console.log(
|
||||||
`${thirdPlaceUser.globalName || thirdPlaceUser.username} got third place in the previous SOTD and received ${reward} coins.`,
|
`${thirdPlaceUser.globalName || thirdPlaceUser.username} got third place in the previous SOTD and received ${reward} coins.`,
|
||||||
@@ -221,9 +210,9 @@ export function initTodaysSOTD() {
|
|||||||
|
|
||||||
// 3. Clear old stats and save the new game state to the database
|
// 3. Clear old stats and save the new game state to the database
|
||||||
try {
|
try {
|
||||||
clearSOTDStats.run();
|
await solitaireService.clearSOTDStats();
|
||||||
deleteSOTD.run();
|
await solitaireService.deleteSOTD();
|
||||||
insertSOTD.run({
|
await solitaireService.insertSOTD({
|
||||||
tableauPiles: JSON.stringify(todaysSOTD.tableauPiles),
|
tableauPiles: JSON.stringify(todaysSOTD.tableauPiles),
|
||||||
foundationPiles: JSON.stringify(todaysSOTD.foundationPiles),
|
foundationPiles: JSON.stringify(todaysSOTD.foundationPiles),
|
||||||
stockPile: JSON.stringify(todaysSOTD.stockPile),
|
stockPile: JSON.stringify(todaysSOTD.stockPile),
|
||||||
|
|||||||
5
src/prisma/client.js
Normal file
5
src/prisma/client.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export default prisma;
|
||||||
@@ -37,6 +37,9 @@ app.post("/interactions", verifyKeyMiddleware(process.env.PUBLIC_KEY), async (re
|
|||||||
await handleInteraction(req, res, client);
|
await handleInteraction(req, res, client);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Stripe webhook endpoint needs raw body for signature verification
|
||||||
|
app.use("/api/buy-coins", express.raw({ type: "application/json" }));
|
||||||
|
|
||||||
// JSON Body Parser Middleware
|
// JSON Body Parser Middleware
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,8 @@ import {
|
|||||||
} from "../../game/blackjack.js";
|
} from "../../game/blackjack.js";
|
||||||
|
|
||||||
// Optional: hook into your DB & Discord systems if available
|
// Optional: hook into your DB & Discord systems if available
|
||||||
import { getUser, insertLog, updateUserCoins } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
import * as logService from "../../services/log.service.js";
|
||||||
import { client } from "../../bot/client.js";
|
import { client } from "../../bot/client.js";
|
||||||
import { emitToast, emitUpdate, emitPlayerUpdate } from "../socket.js";
|
import { emitToast, emitUpdate, emitPlayerUpdate } from "../socket.js";
|
||||||
import { EmbedBuilder, time } from "discord.js";
|
import { EmbedBuilder, time } from "discord.js";
|
||||||
@@ -128,7 +129,7 @@ export function blackjackRoutes(io) {
|
|||||||
if (room.players[userId]) return res.status(200).json({ message: "Already here" });
|
if (room.players[userId]) return res.status(200).json({ message: "Already here" });
|
||||||
|
|
||||||
const user = await client.users.fetch(userId);
|
const user = await client.users.fetch(userId);
|
||||||
const bank = getUser.get(userId)?.coins ?? 0;
|
const bank = (await userService.getUser(userId))?.coins ?? 0;
|
||||||
|
|
||||||
room.players[userId] = {
|
room.players[userId] = {
|
||||||
id: userId,
|
id: userId,
|
||||||
@@ -229,7 +230,7 @@ export function blackjackRoutes(io) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/bet", (req, res) => {
|
router.post("/bet", async (req, res) => {
|
||||||
const { userId, amount } = req.body;
|
const { userId, amount } = req.body;
|
||||||
const p = room.players[userId];
|
const p = room.players[userId];
|
||||||
if (!p) return res.status(404).json({ message: "not in room" });
|
if (!p) return res.status(404).json({ message: "not in room" });
|
||||||
@@ -239,17 +240,17 @@ export function blackjackRoutes(io) {
|
|||||||
if (bet < room.minBet || bet > room.maxBet) return res.status(400).json({ message: "invalid-bet" });
|
if (bet < room.minBet || bet > room.maxBet) return res.status(400).json({ message: "invalid-bet" });
|
||||||
|
|
||||||
if (!room.settings.fakeMoney) {
|
if (!room.settings.fakeMoney) {
|
||||||
const userDB = getUser.get(userId);
|
const userDB = await userService.getUser(userId);
|
||||||
const coins = userDB?.coins ?? 0;
|
const coins = userDB?.coins ?? 0;
|
||||||
if (coins < bet) return res.status(403).json({ message: "insufficient-funds" });
|
if (coins < bet) return res.status(403).json({ message: "insufficient-funds" });
|
||||||
updateUserCoins.run({ id: userId, coins: coins - bet });
|
await userService.updateUserCoins(userId, coins - bet);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-blackjack-${Date.now()}`,
|
id: `${userId}-blackjack-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "BLACKJACK_BET",
|
action: "BLACKJACK_BET",
|
||||||
coins_amount: -bet,
|
coinsAmount: -bet,
|
||||||
user_new_amount: coins - bet,
|
userNewAmount: coins - bet,
|
||||||
});
|
});
|
||||||
p.bank = coins - bet;
|
p.bank = coins - bet;
|
||||||
}
|
}
|
||||||
@@ -261,7 +262,7 @@ export function blackjackRoutes(io) {
|
|||||||
return res.status(200).json({ message: "bet-accepted" });
|
return res.status(200).json({ message: "bet-accepted" });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/action/:action", (req, res) => {
|
router.post("/action/:action", async (req, res) => {
|
||||||
const { userId } = req.body;
|
const { userId } = req.body;
|
||||||
const action = req.params.action;
|
const action = req.params.action;
|
||||||
const p = room.players[userId];
|
const p = room.players[userId];
|
||||||
@@ -270,36 +271,36 @@ export function blackjackRoutes(io) {
|
|||||||
|
|
||||||
// Handle extra coin lock for double
|
// Handle extra coin lock for double
|
||||||
if (action === "double" && !room.settings.fakeMoney) {
|
if (action === "double" && !room.settings.fakeMoney) {
|
||||||
const userDB = getUser.get(userId);
|
const userDB = await userService.getUser(userId);
|
||||||
const coins = userDB?.coins ?? 0;
|
const coins = userDB?.coins ?? 0;
|
||||||
const hand = p.hands[p.activeHand];
|
const hand = p.hands[p.activeHand];
|
||||||
if (coins < hand.bet) return res.status(403).json({ message: "insufficient-funds-for-double" });
|
if (coins < hand.bet) return res.status(403).json({ message: "insufficient-funds-for-double" });
|
||||||
updateUserCoins.run({ id: userId, coins: coins - hand.bet });
|
await userService.updateUserCoins(userId, coins - hand.bet);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-blackjack-${Date.now()}`,
|
id: `${userId}-blackjack-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "BLACKJACK_DOUBLE",
|
action: "BLACKJACK_DOUBLE",
|
||||||
coins_amount: -hand.bet,
|
coinsAmount: -hand.bet,
|
||||||
user_new_amount: coins - hand.bet,
|
userNewAmount: coins - hand.bet,
|
||||||
});
|
});
|
||||||
p.bank = coins - hand.bet;
|
p.bank = coins - hand.bet;
|
||||||
// effective bet size is handled in settlement via hand.doubled flag
|
// effective bet size is handled in settlement via hand.doubled flag
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === "split" && !room.settings.fakeMoney) {
|
if (action === "split" && !room.settings.fakeMoney) {
|
||||||
const userDB = getUser.get(userId);
|
const userDB = await userService.getUser(userId);
|
||||||
const coins = userDB?.coins ?? 0;
|
const coins = userDB?.coins ?? 0;
|
||||||
const hand = p.hands[p.activeHand];
|
const hand = p.hands[p.activeHand];
|
||||||
if (coins < hand.bet) return res.status(403).json({ message: "insufficient-funds-for-split" });
|
if (coins < hand.bet) return res.status(403).json({ message: "insufficient-funds-for-split" });
|
||||||
updateUserCoins.run({ id: userId, coins: coins - hand.bet });
|
await userService.updateUserCoins(userId, coins - hand.bet);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-blackjack-${Date.now()}`,
|
id: `${userId}-blackjack-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "BLACKJACK_SPLIT",
|
action: "BLACKJACK_SPLIT",
|
||||||
coins_amount: -hand.bet,
|
coinsAmount: -hand.bet,
|
||||||
user_new_amount: coins - hand.bet,
|
userNewAmount: coins - hand.bet,
|
||||||
});
|
});
|
||||||
p.bank = coins - hand.bet;
|
p.bank = coins - hand.bet;
|
||||||
// effective bet size is handled in settlement via hand.doubled flag
|
// effective bet size is handled in settlement via hand.doubled flag
|
||||||
|
|||||||
@@ -5,18 +5,10 @@ import express from "express";
|
|||||||
// --- Utility and API Imports ---
|
// --- Utility and API Imports ---
|
||||||
// --- Discord.js Builder Imports ---
|
// --- Discord.js Builder Imports ---
|
||||||
import { ButtonStyle } from "discord.js";
|
import { ButtonStyle } from "discord.js";
|
||||||
import {
|
import * as userService from "../../services/user.service.js";
|
||||||
getMarketOfferById,
|
import * as skinService from "../../services/skin.service.js";
|
||||||
getMarketOffers,
|
import * as logService from "../../services/log.service.js";
|
||||||
getMarketOffersBySkin,
|
import * as marketService from "../../services/market.service.js";
|
||||||
getOfferBids,
|
|
||||||
getSkin,
|
|
||||||
getUser,
|
|
||||||
insertBid,
|
|
||||||
insertLog,
|
|
||||||
insertMarketOffer,
|
|
||||||
updateUserCoins,
|
|
||||||
} from "../../database/index.js";
|
|
||||||
import { emitMarketUpdate } from "../socket.js";
|
import { emitMarketUpdate } from "../socket.js";
|
||||||
import { handleNewMarketOffer, handleNewMarketOfferBid } from "../../utils/marketNotifs.js";
|
import { handleNewMarketOffer, handleNewMarketOfferBid } from "../../utils/marketNotifs.js";
|
||||||
|
|
||||||
@@ -32,25 +24,26 @@ const router = express.Router();
|
|||||||
export function marketRoutes(client, io) {
|
export function marketRoutes(client, io) {
|
||||||
router.get("/offers", async (req, res) => {
|
router.get("/offers", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const offers = getMarketOffers.all();
|
const offers = await marketService.getMarketOffers();
|
||||||
offers.forEach((offer) => {
|
for (const offer of offers) {
|
||||||
offer.skin = getSkin.get(offer.skin_uuid);
|
offer.skin = await skinService.getSkin(offer.skinUuid);
|
||||||
offer.seller = getUser.get(offer.seller_id);
|
offer.seller = await userService.getUser(offer.sellerId);
|
||||||
offer.buyer = getUser.get(offer.buyer_id) || null;
|
offer.buyer = offer.buyerId ? await userService.getUser(offer.buyerId) : null;
|
||||||
offer.bids = getOfferBids.all(offer.id) || {};
|
offer.bids = (await marketService.getOfferBids(offer.id)) || {};
|
||||||
offer.bids.forEach((bid) => {
|
for (const bid of offer.bids) {
|
||||||
bid.bidder = getUser.get(bid.bidder_id);
|
bid.bidder = await userService.getUser(bid.bidderId);
|
||||||
});
|
}
|
||||||
});
|
}
|
||||||
res.status(200).send({ offers });
|
res.status(200).send({ offers });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
res.status(500).send({ error: e });
|
res.status(500).send({ error: e });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/offers/:id", async (req, res) => {
|
router.get("/offers/:id", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const offer = getMarketOfferById.get(req.params.id);
|
const offer = await marketService.getMarketOfferById(req.params.id);
|
||||||
if (offer) {
|
if (offer) {
|
||||||
res.status(200).send({ offer });
|
res.status(200).send({ offer });
|
||||||
} else {
|
} else {
|
||||||
@@ -63,7 +56,7 @@ export function marketRoutes(client, io) {
|
|||||||
|
|
||||||
router.get("/offers/:id/bids", async (req, res) => {
|
router.get("/offers/:id/bids", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const bids = getOfferBids.get(req.params.id);
|
const bids = await marketService.getOfferBids(req.params.id);
|
||||||
res.status(200).send({ bids });
|
res.status(200).send({ bids });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send({ error: e });
|
res.status(500).send({ error: e });
|
||||||
@@ -74,13 +67,13 @@ export function marketRoutes(client, io) {
|
|||||||
const { seller_id, skin_uuid, starting_price, delay, duration, timestamp } = req.body;
|
const { seller_id, skin_uuid, starting_price, delay, duration, timestamp } = req.body;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
try {
|
try {
|
||||||
const skin = getSkin.get(skin_uuid);
|
const skin = await skinService.getSkin(skin_uuid);
|
||||||
if (!skin) return res.status(404).send({ error: "Skin not found" });
|
if (!skin) return res.status(404).send({ error: "Skin not found" });
|
||||||
const seller = getUser.get(seller_id);
|
const seller = await userService.getUser(seller_id);
|
||||||
if (!seller) return res.status(404).send({ error: "Seller not found" });
|
if (!seller) return res.status(404).send({ error: "Seller not found" });
|
||||||
if (skin.user_id !== seller.id) return res.status(403).send({ error: "You do not own this skin" });
|
if (skin.userId !== seller.id) return res.status(403).send({ error: "You do not own this skin" });
|
||||||
|
|
||||||
const existingOffers = getMarketOffersBySkin.all(skin.uuid);
|
const existingOffers = await marketService.getMarketOffersBySkin(skin.uuid);
|
||||||
if (
|
if (
|
||||||
existingOffers.length > 0 &&
|
existingOffers.length > 0 &&
|
||||||
existingOffers.some((offer) => offer.status === "open" || offer.status === "pending")
|
existingOffers.some((offer) => offer.status === "open" || offer.status === "pending")
|
||||||
@@ -92,15 +85,15 @@ export function marketRoutes(client, io) {
|
|||||||
const closing_at = opening_at + duration;
|
const closing_at = opening_at + duration;
|
||||||
|
|
||||||
const offerId = Date.now() + "-" + seller.id + "-" + skin.uuid;
|
const offerId = Date.now() + "-" + seller.id + "-" + skin.uuid;
|
||||||
insertMarketOffer.run({
|
await marketService.insertMarketOffer({
|
||||||
id: offerId,
|
id: offerId,
|
||||||
skin_uuid: skin.uuid,
|
skinUuid: skin.uuid,
|
||||||
seller_id: seller.id,
|
sellerId: seller.id,
|
||||||
starting_price: starting_price,
|
startingPrice: starting_price,
|
||||||
buyout_price: null,
|
buyoutPrice: null,
|
||||||
status: delay > 0 ? "pending" : "open",
|
status: delay > 0 ? "pending" : "open",
|
||||||
opening_at: opening_at,
|
openingAt: opening_at,
|
||||||
closing_at: closing_at,
|
closingAt: closing_at,
|
||||||
});
|
});
|
||||||
await emitMarketUpdate();
|
await emitMarketUpdate();
|
||||||
await handleNewMarketOffer(offerId, client);
|
await handleNewMarketOffer(offerId, client);
|
||||||
@@ -114,62 +107,62 @@ export function marketRoutes(client, io) {
|
|||||||
router.post("/offers/:id/place-bid", async (req, res) => {
|
router.post("/offers/:id/place-bid", async (req, res) => {
|
||||||
const { buyer_id, bid_amount, timestamp } = req.body;
|
const { buyer_id, bid_amount, timestamp } = req.body;
|
||||||
try {
|
try {
|
||||||
const offer = getMarketOfferById.get(req.params.id);
|
const offer = await marketService.getMarketOfferById(req.params.id);
|
||||||
if (!offer) return res.status(404).send({ error: "Offer not found" });
|
if (!offer) return res.status(404).send({ error: "Offer not found" });
|
||||||
if (offer.closing_at < timestamp) return res.status(403).send({ error: "Bidding period has ended" });
|
if (offer.closingAt < timestamp) return res.status(403).send({ error: "Bidding period has ended" });
|
||||||
|
|
||||||
if (buyer_id === offer.seller_id) return res.status(403).send({ error: "You can't bid on your own offer" });
|
if (buyer_id === offer.sellerId) return res.status(403).send({ error: "You can't bid on your own offer" });
|
||||||
|
|
||||||
const offerBids = getOfferBids.all(offer.id);
|
const offerBids = await marketService.getOfferBids(offer.id);
|
||||||
const lastBid = offerBids[0];
|
const lastBid = offerBids[0];
|
||||||
|
|
||||||
if (lastBid) {
|
if (lastBid) {
|
||||||
if (lastBid?.bidder_id === buyer_id)
|
if (lastBid?.bidderId === buyer_id)
|
||||||
return res.status(403).send({ error: "You are already the highest bidder" });
|
return res.status(403).send({ error: "You are already the highest bidder" });
|
||||||
if (bid_amount < lastBid?.offer_amount + 10) {
|
if (bid_amount < lastBid?.offerAmount + 10) {
|
||||||
return res.status(403).send({ error: "Bid amount is below minimum" });
|
return res.status(403).send({ error: "Bid amount is below minimum" });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (bid_amount < offer.starting_price + 10) {
|
if (bid_amount < offer.startingPrice + 10) {
|
||||||
return res.status(403).send({ error: "Bid amount is below minimum" });
|
return res.status(403).send({ error: "Bid amount is below minimum" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bidder = getUser.get(buyer_id);
|
const bidder = await userService.getUser(buyer_id);
|
||||||
if (!bidder) return res.status(404).send({ error: "Bidder not found" });
|
if (!bidder) return res.status(404).send({ error: "Bidder not found" });
|
||||||
if (bidder.coins < bid_amount)
|
if (bidder.coins < bid_amount)
|
||||||
return res.status(403).send({ error: "You do not have enough coins to place this bid" });
|
return res.status(403).send({ error: "You do not have enough coins to place this bid" });
|
||||||
|
|
||||||
const bidId = Date.now() + "-" + buyer_id + "-" + offer.id;
|
const bidId = Date.now() + "-" + buyer_id + "-" + offer.id;
|
||||||
insertBid.run({
|
await marketService.insertBid({
|
||||||
id: bidId,
|
id: bidId,
|
||||||
bidder_id: buyer_id,
|
bidderId: buyer_id,
|
||||||
market_offer_id: offer.id,
|
marketOfferId: offer.id,
|
||||||
offer_amount: bid_amount,
|
offerAmount: bid_amount,
|
||||||
});
|
});
|
||||||
const newCoinsAmount = bidder.coins - bid_amount;
|
const newCoinsAmount = bidder.coins - bid_amount;
|
||||||
updateUserCoins.run({ id: buyer_id, coins: newCoinsAmount });
|
await userService.updateUserCoins(buyer_id, newCoinsAmount);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${buyer_id}-bid-${offer.id}-${Date.now()}`,
|
id: `${buyer_id}-bid-${offer.id}-${Date.now()}`,
|
||||||
user_id: buyer_id,
|
userId: buyer_id,
|
||||||
action: "BID_PLACED",
|
action: "BID_PLACED",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: bid_amount,
|
coinsAmount: bid_amount,
|
||||||
user_new_amount: newCoinsAmount,
|
userNewAmount: newCoinsAmount,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Refund the previous highest bidder
|
// Refund the previous highest bidder
|
||||||
if (lastBid) {
|
if (lastBid) {
|
||||||
const previousBidder = getUser.get(lastBid.bidder_id);
|
const previousBidder = await userService.getUser(lastBid.bidderId);
|
||||||
const refundedCoinsAmount = previousBidder.coins + lastBid.offer_amount;
|
const refundedCoinsAmount = previousBidder.coins + lastBid.offerAmount;
|
||||||
updateUserCoins.run({ id: previousBidder.id, coins: refundedCoinsAmount });
|
await userService.updateUserCoins(previousBidder.id, refundedCoinsAmount);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${previousBidder.id}-bid-refund-${offer.id}-${Date.now()}`,
|
id: `${previousBidder.id}-bid-refund-${offer.id}-${Date.now()}`,
|
||||||
user_id: previousBidder.id,
|
userId: previousBidder.id,
|
||||||
action: "BID_REFUNDED",
|
action: "BID_REFUNDED",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: lastBid.offer_amount,
|
coinsAmount: lastBid.offerAmount,
|
||||||
user_new_amount: refundedCoinsAmount,
|
userNewAmount: refundedCoinsAmount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import express from "express";
|
|||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { monkePaths } from "../../game/state.js";
|
import { monkePaths } from "../../game/state.js";
|
||||||
import { socketEmit } from "../socket.js";
|
import { socketEmit } from "../socket.js";
|
||||||
import { getUser, updateUserCoins, insertLog } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
import * as logService from "../../services/log.service.js";
|
||||||
import { init } from "openai/_shims/index.mjs";
|
import { init } from "openai/_shims/index.mjs";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
@@ -16,11 +17,11 @@ const router = express.Router();
|
|||||||
export function monkeRoutes(client, io) {
|
export function monkeRoutes(client, io) {
|
||||||
// --- Router Management Endpoints
|
// --- Router Management Endpoints
|
||||||
|
|
||||||
router.get("/:userId", (req, res) => {
|
router.get("/:userId", async (req, res) => {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
|
|
||||||
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
||||||
const user = getUser.get(userId);
|
const user = await userService.getUser(userId);
|
||||||
if (!user) return res.status(404).json({ error: "User not found" });
|
if (!user) return res.status(404).json({ error: "User not found" });
|
||||||
const userGamePath = monkePaths[userId] || null;
|
const userGamePath = monkePaths[userId] || null;
|
||||||
if (!userGamePath) return res.status(404).json({ error: "No active game found for this user" });
|
if (!userGamePath) return res.status(404).json({ error: "No active game found for this user" });
|
||||||
@@ -28,29 +29,26 @@ export function monkeRoutes(client, io) {
|
|||||||
return res.status(200).json({ userGamePath });
|
return res.status(200).json({ userGamePath });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/:userId/start", (req, res) => {
|
router.post("/:userId/start", async (req, res) => {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
const { initialBet } = req.body;
|
const { initialBet } = req.body;
|
||||||
|
|
||||||
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
||||||
const user = getUser.get(userId);
|
const user = await userService.getUser(userId);
|
||||||
if (!user) return res.status(404).json({ error: "User not found" });
|
if (!user) return res.status(404).json({ error: "User not found" });
|
||||||
if (!initialBet) return res.status(400).json({ error: "Initial bet is required" });
|
if (!initialBet) return res.status(400).json({ error: "Initial bet is required" });
|
||||||
if (initialBet > user.coins) return res.status(400).json({ error: "Insufficient coins for the initial bet" });
|
if (initialBet > user.coins) return res.status(400).json({ error: "Insufficient coins for the initial bet" });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const newCoins = user.coins - initialBet;
|
const newCoins = user.coins - initialBet;
|
||||||
updateUserCoins.run({
|
await userService.updateUserCoins(userId, newCoins);
|
||||||
id: userId,
|
await logService.insertLog({
|
||||||
coins: newCoins,
|
|
||||||
});
|
|
||||||
insertLog.run({
|
|
||||||
id: `${userId}-monke-bet-${Date.now()}`,
|
id: `${userId}-monke-bet-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "MONKE_BET",
|
action: "MONKE_BET",
|
||||||
coins_amount: -initialBet,
|
coinsAmount: -initialBet,
|
||||||
user_new_amount: newCoins,
|
userNewAmount: newCoins,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(500).json({ error: "Failed to update user coins" });
|
return res.status(500).json({ error: "Failed to update user coins" });
|
||||||
@@ -61,12 +59,12 @@ export function monkeRoutes(client, io) {
|
|||||||
return res.status(200).json({ message: "Monke game started", userGamePath: monkePaths[userId] });
|
return res.status(200).json({ message: "Monke game started", userGamePath: monkePaths[userId] });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/:userId/play", (req, res) => {
|
router.post("/:userId/play", async (req, res) => {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
const { choice, step } = req.body;
|
const { choice, step } = req.body;
|
||||||
|
|
||||||
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
||||||
const user = getUser.get(userId);
|
const user = await userService.getUser(userId);
|
||||||
if (!user) return res.status(404).json({ error: "User not found" });
|
if (!user) return res.status(404).json({ error: "User not found" });
|
||||||
if (!monkePaths[userId]) return res.status(400).json({ error: "No active game found for this user" });
|
if (!monkePaths[userId]) return res.status(400).json({ error: "No active game found for this user" });
|
||||||
|
|
||||||
@@ -97,10 +95,10 @@ export function monkeRoutes(client, io) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/:userId/stop", (req, res) => {
|
router.post("/:userId/stop", async (req, res) => {
|
||||||
const { userId } = req.params;
|
const { userId } = req.params;
|
||||||
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
if (!userId) return res.status(400).json({ error: "User ID is required" });
|
||||||
const user = getUser.get(userId);
|
const user = await userService.getUser(userId);
|
||||||
if (!user) return res.status(404).json({ error: "User not found" });
|
if (!user) return res.status(404).json({ error: "User not found" });
|
||||||
if (!monkePaths[userId]) return res.status(400).json({ error: "No active game found for this user" });
|
if (!monkePaths[userId]) return res.status(400).json({ error: "No active game found for this user" });
|
||||||
const userGamePath = monkePaths[userId];
|
const userGamePath = monkePaths[userId];
|
||||||
@@ -112,17 +110,14 @@ export function monkeRoutes(client, io) {
|
|||||||
const newCoins = coins + extractValue;
|
const newCoins = coins + extractValue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateUserCoins.run({
|
await userService.updateUserCoins(userId, newCoins);
|
||||||
id: userId,
|
await logService.insertLog({
|
||||||
coins: newCoins,
|
|
||||||
});
|
|
||||||
insertLog.run({
|
|
||||||
id: `${userId}-monke-withdraw-${Date.now()}`,
|
id: `${userId}-monke-withdraw-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "MONKE_WITHDRAW",
|
action: "MONKE_WITHDRAW",
|
||||||
coins_amount: extractValue,
|
coinsAmount: extractValue,
|
||||||
user_new_amount: newCoins,
|
userNewAmount: newCoins,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).json({ message: "Game stopped", userGamePath });
|
return res.status(200).json({ message: "Game stopped", userGamePath });
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
getNextActivePlayer,
|
getNextActivePlayer,
|
||||||
initialShuffledCards,
|
initialShuffledCards,
|
||||||
} from "../../game/poker.js";
|
} from "../../game/poker.js";
|
||||||
import { getUser, insertLog, updateUserCoins } from "../../database/index.js";
|
import * as userService from "../../services/user.service.js";
|
||||||
|
import * as logService from "../../services/log.service.js";
|
||||||
import { sleep } from "openai/core";
|
import { sleep } from "openai/core";
|
||||||
import { client } from "../../bot/client.js";
|
import { client } from "../../bot/client.js";
|
||||||
import { emitPokerToast, emitPokerUpdate } from "../socket.js";
|
import { emitPokerToast, emitPokerUpdate } from "../socket.js";
|
||||||
@@ -131,7 +132,7 @@ export function pokerRoutes(client, io) {
|
|||||||
if (Object.values(pokerRooms).some((r) => r.players[userId] || r.queue[userId])) {
|
if (Object.values(pokerRooms).some((r) => r.players[userId] || r.queue[userId])) {
|
||||||
return res.status(403).json({ message: "You are already in a room or queue." });
|
return res.status(403).json({ message: "You are already in a room or queue." });
|
||||||
}
|
}
|
||||||
if (!pokerRooms[roomId].fakeMoney && pokerRooms[roomId].minBet > (getUser.get(userId)?.coins ?? 0)) {
|
if (!pokerRooms[roomId].fakeMoney && pokerRooms[roomId].minBet > ((await userService.getUser(userId))?.coins ?? 0)) {
|
||||||
return res.status(403).json({ message: "You do not have enough coins to join this room." });
|
return res.status(403).json({ message: "You do not have enough coins to join this room." });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,19 +148,16 @@ export function pokerRoutes(client, io) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!room.fakeMoney) {
|
if (!room.fakeMoney) {
|
||||||
const userDB = getUser.get(playerId);
|
const userDB = await userService.getUser(playerId);
|
||||||
if (userDB) {
|
if (userDB) {
|
||||||
updateUserCoins.run({
|
await userService.updateUserCoins(playerId, userDB.coins - room.minBet);
|
||||||
id: playerId,
|
await logService.insertLog({
|
||||||
coins: userDB.coins - room.minBet,
|
|
||||||
});
|
|
||||||
insertLog.run({
|
|
||||||
id: `${playerId}-poker-${Date.now()}`,
|
id: `${playerId}-poker-${Date.now()}`,
|
||||||
user_id: playerId,
|
userId: playerId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "POKER_JOIN",
|
action: "POKER_JOIN",
|
||||||
coins_amount: -room.minBet,
|
coinsAmount: -room.minBet,
|
||||||
user_new_amount: userDB.coins - room.minBet,
|
userNewAmount: userDB.coins - room.minBet,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +197,7 @@ export function pokerRoutes(client, io) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updatePlayerCoins(
|
await updatePlayerCoins(
|
||||||
pokerRooms[roomId].players[userId],
|
pokerRooms[roomId].players[userId],
|
||||||
pokerRooms[roomId].players[userId].bank,
|
pokerRooms[roomId].players[userId].bank,
|
||||||
pokerRooms[roomId].fakeMoney,
|
pokerRooms[roomId].fakeMoney,
|
||||||
@@ -239,7 +237,7 @@ export function pokerRoutes(client, io) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updatePlayerCoins(
|
await updatePlayerCoins(
|
||||||
pokerRooms[roomId].players[userId],
|
pokerRooms[roomId].players[userId],
|
||||||
pokerRooms[roomId].players[userId].bank,
|
pokerRooms[roomId].players[userId].bank,
|
||||||
pokerRooms[roomId].fakeMoney,
|
pokerRooms[roomId].fakeMoney,
|
||||||
@@ -359,7 +357,7 @@ export function pokerRoutes(client, io) {
|
|||||||
|
|
||||||
async function joinRoom(roomId, userId, io) {
|
async function joinRoom(roomId, userId, io) {
|
||||||
const user = await client.users.fetch(userId);
|
const user = await client.users.fetch(userId);
|
||||||
const userDB = getUser.get(userId);
|
const userDB = await userService.getUser(userId);
|
||||||
const room = pokerRooms[roomId];
|
const room = pokerRooms[roomId];
|
||||||
|
|
||||||
const playerObject = {
|
const playerObject = {
|
||||||
@@ -380,14 +378,14 @@ async function joinRoom(roomId, userId, io) {
|
|||||||
} else {
|
} else {
|
||||||
room.players[userId] = playerObject;
|
room.players[userId] = playerObject;
|
||||||
if (!room.fakeMoney) {
|
if (!room.fakeMoney) {
|
||||||
updateUserCoins.run({ id: userId, coins: userDB.coins - room.minBet });
|
await userService.updateUserCoins(userId, userDB.coins - room.minBet);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-poker-${Date.now()}`,
|
id: `${userId}-poker-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: "POKER_JOIN",
|
action: "POKER_JOIN",
|
||||||
coins_amount: -room.minBet,
|
coinsAmount: -room.minBet,
|
||||||
user_new_amount: userDB.coins - room.minBet,
|
userNewAmount: userDB.coins - room.minBet,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -539,29 +537,29 @@ function updatePlayerHandSolves(room) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updatePlayerCoins(player, amount, isFake) {
|
async function updatePlayerCoins(player, amount, isFake) {
|
||||||
if (isFake) return;
|
if (isFake) return;
|
||||||
const user = getUser.get(player.id);
|
const user = await userService.getUser(player.id);
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
const userDB = getUser.get(player.id);
|
const userDB = await userService.getUser(player.id);
|
||||||
updateUserCoins.run({ id: player.id, coins: userDB.coins + amount });
|
await userService.updateUserCoins(player.id, userDB.coins + amount);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${player.id}-poker-${Date.now()}`,
|
id: `${player.id}-poker-${Date.now()}`,
|
||||||
user_id: player.id,
|
userId: player.id,
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
action: `POKER_${amount > 0 ? "WIN" : "LOSE"}`,
|
action: `POKER_${amount > 0 ? "WIN" : "LOSE"}`,
|
||||||
coins_amount: amount,
|
coinsAmount: amount,
|
||||||
user_new_amount: userDB.coins + amount,
|
userNewAmount: userDB.coins + amount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearAfkPlayers(room) {
|
async function clearAfkPlayers(room) {
|
||||||
Object.keys(room.afk).forEach((playerId) => {
|
for (const playerId of Object.keys(room.afk)) {
|
||||||
if (room.players[playerId]) {
|
if (room.players[playerId]) {
|
||||||
updatePlayerCoins(room.players[playerId], room.players[playerId].bank, room.fakeMoney);
|
await updatePlayerCoins(room.players[playerId], room.players[playerId].bank, room.fakeMoney);
|
||||||
delete room.players[playerId];
|
delete room.players[playerId];
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
room.afk = {};
|
room.afk = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,9 @@ import {
|
|||||||
|
|
||||||
// --- Game State & Database Imports ---
|
// --- Game State & Database Imports ---
|
||||||
import { activeSolitaireGames } from "../../game/state.js";
|
import { activeSolitaireGames } from "../../game/state.js";
|
||||||
import {
|
import * as userService from "../../services/user.service.js";
|
||||||
getSOTD,
|
import * as logService from "../../services/log.service.js";
|
||||||
getUser,
|
import * as solitaireService from "../../services/solitaire.service.js";
|
||||||
insertSOTDStats,
|
|
||||||
deleteUserSOTDStats,
|
|
||||||
getUserSOTDStats,
|
|
||||||
updateUserCoins,
|
|
||||||
insertLog,
|
|
||||||
getAllSOTDStats,
|
|
||||||
} from "../../database/index.js";
|
|
||||||
import { socketEmit } from "../socket.js";
|
import { socketEmit } from "../socket.js";
|
||||||
|
|
||||||
// Create a new router instance
|
// Create a new router instance
|
||||||
@@ -85,7 +78,7 @@ export function solitaireRoutes(client, io) {
|
|||||||
res.json({ success: true, gameState });
|
res.json({ success: true, gameState });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("/start/sotd", (req, res) => {
|
router.post("/start/sotd", async (req, res) => {
|
||||||
const { userId } = req.body;
|
const { userId } = req.body;
|
||||||
/*if (!userId || !getUser.get(userId)) {
|
/*if (!userId || !getUser.get(userId)) {
|
||||||
return res.status(404).json({ error: 'User not found.' });
|
return res.status(404).json({ error: 'User not found.' });
|
||||||
@@ -98,7 +91,7 @@ export function solitaireRoutes(client, io) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const sotd = getSOTD.get();
|
const sotd = await solitaireService.getSOTD();
|
||||||
if (!sotd) {
|
if (!sotd) {
|
||||||
return res.status(500).json({ error: "Solitaire of the Day is not configured." });
|
return res.status(500).json({ error: "Solitaire of the Day is not configured." });
|
||||||
}
|
}
|
||||||
@@ -126,9 +119,9 @@ export function solitaireRoutes(client, io) {
|
|||||||
|
|
||||||
// --- Game State & Action Endpoints ---
|
// --- Game State & Action Endpoints ---
|
||||||
|
|
||||||
router.get("/sotd/rankings", (req, res) => {
|
router.get("/sotd/rankings", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const rankings = getAllSOTDStats.all();
|
const rankings = await solitaireService.getAllSOTDStats();
|
||||||
res.json({ rankings });
|
res.json({ rankings });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).json({ error: "Failed to fetch SOTD rankings." });
|
res.status(500).json({ error: "Failed to fetch SOTD rankings." });
|
||||||
@@ -237,20 +230,20 @@ function updateGameStats(gameState, actionType, moveData = {}) {
|
|||||||
|
|
||||||
/** Handles the logic when a game is won. */
|
/** Handles the logic when a game is won. */
|
||||||
async function handleWin(userId, gameState, io) {
|
async function handleWin(userId, gameState, io) {
|
||||||
const currentUser = getUser.get(userId);
|
const currentUser = await userService.getUser(userId);
|
||||||
if (!currentUser) return;
|
if (!currentUser) return;
|
||||||
|
|
||||||
if (gameState.hardMode) {
|
if (gameState.hardMode) {
|
||||||
const bonus = 100;
|
const bonus = 100;
|
||||||
const newCoins = currentUser.coins + bonus;
|
const newCoins = currentUser.coins + bonus;
|
||||||
updateUserCoins.run({ id: userId, coins: newCoins });
|
await userService.updateUserCoins(userId, newCoins);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-hardmode-solitaire-${Date.now()}`,
|
id: `${userId}-hardmode-solitaire-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
action: "HARDMODE_SOLITAIRE_WIN",
|
action: "HARDMODE_SOLITAIRE_WIN",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: bonus,
|
coinsAmount: bonus,
|
||||||
user_new_amount: newCoins,
|
userNewAmount: newCoins,
|
||||||
});
|
});
|
||||||
await socketEmit("data-updated", { table: "users" });
|
await socketEmit("data-updated", { table: "users" });
|
||||||
}
|
}
|
||||||
@@ -260,20 +253,20 @@ async function handleWin(userId, gameState, io) {
|
|||||||
gameState.endTime = Date.now();
|
gameState.endTime = Date.now();
|
||||||
const timeTaken = gameState.endTime - gameState.startTime;
|
const timeTaken = gameState.endTime - gameState.startTime;
|
||||||
|
|
||||||
const existingStats = getUserSOTDStats.get(userId);
|
const existingStats = await solitaireService.getUserSOTDStats(userId);
|
||||||
|
|
||||||
if (!existingStats) {
|
if (!existingStats) {
|
||||||
// First time completing the SOTD, grant bonus coins
|
// First time completing the SOTD, grant bonus coins
|
||||||
const bonus = 1000;
|
const bonus = 1000;
|
||||||
const newCoins = currentUser.coins + bonus;
|
const newCoins = currentUser.coins + bonus;
|
||||||
updateUserCoins.run({ id: userId, coins: newCoins });
|
await userService.updateUserCoins(userId, newCoins);
|
||||||
insertLog.run({
|
await logService.insertLog({
|
||||||
id: `${userId}-sotd-complete-${Date.now()}`,
|
id: `${userId}-sotd-complete-${Date.now()}`,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
action: "SOTD_WIN",
|
action: "SOTD_WIN",
|
||||||
target_user_id: null,
|
targetUserId: null,
|
||||||
coins_amount: bonus,
|
coinsAmount: bonus,
|
||||||
user_new_amount: newCoins,
|
userNewAmount: newCoins,
|
||||||
});
|
});
|
||||||
await socketEmit("data-updated", { table: "users" });
|
await socketEmit("data-updated", { table: "users" });
|
||||||
}
|
}
|
||||||
@@ -288,10 +281,10 @@ async function handleWin(userId, gameState, io) {
|
|||||||
timeTaken < existingStats.time);
|
timeTaken < existingStats.time);
|
||||||
|
|
||||||
if (isNewBest) {
|
if (isNewBest) {
|
||||||
deleteUserSOTDStats.run(userId);
|
await solitaireService.deleteUserSOTDStats(userId);
|
||||||
insertSOTDStats.run({
|
await solitaireService.insertSOTDStats({
|
||||||
id: userId,
|
id: userId,
|
||||||
user_id: userId,
|
userId: userId,
|
||||||
time: timeTaken,
|
time: timeTaken,
|
||||||
moves: gameState.moves,
|
moves: gameState.moves,
|
||||||
score: gameState.score,
|
score: gameState.score,
|
||||||
|
|||||||
49
src/services/game.service.js
Normal file
49
src/services/game.service.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
export async function getUserElo(id) {
|
||||||
|
return prisma.elo.findUnique({ where: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertElo(id, elo) {
|
||||||
|
return prisma.elo.create({ data: { id, elo } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateElo(id, elo) {
|
||||||
|
return prisma.elo.update({ where: { id }, data: { elo } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUsersByElo() {
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
include: { elo: true },
|
||||||
|
orderBy: { elo: { elo: "desc" } },
|
||||||
|
});
|
||||||
|
return users
|
||||||
|
.filter((u) => u.elo)
|
||||||
|
.map((u) => ({ ...u, elo: u.elo?.elo ?? null }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toGame(game) {
|
||||||
|
return { ...game, timestamp: game.timestamp != null ? game.timestamp.getTime() : null };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertGame(data) {
|
||||||
|
return prisma.game.create({
|
||||||
|
data: {
|
||||||
|
...data,
|
||||||
|
timestamp: data.timestamp != null ? new Date(data.timestamp) : null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGames() {
|
||||||
|
const games = await prisma.game.findMany();
|
||||||
|
return games.map(toGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserGames(userId) {
|
||||||
|
const games = await prisma.game.findMany({
|
||||||
|
where: { OR: [{ p1: userId }, { p2: userId }] },
|
||||||
|
orderBy: { timestamp: "asc" },
|
||||||
|
});
|
||||||
|
return games.map(toGame);
|
||||||
|
}
|
||||||
33
src/services/log.service.js
Normal file
33
src/services/log.service.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
export async function insertLog(data) {
|
||||||
|
return prisma.log.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getLogs() {
|
||||||
|
return prisma.log.findMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserLogs(userId) {
|
||||||
|
return prisma.log.findMany({ where: { userId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function pruneOldLogs() {
|
||||||
|
const limit = parseInt(process.env.LOGS_BY_USER);
|
||||||
|
const usersWithExcess = await prisma.$queryRawUnsafe(
|
||||||
|
`SELECT user_id as userId FROM logs GROUP BY user_id HAVING COUNT(*) > ?`,
|
||||||
|
limit,
|
||||||
|
);
|
||||||
|
for (const { userId } of usersWithExcess) {
|
||||||
|
await prisma.$executeRawUnsafe(
|
||||||
|
`DELETE FROM logs WHERE id IN (
|
||||||
|
SELECT id FROM (
|
||||||
|
SELECT id, ROW_NUMBER() OVER (ORDER BY created_at DESC) AS rn
|
||||||
|
FROM logs WHERE user_id = ?
|
||||||
|
) WHERE rn > ?
|
||||||
|
)`,
|
||||||
|
userId,
|
||||||
|
limit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
src/services/market.service.js
Normal file
107
src/services/market.service.js
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
function toOffer(offer) {
|
||||||
|
return { ...offer, openingAt: offer.openingAt.getTime(), closingAt: offer.closingAt.getTime() };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMarketOffers() {
|
||||||
|
const offers = await prisma.marketOffer.findMany({ orderBy: { postedAt: "desc" } });
|
||||||
|
return offers.map(toOffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMarketOfferById(id) {
|
||||||
|
const offer = await prisma.marketOffer.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
skin: { select: { displayName: true, displayIcon: true } },
|
||||||
|
seller: { select: { username: true, globalName: true } },
|
||||||
|
buyer: { select: { username: true, globalName: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!offer) return null;
|
||||||
|
// Flatten to match the old query shape
|
||||||
|
return toOffer({
|
||||||
|
...offer,
|
||||||
|
skinName: offer.skin?.displayName,
|
||||||
|
skinIcon: offer.skin?.displayIcon,
|
||||||
|
sellerName: offer.seller?.username,
|
||||||
|
sellerGlobalName: offer.seller?.globalName,
|
||||||
|
buyerName: offer.buyer?.username ?? null,
|
||||||
|
buyerGlobalName: offer.buyer?.globalName ?? null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMarketOffersBySkin(skinUuid) {
|
||||||
|
const offers = await prisma.marketOffer.findMany({
|
||||||
|
where: { skinUuid },
|
||||||
|
include: {
|
||||||
|
skin: { select: { displayName: true, displayIcon: true } },
|
||||||
|
seller: { select: { username: true, globalName: true } },
|
||||||
|
buyer: { select: { username: true, globalName: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return offers.map((offer) => toOffer({
|
||||||
|
...offer,
|
||||||
|
skinName: offer.skin?.displayName,
|
||||||
|
skinIcon: offer.skin?.displayIcon,
|
||||||
|
sellerName: offer.seller?.username,
|
||||||
|
sellerGlobalName: offer.seller?.globalName,
|
||||||
|
buyerName: offer.buyer?.username ?? null,
|
||||||
|
buyerGlobalName: offer.buyer?.globalName ?? null,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertMarketOffer(data) {
|
||||||
|
return prisma.marketOffer.create({
|
||||||
|
data: {
|
||||||
|
...data,
|
||||||
|
openingAt: new Date(data.openingAt),
|
||||||
|
closingAt: new Date(data.closingAt),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateMarketOffer(data) {
|
||||||
|
const { id, ...rest } = data;
|
||||||
|
return prisma.marketOffer.update({ where: { id }, data: rest });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteMarketOffer(id) {
|
||||||
|
return prisma.marketOffer.delete({ where: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Bids ---
|
||||||
|
|
||||||
|
export async function getBids() {
|
||||||
|
const bids = await prisma.bid.findMany({
|
||||||
|
include: { bidder: { select: { username: true, globalName: true } } },
|
||||||
|
orderBy: [{ offerAmount: "desc" }, { offeredAt: "asc" }],
|
||||||
|
});
|
||||||
|
return bids.map((bid) => ({
|
||||||
|
...bid,
|
||||||
|
bidderName: bid.bidder?.username,
|
||||||
|
bidderGlobalName: bid.bidder?.globalName,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBidById(id) {
|
||||||
|
return prisma.bid.findUnique({ where: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getOfferBids(marketOfferId) {
|
||||||
|
const bids = await prisma.bid.findMany({
|
||||||
|
where: { marketOfferId },
|
||||||
|
orderBy: [{ offerAmount: "desc" }, { offeredAt: "asc" }],
|
||||||
|
});
|
||||||
|
return bids.map((bid) => ({
|
||||||
|
...bid,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertBid(data) {
|
||||||
|
return prisma.bid.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteBid(id) {
|
||||||
|
return prisma.bid.delete({ where: { id } });
|
||||||
|
}
|
||||||
59
src/services/skin.service.js
Normal file
59
src/services/skin.service.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
export async function getSkin(uuid) {
|
||||||
|
return prisma.skin.findUnique({ where: { uuid } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllSkins() {
|
||||||
|
return prisma.skin.findMany({ orderBy: { maxPrice: "desc" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllAvailableSkins() {
|
||||||
|
return prisma.skin.findMany({ where: { userId: null } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserInventory(userId) {
|
||||||
|
return prisma.skin.findMany({
|
||||||
|
where: { userId },
|
||||||
|
orderBy: { currentPrice: "desc" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTopSkins() {
|
||||||
|
return prisma.skin.findMany({ orderBy: { maxPrice: "desc" }, take: 10 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertSkin(data) {
|
||||||
|
return prisma.skin.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSkin(data) {
|
||||||
|
const { uuid, ...rest } = data;
|
||||||
|
return prisma.skin.update({ where: { uuid }, data: rest });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function hardUpdateSkin(data) {
|
||||||
|
const { uuid, ...rest } = data;
|
||||||
|
return prisma.skin.update({ where: { uuid }, data: rest });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertManySkins(skins) {
|
||||||
|
return prisma.$transaction(
|
||||||
|
skins.map((skin) =>
|
||||||
|
prisma.skin.upsert({
|
||||||
|
where: { uuid: skin.uuid },
|
||||||
|
update: {},
|
||||||
|
create: skin,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateManySkins(skins) {
|
||||||
|
return prisma.$transaction(
|
||||||
|
skins.map((skin) => {
|
||||||
|
const { uuid, ...data } = skin;
|
||||||
|
return prisma.skin.update({ where: { uuid }, data });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
40
src/services/solitaire.service.js
Normal file
40
src/services/solitaire.service.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
export async function getSOTD() {
|
||||||
|
return prisma.sotd.findUnique({ where: { id: 0 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertSOTD(data) {
|
||||||
|
return prisma.sotd.create({ data: { id: 0, ...data } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteSOTD() {
|
||||||
|
return prisma.sotd.delete({ where: { id: 0 } }).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllSOTDStats() {
|
||||||
|
const stats = await prisma.sotdStat.findMany({
|
||||||
|
include: { user: { select: { globalName: true } } },
|
||||||
|
orderBy: [{ score: "desc" }, { moves: "asc" }, { time: "asc" }],
|
||||||
|
});
|
||||||
|
return stats.map((s) => ({
|
||||||
|
...s,
|
||||||
|
globalName: s.user?.globalName,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserSOTDStats(userId) {
|
||||||
|
return prisma.sotdStat.findFirst({ where: { userId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertSOTDStats(data) {
|
||||||
|
return prisma.sotdStat.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function clearSOTDStats() {
|
||||||
|
return prisma.sotdStat.deleteMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteUserSOTDStats(userId) {
|
||||||
|
return prisma.sotdStat.deleteMany({ where: { userId } });
|
||||||
|
}
|
||||||
20
src/services/transaction.service.js
Normal file
20
src/services/transaction.service.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
export async function insertTransaction(data) {
|
||||||
|
return prisma.transaction.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTransactionBySessionId(sessionId) {
|
||||||
|
return prisma.transaction.findUnique({ where: { sessionId } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllTransactions() {
|
||||||
|
return prisma.transaction.findMany({ orderBy: { createdAt: "desc" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserTransactions(userId) {
|
||||||
|
return prisma.transaction.findMany({
|
||||||
|
where: { userId },
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
});
|
||||||
|
}
|
||||||
73
src/services/user.service.js
Normal file
73
src/services/user.service.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import prisma from "../prisma/client.js";
|
||||||
|
|
||||||
|
export async function getUser(id) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { elo: true },
|
||||||
|
});
|
||||||
|
if (!user) return null;
|
||||||
|
return { ...user, elo: user.elo?.elo ?? null };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllUsers() {
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
include: { elo: true },
|
||||||
|
orderBy: { coins: "desc" },
|
||||||
|
});
|
||||||
|
return users.map((u) => ({ ...u, elo: u.elo?.elo ?? null }));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllAkhys() {
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
where: { isAkhy: 1 },
|
||||||
|
include: { elo: true },
|
||||||
|
orderBy: { coins: "desc" },
|
||||||
|
});
|
||||||
|
return users.map((u) => ({ ...u, elo: u.elo?.elo ?? null }));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertUser(data) {
|
||||||
|
return prisma.user.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUser(data) {
|
||||||
|
const { id, ...rest } = data;
|
||||||
|
return prisma.user.update({ where: { id }, data: rest });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUserCoins(id, coins) {
|
||||||
|
return prisma.user.update({ where: { id }, data: { coins } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUserAvatar(id, avatarUrl) {
|
||||||
|
return prisma.user.update({ where: { id }, data: { avatarUrl } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function queryDailyReward(id) {
|
||||||
|
return prisma.user.update({ where: { id }, data: { dailyQueried: 1 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetDailyReward() {
|
||||||
|
return prisma.user.updateMany({ data: { dailyQueried: 0 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertManyUsers(users) {
|
||||||
|
return prisma.$transaction(
|
||||||
|
users.map((user) =>
|
||||||
|
prisma.user.upsert({
|
||||||
|
where: { id: user.id },
|
||||||
|
update: {},
|
||||||
|
create: user,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateManyUsers(users) {
|
||||||
|
return prisma.$transaction(
|
||||||
|
users.map((user) => {
|
||||||
|
const { id, elo, ...data } = user;
|
||||||
|
return prisma.user.update({ where: { id }, data });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAllAvailableSkins, getSkin } from "../database/index.js";
|
import * as skinService from "../services/skin.service.js";
|
||||||
import { skins } from "../game/state.js";
|
import { skins } from "../game/state.js";
|
||||||
import { isChampionsSkin } from "./index.js";
|
import { isChampionsSkin } from "./index.js";
|
||||||
|
|
||||||
@@ -6,16 +6,15 @@ export async function drawCaseContent(caseType = "standard", poolSize = 100) {
|
|||||||
if (caseType === "esport") {
|
if (caseType === "esport") {
|
||||||
// Esport case: return all esport skins
|
// Esport case: return all esport skins
|
||||||
try {
|
try {
|
||||||
const dbSkins = getAllAvailableSkins.all();
|
const dbSkins = await skinService.getAllAvailableSkins();
|
||||||
const esportSkins = skins
|
const esportSkins = [];
|
||||||
.filter((s) => dbSkins.find((dbSkin) => dbSkin.displayName.includes("Classic (VCT") && dbSkin.uuid === s.uuid))
|
for (const s of skins.filter((s) => dbSkins.find((dbSkin) => dbSkin.displayName.includes("Classic (VCT") && dbSkin.uuid === s.uuid))) {
|
||||||
.map((s) => {
|
const dbSkin = await skinService.getSkin(s.uuid);
|
||||||
const dbSkin = getSkin.get(s.uuid);
|
esportSkins.push({
|
||||||
return {
|
...s,
|
||||||
...s, // Shallow copy to avoid mutating the imported 'skins' object
|
tierColor: dbSkin?.tierColor,
|
||||||
tierColor: dbSkin?.tierColor,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return esportSkins;
|
return esportSkins;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
@@ -55,8 +54,8 @@ export async function drawCaseContent(caseType = "standard", poolSize = 100) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const dbSkins = getAllAvailableSkins.all();
|
const dbSkins = await skinService.getAllAvailableSkins();
|
||||||
const weightedPool = skins
|
const filtered = skins
|
||||||
.filter((s) => dbSkins.find((dbSkin) => dbSkin.uuid === s.uuid))
|
.filter((s) => dbSkins.find((dbSkin) => dbSkin.uuid === s.uuid))
|
||||||
.filter((s) => {
|
.filter((s) => {
|
||||||
if (caseType === "ultra") {
|
if (caseType === "ultra") {
|
||||||
@@ -71,16 +70,19 @@ export async function drawCaseContent(caseType = "standard", poolSize = 100) {
|
|||||||
} else {
|
} else {
|
||||||
return isChampionsSkin(s.displayName) === false;
|
return isChampionsSkin(s.displayName) === false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.map((s) => {
|
const weightedPool = [];
|
||||||
const dbSkin = getSkin.get(s.uuid);
|
for (const s of filtered) {
|
||||||
return {
|
const dbSkin = await skinService.getSkin(s.uuid);
|
||||||
...s, // Shallow copy to avoid mutating the imported 'skins' object
|
const weight = tierWeights[s.contentTierUuid] ?? 0;
|
||||||
|
if (weight > 0) { // <--- CRITICAL: Remove 0 weight skins
|
||||||
|
weightedPool.push({
|
||||||
|
...s,
|
||||||
tierColor: dbSkin?.tierColor,
|
tierColor: dbSkin?.tierColor,
|
||||||
weight: tierWeights[s.contentTierUuid] ?? 0,
|
weight,
|
||||||
};
|
});
|
||||||
})
|
}
|
||||||
.filter((s) => s.weight > 0); // <--- CRITICAL: Remove 0 weight skins
|
}
|
||||||
|
|
||||||
function weightedSample(arr, count) {
|
function weightedSample(arr, count) {
|
||||||
let totalWeight = arr.reduce((sum, x) => sum + x.weight, 0);
|
let totalWeight = arr.reduce((sum, x) => sum + x.weight, 0);
|
||||||
@@ -123,7 +125,7 @@ export async function drawCaseContent(caseType = "standard", poolSize = 100) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawCaseSkin(caseContent) {
|
export async function drawCaseSkin(caseContent) {
|
||||||
let randomSelectedSkinIndex;
|
let randomSelectedSkinIndex;
|
||||||
let randomSelectedSkinUuid;
|
let randomSelectedSkinUuid;
|
||||||
try {
|
try {
|
||||||
@@ -134,7 +136,7 @@ export function drawCaseSkin(caseContent) {
|
|||||||
throw new Error("Failed to draw a skin from the case content.");
|
throw new Error("Failed to draw a skin from the case content.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbSkin = getSkin.get(randomSelectedSkinUuid);
|
const dbSkin = await skinService.getSkin(randomSelectedSkinUuid);
|
||||||
const randomSkinData = skins.find((skin) => skin.uuid === dbSkin.uuid);
|
const randomSkinData = skins.find((skin) => skin.uuid === dbSkin.uuid);
|
||||||
if (!randomSkinData) {
|
if (!randomSkinData) {
|
||||||
throw new Error(`Could not find skin data for UUID: ${dbSkin.uuid}`);
|
throw new Error(`Could not find skin data for UUID: ${dbSkin.uuid}`);
|
||||||
|
|||||||
@@ -5,23 +5,9 @@ import cron from "node-cron";
|
|||||||
import { getSkinTiers, getValorantSkins } from "../api/valorant.js";
|
import { getSkinTiers, getValorantSkins } from "../api/valorant.js";
|
||||||
import { DiscordRequest } from "../api/discord.js";
|
import { DiscordRequest } from "../api/discord.js";
|
||||||
import { initTodaysSOTD } from "../game/points.js";
|
import { initTodaysSOTD } from "../game/points.js";
|
||||||
import {
|
import * as userService from "../services/user.service.js";
|
||||||
deleteBid,
|
import * as skinService from "../services/skin.service.js";
|
||||||
deleteMarketOffer,
|
import * as marketService from "../services/market.service.js";
|
||||||
getAllAkhys,
|
|
||||||
getAllUsers,
|
|
||||||
getMarketOffers,
|
|
||||||
getOfferBids,
|
|
||||||
getSkin,
|
|
||||||
getUser,
|
|
||||||
insertManySkins,
|
|
||||||
insertUser,
|
|
||||||
resetDailyReward,
|
|
||||||
updateMarketOffer,
|
|
||||||
updateSkin,
|
|
||||||
updateUserAvatar,
|
|
||||||
updateUserCoins,
|
|
||||||
} from "../database/index.js";
|
|
||||||
import { activeInventories, activePredis, activeSearchs, pokerRooms, skins } from "../game/state.js";
|
import { activeInventories, activePredis, activeSearchs, pokerRooms, skins } from "../game/state.js";
|
||||||
import { emitMarketUpdate } from "../server/socket.js";
|
import { emitMarketUpdate } from "../server/socket.js";
|
||||||
import { handleMarketOfferClosing, handleMarketOfferOpening } from "./marketNotifs.js";
|
import { handleMarketOfferClosing, handleMarketOfferOpening } from "./marketNotifs.js";
|
||||||
@@ -49,7 +35,7 @@ export async function InstallGlobalCommands(appId, commands) {
|
|||||||
export async function getAkhys(client) {
|
export async function getAkhys(client) {
|
||||||
try {
|
try {
|
||||||
// 1. Fetch Discord Members
|
// 1. Fetch Discord Members
|
||||||
const initial_akhys = getAllUsers.all().length;
|
const initial_akhys = (await userService.getAllUsers()).length;
|
||||||
const guild = await client.guilds.fetch(process.env.GUILD_ID);
|
const guild = await client.guilds.fetch(process.env.GUILD_ID);
|
||||||
const members = await guild.members.fetch();
|
const members = await guild.members.fetch();
|
||||||
const akhys = members.filter((m) => !m.user.bot && m.roles.cache.has(process.env.AKHY_ROLE_ID));
|
const akhys = members.filter((m) => !m.user.bot && m.roles.cache.has(process.env.AKHY_ROLE_ID));
|
||||||
@@ -67,14 +53,14 @@ export async function getAkhys(client) {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
if (usersToInsert.length > 0) {
|
if (usersToInsert.length > 0) {
|
||||||
usersToInsert.forEach((user) => {
|
for (const user of usersToInsert) {
|
||||||
try {
|
try {
|
||||||
insertUser.run(user);
|
await userService.insertUser(user);
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const new_akhys = getAllUsers.all().length;
|
const new_akhys = (await userService.getAllUsers()).length;
|
||||||
const diff = new_akhys - initial_akhys;
|
const diff = new_akhys - initial_akhys;
|
||||||
console.log(
|
console.log(
|
||||||
`[Sync] Found and synced ${usersToInsert.length} ${diff !== 0 ? "(" + (diff > 0 ? "+" + diff : diff) + ") " : ""}users with the 'Akhy' role. (ID:${process.env.AKHY_ROLE_ID})`,
|
`[Sync] Found and synced ${usersToInsert.length} ${diff !== 0 ? "(" + (diff > 0 ? "+" + diff : diff) + ") " : ""}users with the 'Akhy' role. (ID:${process.env.AKHY_ROLE_ID})`,
|
||||||
@@ -97,17 +83,17 @@ export async function getAkhys(client) {
|
|||||||
displayName: skin.displayName,
|
displayName: skin.displayName,
|
||||||
contentTierUuid: skin.contentTierUuid,
|
contentTierUuid: skin.contentTierUuid,
|
||||||
displayIcon: skin.displayIcon,
|
displayIcon: skin.displayIcon,
|
||||||
user_id: null,
|
userId: null,
|
||||||
tierRank: tier.rank,
|
tierRank: tier.rank != null ? String(tier.rank) : null,
|
||||||
tierColor: tier.highlightColor?.slice(0, 6) || "F2F3F3",
|
tierColor: tier.highlightColor?.slice(0, 6) || "F2F3F3",
|
||||||
tierText: formatTierText(tier.rank, skin.displayName),
|
tierText: formatTierText(tier.rank, skin.displayName),
|
||||||
basePrice: basePrice.toFixed(0),
|
basePrice: basePrice.toFixed(0),
|
||||||
maxPrice: calculateMaxPrice(basePrice, skin).toFixed(0),
|
maxPrice: parseInt(calculateMaxPrice(basePrice, skin).toFixed(0)),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (skinsToInsert.length > 0) {
|
if (skinsToInsert.length > 0) {
|
||||||
insertManySkins(skinsToInsert);
|
await skinService.insertManySkins(skinsToInsert);
|
||||||
}
|
}
|
||||||
console.log(`[Sync] Fetched and synced ${skinsToInsert.length} Valorant skins.`);
|
console.log(`[Sync] Fetched and synced ${skinsToInsert.length} Valorant skins.`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -175,7 +161,7 @@ export function setupCronJobs(client, io) {
|
|||||||
cron.schedule(process.env.CRON_EXPR, async () => {
|
cron.schedule(process.env.CRON_EXPR, async () => {
|
||||||
console.log("[Cron] Running daily midnight tasks...");
|
console.log("[Cron] Running daily midnight tasks...");
|
||||||
try {
|
try {
|
||||||
resetDailyReward.run();
|
await userService.resetDailyReward();
|
||||||
console.log("[Cron] Daily rewards have been reset for all users.");
|
console.log("[Cron] Daily rewards have been reset for all users.");
|
||||||
//if (!getSOTD.get()) {
|
//if (!getSOTD.get()) {
|
||||||
initTodaysSOTD();
|
initTodaysSOTD();
|
||||||
@@ -184,16 +170,16 @@ export function setupCronJobs(client, io) {
|
|||||||
console.error("[Cron] Error during daily reset:", e);
|
console.error("[Cron] Error during daily reset:", e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const offers = getMarketOffers.all();
|
const offers = await marketService.getMarketOffers();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const TWO_DAYS = 2 * 24 * 60 * 60 * 1000;
|
const TWO_DAYS = 2 * 24 * 60 * 60 * 1000;
|
||||||
for (const offer of offers) {
|
for (const offer of offers) {
|
||||||
if (now >= offer.closing_at + TWO_DAYS) {
|
if (now >= offer.closingAt + TWO_DAYS) {
|
||||||
const offerBids = getOfferBids.all(offer.id);
|
const offerBids = await marketService.getOfferBids(offer.id);
|
||||||
for (const bid of offerBids) {
|
for (const bid of offerBids) {
|
||||||
deleteBid.run(bid.id);
|
await marketService.deleteBid(bid.id);
|
||||||
}
|
}
|
||||||
deleteMarketOffer.run(offer.id);
|
await marketService.deleteMarketOffer(offer.id);
|
||||||
console.log(`[Cron] Deleted expired market offer ID: ${offer.id}`);
|
console.log(`[Cron] Deleted expired market offer ID: ${offer.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,14 +193,11 @@ export function setupCronJobs(client, io) {
|
|||||||
console.log("[Cron] Running daily 7 AM data sync...");
|
console.log("[Cron] Running daily 7 AM data sync...");
|
||||||
await getAkhys(client);
|
await getAkhys(client);
|
||||||
try {
|
try {
|
||||||
const akhys = getAllAkhys.all();
|
const akhys = await userService.getAllAkhys();
|
||||||
for (const akhy of akhys) {
|
for (const akhy of akhys) {
|
||||||
const user = await client.users.cache.get(akhy.id);
|
const user = await client.users.cache.get(akhy.id);
|
||||||
try {
|
try {
|
||||||
updateUserAvatar.run({
|
await userService.updateUserAvatar(akhy.id, user.displayAvatarURL({ dynamic: true, size: 256 }));
|
||||||
id: akhy.id,
|
|
||||||
avatarUrl: user.displayAvatarURL({ dynamic: true, size: 256 }),
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`[Cron] Error updating avatar for user ID: ${akhy.id}`, err);
|
console.error(`[Cron] Error updating avatar for user ID: ${akhy.id}`, err);
|
||||||
}
|
}
|
||||||
@@ -276,51 +259,51 @@ export async function postAPOBuy(userId, amount) {
|
|||||||
|
|
||||||
// --- Miscellaneous Helpers ---
|
// --- Miscellaneous Helpers ---
|
||||||
|
|
||||||
function handleMarketOffersUpdate() {
|
async function handleMarketOffersUpdate() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const offers = getMarketOffers.all();
|
const offers = await marketService.getMarketOffers();
|
||||||
offers.forEach(async (offer) => {
|
offers.forEach(async (offer) => {
|
||||||
if (now >= offer.opening_at && offer.status === "pending") {
|
if (now >= offer.openingAt && offer.status === "pending") {
|
||||||
updateMarketOffer.run({ id: offer.id, final_price: null, buyer_id: null, status: "open" });
|
await marketService.updateMarketOffer({ id: offer.id, finalPrice: null, buyerId: null, status: "open" });
|
||||||
await handleMarketOfferOpening(offer.id, client);
|
await handleMarketOfferOpening(offer.id, client);
|
||||||
await emitMarketUpdate();
|
await emitMarketUpdate();
|
||||||
}
|
}
|
||||||
if (now >= offer.closing_at && offer.status !== "closed") {
|
if (now >= offer.closingAt && offer.status !== "closed") {
|
||||||
const bids = getOfferBids.all(offer.id);
|
const bids = await marketService.getOfferBids(offer.id);
|
||||||
|
|
||||||
if (bids.length === 0) {
|
if (bids.length === 0) {
|
||||||
// No bids placed, mark as closed without a sale
|
// No bids placed, mark as closed without a sale
|
||||||
updateMarketOffer.run({
|
await marketService.updateMarketOffer({
|
||||||
id: offer.id,
|
id: offer.id,
|
||||||
buyer_id: null,
|
buyerId: null,
|
||||||
final_price: null,
|
finalPrice: null,
|
||||||
status: "closed",
|
status: "closed",
|
||||||
});
|
});
|
||||||
await emitMarketUpdate();
|
await emitMarketUpdate();
|
||||||
} else {
|
} else {
|
||||||
const lastBid = bids[0];
|
const lastBid = bids[0];
|
||||||
const seller = getUser.get(offer.seller_id);
|
const seller = await userService.getUser(offer.sellerId);
|
||||||
const buyer = getUser.get(lastBid.bidder_id);
|
const buyer = await userService.getUser(lastBid.bidderId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Change skin ownership
|
// Change skin ownership
|
||||||
const skin = getSkin.get(offer.skin_uuid);
|
const skin = await skinService.getSkin(offer.skinUuid);
|
||||||
if (!skin) throw new Error(`Skin not found for offer ID: ${offer.id}`);
|
if (!skin) throw new Error(`Skin not found for offer ID: ${offer.id}`);
|
||||||
updateSkin.run({
|
await skinService.updateSkin({
|
||||||
user_id: buyer.id,
|
userId: buyer.id,
|
||||||
currentLvl: skin.currentLvl,
|
currentLvl: skin.currentLvl,
|
||||||
currentChroma: skin.currentChroma,
|
currentChroma: skin.currentChroma,
|
||||||
currentPrice: skin.currentPrice,
|
currentPrice: skin.currentPrice,
|
||||||
uuid: skin.uuid,
|
uuid: skin.uuid,
|
||||||
});
|
});
|
||||||
updateMarketOffer.run({
|
await marketService.updateMarketOffer({
|
||||||
id: offer.id,
|
id: offer.id,
|
||||||
buyer_id: buyer.id,
|
buyerId: buyer.id,
|
||||||
final_price: lastBid.offer_amount,
|
finalPrice: lastBid.offerAmount,
|
||||||
status: "closed",
|
status: "closed",
|
||||||
});
|
});
|
||||||
const newUserCoins = seller.coins + lastBid.offer_amount;
|
const newUserCoins = seller.coins + lastBid.offerAmount;
|
||||||
updateUserCoins.run({ id: seller.id, coins: newUserCoins });
|
await userService.updateUserCoins(seller.id, newUserCoins);
|
||||||
await emitMarketUpdate();
|
await emitMarketUpdate();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[Market Cron] Error processing offer ID: ${offer.id}`, e);
|
console.error(`[Market Cron] Error processing offer ID: ${offer.id}`, e);
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import { getMarketOfferById, getOfferBids, getSkin, getUser } from "../database/index.js";
|
import * as userService from "../services/user.service.js";
|
||||||
|
import * as skinService from "../services/skin.service.js";
|
||||||
|
import * as marketService from "../services/market.service.js";
|
||||||
import { EmbedBuilder } from "discord.js";
|
import { EmbedBuilder } from "discord.js";
|
||||||
|
|
||||||
export async function handleNewMarketOffer(offerId, client) {
|
export async function handleNewMarketOffer(offerId, client) {
|
||||||
const offer = getMarketOfferById.get(offerId);
|
const offer = await marketService.getMarketOfferById(offerId);
|
||||||
if (!offer) return;
|
if (!offer) return;
|
||||||
const skin = getSkin.get(offer.skin_uuid);
|
const skin = await skinService.getSkin(offer.skinUuid);
|
||||||
|
|
||||||
const discordUserSeller = await client.users.fetch(offer.seller_id);
|
const discordUserSeller = await client.users.fetch(offer.sellerId);
|
||||||
try {
|
try {
|
||||||
const userSeller = getUser.get(offer.seller_id);
|
const userSeller = await userService.getUser(offer.sellerId);
|
||||||
if (discordUserSeller && userSeller?.isAkhy) {
|
if (discordUserSeller && userSeller?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Offre créée")
|
.setTitle("🔔 Offre créée")
|
||||||
.setDescription(`Ton offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** a bien été créée !`)
|
.setDescription(`Ton offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** a bien été créée !`)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
.addFields(
|
.addFields(
|
||||||
@@ -23,16 +25,16 @@ export async function handleNewMarketOffer(offerId, client) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "💰 Prix de départ",
|
name: "💰 Prix de départ",
|
||||||
value: `\`${offer.starting_price} coins\``,
|
value: `\`${offer.startingPrice} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Ouverture",
|
name: "⏰ Ouverture",
|
||||||
value: `<t:${Math.floor(offer.opening_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.openingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Fermeture",
|
name: "⏰ Fermeture",
|
||||||
value: `<t:${Math.floor(offer.closing_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.closingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "🆔 ID de l’offre",
|
name: "🆔 ID de l’offre",
|
||||||
@@ -53,26 +55,26 @@ export async function handleNewMarketOffer(offerId, client) {
|
|||||||
const guildChannel = await client.channels.fetch(process.env.BOT_CHANNEL_ID);
|
const guildChannel = await client.channels.fetch(process.env.BOT_CHANNEL_ID);
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Nouvelle offre")
|
.setTitle("🔔 Nouvelle offre")
|
||||||
.setDescription(`Une offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** a été créée !`)
|
.setDescription(`Une offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** a été créée !`)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
.addFields(
|
.addFields(
|
||||||
{
|
{
|
||||||
name: "💰 Prix de départ",
|
name: "💰 Prix de départ",
|
||||||
value: `\`${offer.starting_price} coins\``,
|
value: `\`${offer.startingPrice} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Ouverture",
|
name: "⏰ Ouverture",
|
||||||
value: `<t:${Math.floor(offer.opening_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.openingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Fermeture",
|
name: "⏰ Fermeture",
|
||||||
value: `<t:${Math.floor(offer.closing_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.closingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Créée par",
|
name: "Créée par",
|
||||||
value: `<@${offer.seller_id}> ${discordUserSeller ? "(" + discordUserSeller.username + ")" : ""}`,
|
value: `<@${offer.sellerId}> ${discordUserSeller ? "(" + discordUserSeller.username + ")" : ""}`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
@@ -83,18 +85,18 @@ export async function handleNewMarketOffer(offerId, client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function handleMarketOfferOpening(offerId, client) {
|
export async function handleMarketOfferOpening(offerId, client) {
|
||||||
const offer = getMarketOfferById.get(offerId);
|
const offer = await marketService.getMarketOfferById(offerId);
|
||||||
if (!offer) return;
|
if (!offer) return;
|
||||||
const skin = getSkin.get(offer.skin_uuid);
|
const skin = await skinService.getSkin(offer.skinUuid);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const discordUserSeller = await client.users.fetch(offer.seller_id);
|
const discordUserSeller = await client.users.fetch(offer.sellerId);
|
||||||
const userSeller = getUser.get(offer.seller_id);
|
const userSeller = await userService.getUser(offer.sellerId);
|
||||||
if (discordUserSeller && userSeller?.isAkhy) {
|
if (discordUserSeller && userSeller?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Début des enchères")
|
.setTitle("🔔 Début des enchères")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Les enchères sur ton offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** viennent de commencer !`,
|
`Les enchères sur ton offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** viennent de commencer !`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
@@ -106,12 +108,12 @@ export async function handleMarketOfferOpening(offerId, client) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "💰 Prix de départ",
|
name: "💰 Prix de départ",
|
||||||
value: `\`${offer.starting_price} coins\``,
|
value: `\`${offer.startingPrice} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Fermeture",
|
name: "⏰ Fermeture",
|
||||||
value: `<t:${Math.floor(offer.closing_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.closingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "🆔 ID de l’offre",
|
name: "🆔 ID de l’offre",
|
||||||
@@ -133,19 +135,19 @@ export async function handleMarketOfferOpening(offerId, client) {
|
|||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Début des enchères")
|
.setTitle("🔔 Début des enchères")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Les enchères sur l'offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** viennent de commencer !`,
|
`Les enchères sur l'offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** viennent de commencer !`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
.addFields(
|
.addFields(
|
||||||
{
|
{
|
||||||
name: "💰 Prix de départ",
|
name: "💰 Prix de départ",
|
||||||
value: `\`${offer.starting_price} coins\``,
|
value: `\`${offer.startingPrice} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Fermeture",
|
name: "⏰ Fermeture",
|
||||||
value: `<t:${Math.floor(offer.closing_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.closingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
@@ -156,19 +158,19 @@ export async function handleMarketOfferOpening(offerId, client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function handleMarketOfferClosing(offerId, client) {
|
export async function handleMarketOfferClosing(offerId, client) {
|
||||||
const offer = getMarketOfferById.get(offerId);
|
const offer = await marketService.getMarketOfferById(offerId);
|
||||||
if (!offer) return;
|
if (!offer) return;
|
||||||
const skin = getSkin.get(offer.skin_uuid);
|
const skin = await skinService.getSkin(offer.skinUuid);
|
||||||
const bids = getOfferBids.all(offer.id);
|
const bids = await marketService.getOfferBids(offer.id);
|
||||||
|
|
||||||
const discordUserSeller = await client.users.fetch(offer.seller_id);
|
const discordUserSeller = await client.users.fetch(offer.sellerId);
|
||||||
try {
|
try {
|
||||||
const userSeller = getUser.get(offer.seller_id);
|
const userSeller = await userService.getUser(offer.sellerId);
|
||||||
if (discordUserSeller && userSeller?.isAkhy) {
|
if (discordUserSeller && userSeller?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Fin des enchères")
|
.setTitle("🔔 Fin des enchères")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Les enchères sur ton offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** viennent de se terminer !`,
|
`Les enchères sur ton offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** viennent de se terminer !`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
@@ -188,11 +190,11 @@ export async function handleMarketOfferClosing(offerId, client) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const highestBid = bids[0];
|
const highestBid = bids[0];
|
||||||
const highestBidderUser = await client.users.fetch(highestBid.bidder_id);
|
const highestBidderUser = await client.users.fetch(highestBid.bidderId);
|
||||||
embed.addFields(
|
embed.addFields(
|
||||||
{
|
{
|
||||||
name: "✅ Enchères terminées avec succès !",
|
name: "✅ Enchères terminées avec succès !",
|
||||||
value: `Ton skin a été vendu pour \`${highestBid.offer_amount} coins\` à <@${highestBid.bidder_id}> ${highestBidderUser ? "(" + highestBidderUser.username + ")" : ""}.`,
|
value: `Ton skin a été vendu pour \`${highestBid.offerAmount} coins\` à <@${highestBid.bidderId}> ${highestBidderUser ? "(" + highestBidderUser.username + ")" : ""}.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "🆔 ID de l’offre",
|
name: "🆔 ID de l’offre",
|
||||||
@@ -216,7 +218,7 @@ export async function handleMarketOfferClosing(offerId, client) {
|
|||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Fin des enchères")
|
.setTitle("🔔 Fin des enchères")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Les enchères sur l'offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** viennent de se terminer !`,
|
`Les enchères sur l'offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** viennent de se terminer !`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
@@ -229,18 +231,18 @@ export async function handleMarketOfferClosing(offerId, client) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const highestBid = bids[0];
|
const highestBid = bids[0];
|
||||||
const highestBidderUser = await client.users.fetch(highestBid.bidder_id);
|
const highestBidderUser = await client.users.fetch(highestBid.bidderId);
|
||||||
embed.addFields({
|
embed.addFields({
|
||||||
name: "✅ Enchères terminées avec succès !",
|
name: "✅ Enchères terminées avec succès !",
|
||||||
value: `Le skin de <@${offer.seller_id}> ${discordUserSeller ? "(" + discordUserSeller.username + ")" : ""} a été vendu pour \`${highestBid.offer_amount} coins\` à <@${highestBid.bidder_id}> ${highestBidderUser ? "(" + highestBidderUser.username + ")" : ""}.`,
|
value: `Le skin de <@${offer.sellerId}> ${discordUserSeller ? "(" + discordUserSeller.username + ")" : ""} a été vendu pour \`${highestBid.offerAmount} coins\` à <@${highestBid.bidderId}> ${highestBidderUser ? "(" + highestBidderUser.username + ")" : ""}.`,
|
||||||
});
|
});
|
||||||
const discordUserBidder = await client.users.fetch(highestBid.bidder_id);
|
const discordUserBidder = await client.users.fetch(highestBid.bidderId);
|
||||||
const userBidder = getUser.get(highestBid.bidder_id);
|
const userBidder = await userService.getUser(highestBid.bidderId);
|
||||||
if (discordUserBidder && userBidder?.isAkhy) {
|
if (discordUserBidder && userBidder?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Fin des enchères")
|
.setTitle("🔔 Fin des enchères")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Les enchères sur l'offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** viennent de se terminer !`,
|
`Les enchères sur l'offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** viennent de se terminer !`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
@@ -248,7 +250,7 @@ export async function handleMarketOfferClosing(offerId, client) {
|
|||||||
const highestBid = bids[0];
|
const highestBid = bids[0];
|
||||||
embed.addFields({
|
embed.addFields({
|
||||||
name: "✅ Enchères terminées avec succès !",
|
name: "✅ Enchères terminées avec succès !",
|
||||||
value: `Tu as acheté ce skin pour \`${highestBid.offer_amount} coins\` à <@${offer.seller_id}> ${discordUserSeller ? "(" + discordUserSeller.username + ")" : ""}. Il a été ajouté à ton inventaire.`,
|
value: `Tu as acheté ce skin pour \`${highestBid.offerAmount} coins\` à <@${offer.sellerId}> ${discordUserSeller ? "(" + discordUserSeller.username + ")" : ""}. Il a été ajouté à ton inventaire.`,
|
||||||
});
|
});
|
||||||
|
|
||||||
discordUserBidder.send({ embeds: [embed] }).catch(console.error);
|
discordUserBidder.send({ embeds: [embed] }).catch(console.error);
|
||||||
@@ -262,39 +264,39 @@ export async function handleMarketOfferClosing(offerId, client) {
|
|||||||
|
|
||||||
export async function handleNewMarketOfferBid(offerId, bidId, client) {
|
export async function handleNewMarketOfferBid(offerId, bidId, client) {
|
||||||
// Notify Seller and Bidder
|
// Notify Seller and Bidder
|
||||||
const offer = getMarketOfferById.get(offerId);
|
const offer = await marketService.getMarketOfferById(offerId);
|
||||||
if (!offer) return;
|
if (!offer) return;
|
||||||
const bid = getOfferBids.get(offerId);
|
const bid = (await marketService.getOfferBids(offerId))[0];
|
||||||
if (!bid) return;
|
if (!bid) return;
|
||||||
const skin = getSkin.get(offer.skin_uuid);
|
const skin = await skinService.getSkin(offer.skinUuid);
|
||||||
|
|
||||||
const bidderUser = client.users.fetch(bid.bidder_id);
|
const bidderUser = client.users.fetch(bid.bidderId);
|
||||||
try {
|
try {
|
||||||
const discordUserSeller = await client.users.fetch(offer.seller_id);
|
const discordUserSeller = await client.users.fetch(offer.sellerId);
|
||||||
const userSeller = getUser.get(offer.seller_id);
|
const userSeller = await userService.getUser(offer.sellerId);
|
||||||
|
|
||||||
if (discordUserSeller && userSeller?.isAkhy) {
|
if (discordUserSeller && userSeller?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Nouvelle enchère")
|
.setTitle("🔔 Nouvelle enchère")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Il y a eu une nouvelle enchère sur ton offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}**.`,
|
`Il y a eu une nouvelle enchère sur ton offre pour le skin **${skin ? skin.displayName : offer.skinUuid}**.`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
.addFields(
|
.addFields(
|
||||||
{
|
{
|
||||||
name: "👤 Enchérisseur",
|
name: "👤 Enchérisseur",
|
||||||
value: `<@${bid.bidder_id}> ${bidderUser ? "(" + bidderUser.username + ")" : ""}`,
|
value: `<@${bid.bidderId}> ${bidderUser ? "(" + bidderUser.username + ")" : ""}`,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "💰 Montant de l’enchère",
|
name: "💰 Montant de l’enchère",
|
||||||
value: `\`${bid.offer_amount} coins\``,
|
value: `\`${bid.offerAmount} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "⏰ Fermeture",
|
name: "⏰ Fermeture",
|
||||||
value: `<t:${Math.floor(offer.closing_at / 1000)}:F>`,
|
value: `<t:${Math.floor(offer.closingAt / 1000)}:F>`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "🆔 ID de l’offre",
|
name: "🆔 ID de l’offre",
|
||||||
@@ -311,19 +313,19 @@ export async function handleNewMarketOfferBid(offerId, bidId, client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const discordUserNewBidder = await client.users.fetch(bid.bidder_id);
|
const discordUserNewBidder = await client.users.fetch(bid.bidderId);
|
||||||
const userNewBidder = getUser.get(bid.bidder_id);
|
const userNewBidder = await userService.getUser(bid.bidderId);
|
||||||
if (discordUserNewBidder && userNewBidder?.isAkhy) {
|
if (discordUserNewBidder && userNewBidder?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Nouvelle enchère")
|
.setTitle("🔔 Nouvelle enchère")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Ton enchère sur l'offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}** a bien été placée!`,
|
`Ton enchère sur l'offre pour le skin **${skin ? skin.displayName : offer.skinUuid}** a bien été placée!`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
.addFields({
|
.addFields({
|
||||||
name: "💰 Montant de l’enchère",
|
name: "💰 Montant de l’enchère",
|
||||||
value: `\`${bid.offer_amount} coins\``,
|
value: `\`${bid.offerAmount} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
})
|
})
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
@@ -335,28 +337,28 @@ export async function handleNewMarketOfferBid(offerId, bidId, client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const offerBids = getOfferBids.all(offer.id);
|
const offerBids = await marketService.getOfferBids(offer.id);
|
||||||
if (offerBids.length < 2) return; // No previous bidder to notify
|
if (offerBids.length < 2) return; // No previous bidder to notify
|
||||||
|
|
||||||
const discordUserPreviousBidder = await client.users.fetch(offerBids[1].bidder_id);
|
const discordUserPreviousBidder = await client.users.fetch(offerBids[1].bidderId);
|
||||||
const userPreviousBidder = getUser.get(offerBids[1].bidder_id);
|
const userPreviousBidder = await userService.getUser(offerBids[1].bidderId);
|
||||||
if (discordUserPreviousBidder && userPreviousBidder?.isAkhy) {
|
if (discordUserPreviousBidder && userPreviousBidder?.isAkhy) {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle("🔔 Nouvelle enchère")
|
.setTitle("🔔 Nouvelle enchère")
|
||||||
.setDescription(
|
.setDescription(
|
||||||
`Quelqu'un a surenchéri sur l'offre pour le skin **${skin ? skin.displayName : offer.skin_uuid}**, tu n'es plus le meilleur enchérisseur !`,
|
`Quelqu'un a surenchéri sur l'offre pour le skin **${skin ? skin.displayName : offer.skinUuid}**, tu n'es plus le meilleur enchérisseur !`,
|
||||||
)
|
)
|
||||||
.setThumbnail(skin.displayIcon)
|
.setThumbnail(skin.displayIcon)
|
||||||
.setColor(0x5865f2) // Discord blurple
|
.setColor(0x5865f2) // Discord blurple
|
||||||
.addFields(
|
.addFields(
|
||||||
{
|
{
|
||||||
name: "👤 Enchérisseur",
|
name: "👤 Enchérisseur",
|
||||||
value: `<@${bid.bidder_id}> ${bidderUser ? "(" + bidderUser.username + ")" : ""}`,
|
value: `<@${bid.bidderId}> ${bidderUser ? "(" + bidderUser.username + ")" : ""}`,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "💰 Montant de l’enchère",
|
name: "💰 Montant de l’enchère",
|
||||||
value: `\`${bid.offer_amount} coins\``,
|
value: `\`${bid.offerAmount} coins\``,
|
||||||
inline: true,
|
inline: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -373,7 +375,7 @@ export async function handleNewMarketOfferBid(offerId, bidId, client) {
|
|||||||
|
|
||||||
export async function handleCaseOpening(caseType, userId, skinUuid, client) {
|
export async function handleCaseOpening(caseType, userId, skinUuid, client) {
|
||||||
const discordUser = await client.users.fetch(userId);
|
const discordUser = await client.users.fetch(userId);
|
||||||
const skin = getSkin.get(skinUuid);
|
const skin = await skinService.getSkin(skinUuid);
|
||||||
try {
|
try {
|
||||||
const guildChannel = await client.channels.fetch(process.env.BOT_CHANNEL_ID);
|
const guildChannel = await client.channels.fetch(process.env.BOT_CHANNEL_ID);
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
|
|||||||
Reference in New Issue
Block a user