diff --git a/src/api/discord.js b/src/api/discord.js index 41eb7c9..4bbddcb 100644 --- a/src/api/discord.js +++ b/src/api/discord.js @@ -36,7 +36,7 @@ export async function DiscordRequest(endpoint, options) { } catch (err) { data = res; } - console.error(`[${Date.now()}] Discord API Error on endpoint ${endpoint}:`, res.status, data); + console.error(`Discord API Error on endpoint ${endpoint}:`, res.status, data); throw new Error(JSON.stringify(data)); } diff --git a/src/bot/handlers/messageCreate.js b/src/bot/handlers/messageCreate.js index 969e2cc..e33aecc 100644 --- a/src/bot/handlers/messageCreate.js +++ b/src/bot/handlers/messageCreate.js @@ -96,7 +96,7 @@ async function handleAiMention(message, client, io) { const timestamps = (requestTimestamps.get(authorId) || []).filter((ts) => now - ts < SPAM_INTERVAL); if (timestamps.length >= MAX_REQUESTS_PER_INTERVAL) { - console.log(`[${Date.now()}] Rate limit exceeded for ${authorDB.username}`); + console.log(`Rate limit exceeded for ${authorDB.username}`); if (!authorDB.warned) { await message.reply(`T'abuses fréro, attends un peu ⏳`).catch(console.error); } diff --git a/src/database/index.js b/src/database/index.js index eacd1ec..4babb87 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -748,7 +748,7 @@ export const updateManyUsers = flopoDB.transaction((users) => { try { updateUser.run(user); } catch (e) { - console.log(`[${Date.now()}] user update failed`); + console.log(`User update failed`); } }); diff --git a/src/game/elo.js b/src/game/elo.js index 02ffaaf..c42b8fa 100644 --- a/src/game/elo.js +++ b/src/game/elo.js @@ -15,7 +15,7 @@ export async function eloHandler(p1Id, p2Id, p1Score, p2Score, type) { const p1DB = getUser.get(p1Id); const p2DB = getUser.get(p2Id); if (!p1DB || !p2DB) { - console.error(`[${Date.now()}] 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; } @@ -51,8 +51,8 @@ export async function eloHandler(p1Id, p2Id, p1Score, p2Score, type) { const finalP1Elo = Math.max(0, p1NewElo); const finalP2Elo = Math.max(0, p2NewElo); - console.log(`[${Date.now()}] Elo Update (${type}) for ${p1DB.globalName}: ${p1CurrentElo} -> ${finalP1Elo}`); - console.log(`[${Date.now()}] Elo Update (${type}) for ${p2DB.globalName}: ${p2CurrentElo} -> ${finalP2Elo}`); + console.log(`Elo Update (${type}) for ${p1DB.globalName}: ${p1CurrentElo} -> ${finalP1Elo}`); + console.log(`Elo Update (${type}) for ${p2DB.globalName}: ${p2CurrentElo} -> ${finalP2Elo}`); try { const generalChannel = await client.channels.fetch(process.env.BOT_CHANNEL_ID); const user1 = await client.users.fetch(p1Id); @@ -70,7 +70,7 @@ export async function eloHandler(p1Id, p2Id, p1Score, p2Score, type) { .setColor("#5865f2"); await generalChannel.send({ embeds: [embed] }); } catch (e) { - console.error(`[${Date.now()}] Failed to post elo update message`, e); + console.error(`Failed to post elo update message`, e); } // --- 4. Update Database --- @@ -156,7 +156,7 @@ export async function pokerEloHandler(room) { timestamp: Date.now(), }); } else { - console.error(`[${Date.now()}] Error calculating new Elo for ${player.globalName}.`); + console.error(`Error calculating new Elo for ${player.globalName}.`); } }); } diff --git a/src/game/points.js b/src/game/points.js index dbee347..8ae58b9 100644 --- a/src/game/points.js +++ b/src/game/points.js @@ -10,8 +10,9 @@ import { pruneOldLogs, updateUserCoins } from "../database/index.js"; -import { activeSlowmodes, messagesTimestamps, skins } from "./state.js"; +import { activeSlowmodes, activeSolitaireGames, messagesTimestamps, skins } from "./state.js"; import { createDeck, createSeededRNG, deal, seededShuffle } from "./solitaire.js"; +import { emitSolitaireUpdate } from "../server/socket.js"; /** * Handles awarding points (coins) to users for their message activity. @@ -88,7 +89,7 @@ export async function slowmodesHandler(message) { // Check if the slowmode duration has passed if (now > authorSlowmode.endAt) { - console.log(`[${Date.now()}] Slowmode for ${author.username} has expired.`); + console.log(`Slowmode for ${author.username} has expired.`); delete activeSlowmodes[author.id]; return { deleted: false, expired: true }; } @@ -97,10 +98,10 @@ export async function slowmodesHandler(message) { if (authorSlowmode.lastMessage && now - authorSlowmode.lastMessage < 60 * 1000) { try { await message.delete(); - console.log(`[${Date.now()}] Deleted a message from slowmoded user: ${author.username}`); + console.log(`Deleted a message from slowmoded user: ${author.username}`); return { deleted: true, expired: false }; } catch (err) { - console.error(`[${Date.now()}] Failed to delete slowmode message:`, err); + console.error(`Failed to delete slowmode message:`, err); return { deleted: false, expired: false }; } } else { @@ -144,16 +145,20 @@ export function randomSkinPrice() { * This function clears previous stats, awards the winner, and generates a new daily seed. */ export function initTodaysSOTD() { - console.log(`[${Date.now()}] Initializing new Solitaire of the Day...`); + console.log(`Initializing new Solitaire of the Day...`); // 1. Award previous day's winner const rankings = getAllSOTDStats.all(); if (rankings.length > 0) { const winnerId = rankings[0].user_id; + const secondPlaceId = rankings[1] ? rankings[1].user_id : null; + const thirdPlaceId = rankings[2] ? rankings[2].user_id : null; const winnerUser = getUser.get(winnerId); + const secondPlaceUser = secondPlaceId ? getUser.get(secondPlaceId) : null; + const thirdPlaceUser = thirdPlaceId ? getUser.get(thirdPlaceId) : null; if (winnerUser) { - const reward = 1000; + const reward = 2500; const newCoinTotal = winnerUser.coins + reward; updateUserCoins.run({ id: winnerId, coins: newCoinTotal }); insertLog.run({ @@ -165,21 +170,40 @@ export function initTodaysSOTD() { user_new_amount: newCoinTotal, }); console.log( - `[${Date.now()}] ${winnerUser.globalName || winnerUser.username} won the previous SOTD and received ${reward} coins.`, + `${winnerUser.globalName || winnerUser.username} won the previous SOTD and received ${reward} coins.`, ); - insertGame.run({ - id: `${winnerId}-${Date.now()}`, - p1: winnerId, - p2: null, - p1_score: rankings[0].score, - p2_score: null, - p1_elo: winnerUser.elo, - p2_elo: null, - p1_new_elo: winnerUser.elo, - p2_new_elo: null, - type: "SOTD", - timestamp: Date.now(), + } + if (secondPlaceUser) { + const reward = 1500; + const newCoinTotal = secondPlaceUser.coins + reward; + updateUserCoins.run({ id: secondPlaceId, coins: newCoinTotal }); + insertLog.run({ + id: `${secondPlaceId}-sotd-second-${Date.now()}`, + target_user_id: null, + user_id: secondPlaceId, + action: "SOTD_SECOND_PLACE", + coins_amount: reward, + user_new_amount: newCoinTotal, }); + console.log( + `${secondPlaceUser.globalName || secondPlaceUser.username} got second place in the previous SOTD and received ${reward} coins.`, + ); + } + if (thirdPlaceUser) { + const reward = 750; + const newCoinTotal = thirdPlaceUser.coins + reward; + updateUserCoins.run({ id: thirdPlaceId, coins: newCoinTotal }); + insertLog.run({ + id: `${thirdPlaceId}-sotd-third-${Date.now()}`, + target_user_id: null, + user_id: thirdPlaceId, + action: "SOTD_THIRD_PLACE", + coins_amount: reward, + user_new_amount: newCoinTotal, + }); + console.log( + `${thirdPlaceUser.globalName || thirdPlaceUser.username} got third place in the previous SOTD and received ${reward} coins.`, + ); } } @@ -206,8 +230,15 @@ export function initTodaysSOTD() { wastePile: JSON.stringify(todaysSOTD.wastePile), seed: newRandomSeed, }); - console.log(`[${Date.now()}] Today's SOTD is ready with a new seed.`); + for (const [userId, gameData] of Object.entries(activeSolitaireGames)) { + if (gameData.isSOTD) { + delete activeSolitaireGames[userId]; + emitSolitaireUpdate(userId); + } + } + + console.log(`Today's SOTD is ready with a new seed.`); } catch (e) { - console.error(`[${Date.now()}] Error saving new SOTD to database:`, e); + console.error(`Error saving new SOTD to database:`, e); } } diff --git a/src/server/routes/api.js b/src/server/routes/api.js index 207a255..bc36262 100644 --- a/src/server/routes/api.js +++ b/src/server/routes/api.js @@ -27,7 +27,7 @@ import { import { activePolls, activePredis, activeSlowmodes, skins } from "../../game/state.js"; // --- Utility and API Imports --- -import { formatTime } from "../../utils/index.js"; +import { formatTime, isMeleeSkin, isVCTSkin, isChampionsSkin, getVCTRegion } from "../../utils/index.js"; import { DiscordRequest } from "../../api/discord.js"; // --- Discord.js Builder Imports --- @@ -99,11 +99,11 @@ export function apiRoutes(client, io) { user_new_amount: 5000, }); - console.log(`[${Date.now()}] New registered user: ${discordUser.username} (${discordUser.id})`); + console.log(`New registered user: ${discordUser.username} (${discordUser.id})`); res.status(200).json({ message: `Bienvenue ${discordUser.username} !` }); } catch (e) { - console.log(`[${Date.now()}] Failed to register user ${discordUser.username} (${discordUser.id})`, e); + console.log(`Failed to register user ${discordUser.username} (${discordUser.id})`, e); res.status(500).json({ error: "Erreur lors de la création du nouvel utilisateur." }); } }); @@ -173,8 +173,18 @@ export function apiRoutes(client, io) { ); const updatedSkin = getSkin.get(result.randomSkinData.uuid); await handleCaseOpening(caseType, userId, result.randomSelectedSkinUuid, client); + + const contentSkins = selectedSkins.map((item) => { + return { + ...item, + isMelee: isMeleeSkin(item.displayName), + isVCT: isVCTSkin(item.displayName), + isChampions: isChampionsSkin(item.displayName), + vctRegion: getVCTRegion(item.displayName), + } + }); res.json({ - selectedSkins, + selectedSkins: contentSkins, randomSelectedSkinUuid: result.randomSelectedSkinUuid, randomSelectedSkinIndex: result.randomSelectedSkinIndex, updatedSkin, @@ -475,6 +485,10 @@ export function apiRoutes(client, io) { }); }); skin.offers = marketOffers || {}; + skin.isMelee = isMeleeSkin(skin.displayName); + skin.isVCT = isVCTSkin(skin.displayName); + skin.isChampions = isChampionsSkin(skin.displayName); + skin.vctRegion = getVCTRegion(skin.displayName); }); res.json({ inventory }); } catch (error) { @@ -560,7 +574,7 @@ export function apiRoutes(client, io) { user_new_amount: newCoins, }); - console.log(`[${Date.now()}] ${commandUserId} change nickname of ${userId}: ${old_nickname} -> ${nickname}`); + console.log(`${commandUserId} change nickname of ${userId}: ${old_nickname} -> ${nickname}`); try { const generalChannel = await guild.channels.fetch(process.env.GENERAL_CHANNEL_ID); @@ -1065,7 +1079,7 @@ export function apiRoutes(client, io) { user_new_amount: tempUser.coins + v.amount, }); } catch (e) { - console.log(`[${Date.now()}] Impossible de rembourser ${v.id} (${v.amount} coins)`); + console.log(`Impossible de rembourser ${v.id} (${v.amount} coins)`); } }); activePredis[predi].options[1].votes.forEach((v) => { @@ -1084,7 +1098,7 @@ export function apiRoutes(client, io) { user_new_amount: tempUser.coins + v.amount, }); } catch (e) { - console.log(`[${Date.now()}] Impossible de rembourser ${v.id} (${v.amount} coins)`); + console.log(`Impossible de rembourser ${v.id} (${v.amount} coins)`); } }); activePredis[predi].closed = true; @@ -1110,7 +1124,7 @@ export function apiRoutes(client, io) { user_new_amount: tempUser.coins + v.amount * (1 + ratio), }); } catch (e) { - console.log(`[${Date.now()}] Impossible de créditer ${v.id} (${v.amount} coins pariés, *${1 + ratio})`); + console.log(`Impossible de créditer ${v.id} (${v.amount} coins pariés, *${1 + ratio})`); } }); activePredis[predi].paidTime = new Date(); diff --git a/src/server/routes/blackjack.js b/src/server/routes/blackjack.js index 10affa3..5dc198c 100644 --- a/src/server/routes/blackjack.js +++ b/src/server/routes/blackjack.js @@ -83,12 +83,12 @@ export function blackjackRoutes(io) { h.surrendered = true; h.stood = true; h.hasActed = true; - room.leavingAfterRound[p.id] = true; // kick at end of round + //room.leavingAfterRound[p.id] = true; // kick at end of round emitToast({ type: "player-timeout", userId: p.id }); changed = true; } else if (h.hasActed && !h.stood) { h.stood = true; - room.leavingAfterRound[p.id] = true; // kick at end of round + //room.leavingAfterRound[p.id] = true; // kick at end of round emitToast({ type: "player-auto-stand", userId: p.id }); changed = true; } @@ -183,7 +183,7 @@ export function blackjackRoutes(io) { router.post("/leave", async (req, res) => { const { userId } = req.body; - if (!userId || !room.players[userId]) return res.status(404).json({ message: "not in room" }); + if (!userId || !room.players[userId]) return res.status(403).json({ message: "not in room" }); try { const guild = await client.guilds.fetch(process.env.GUILD_ID); @@ -211,7 +211,7 @@ export function blackjackRoutes(io) { } const p = room.players[userId]; - if (p.inRound) { + if (p?.inRound) { // leave after round to avoid abandoning an active bet room.leavingAfterRound[userId] = true; return res.status(200).json({ message: "will-leave-after-round" }); diff --git a/src/utils/index.js b/src/utils/index.js index 24841c8..0e2c5cd 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -446,3 +446,51 @@ function formatTierText(rank, displayName) { if (displayName.toLowerCase().includes("champions")) res += " | Champions"; return res; } + +export function isMeleeSkin(skinName) { + const name = skinName.toLowerCase(); + return !(name.includes("classic") || name.includes("shorty") || name.includes("frenzy") || name.includes("ghost") || name.includes("sheriff") || name.includes("stinger") || name.includes("spectre") || + name.includes("bucky") || name.includes("judge") || name.includes("bulldog") || name.includes("guardian") || + name.includes("vandal") || name.includes("phantom") || name.includes("marshal") || name.includes("outlaw") || + name.includes("operator") || name.includes("ares") || name.includes("odin")); +} + +export function isVCTSkin(skinName) { + const name = skinName.toLowerCase(); + return name.includes("vct"); +} + +const VCT_TEAMS = { + "vct-am": [ + /x 100t\)$/g, /x c9\)$/g, /x eg\)$/g, /x fur\)$/g, /x krü\)$/g, /x lev\)$/g, /x loud\)$/g, + /x mibr\)$/g, /x sen\)$/g, /x nrg\)$/g, /x g2\)$/g, /x nv\)$/g, /x 2g\)$/g + ], + "vct-emea": [ + /x bbl\)$/g, /x fnc\)$/g, /x fut\)$/g, /x m8\)$/g, /x gx\)$/g, /x kc\)$/g, /x navi\)$/g, + /x th\)$/g, /x tl\)$/g, /x vit\)$/g, /x ulf\)$/g, /x pcf\)$/g, /x koi\)$/g, /x apk\)$/g + ], + "vct-pcf": [ + /x dfm\)$/g, /x drx\)$/g, /x fs\)$/g, /x gen\)$/g, /x ge\)$/g, /x prx\)$/g, /x rrq\)$/g, + /x t1\)$/g, /x ts\)$/g, /x zeta\)$/g, /x vl\)$/g, /x ns\)$/g, /x tln\)$/g, /x boom\)$/g, /x bld\)$/g + ], + "vct-cn": [ + /x ag\)$/g, /x blg\)$/g, /x edg\)$/g, /x fpx\)$/g, /x jdg\)$/g, /x nova\)$/g, /x tec\)$/g, + /x te\)$/g, /x tyl\)$/g, /x wol\)$/g, /x xlg\)$/g, /x xlg\)$/g, /x drg\)$/g, /x drg\)$/g + ] +}; + +export function getVCTRegion(skinName) { + if (!isVCTSkin(skinName)) return null; + const name = skinName.toLowerCase().trim(); + for (const [region, regexes] of Object.entries(VCT_TEAMS)) { + if (regexes.some(regex => regex.test(name))) { + return region; + } + } + return null; +} + +export function isChampionsSkin(skinName) { + const name = skinName.toLowerCase(); + return name.includes("champions"); +} \ No newline at end of file