Files
ShaiBot/handlers/poker.js
T
2026-09-08 21:49:43 +02:00

314 lines
6.9 KiB
JavaScript

const {
getPokerService
} = require("../utils/crumbPoker");
const {
refreshPokerTableMessage
} = require("../utils/pokerDiscord");
const {
announcePokerHandProgress,
createPokerProfileContext,
recordPokerHandProfiles
} = require("../utils/pokerProfile");
const TURN_CHECK_INTERVAL =
5 * 1000;
const LOBBY_CHECK_INTERVAL =
30 * 1000;
const STARTUP_RECOVERY_DELAY =
10 * 1000;
module.exports = client => {
setTimeout(
() => {
recoverPoker(
client
);
},
STARTUP_RECOVERY_DELAY
);
setInterval(
() => {
processPokerTurns(
client
);
},
TURN_CHECK_INTERVAL
);
setInterval(
() => {
processPokerLobbies(
client
);
},
LOBBY_CHECK_INTERVAL
);
console.log(
"[Poker] Recovery and timeout watcher initialized"
);
};
async function refreshResultGame(
client,
result
) {
const game =
result?.game;
if (!game) {
return;
}
await refreshPokerTableMessage(
client,
game
);
}
async function processPokerTurns(
client
) {
if (!client.isReady()) {
return;
}
try {
const results =
await getPokerService()
.processExpiredTurns();
for (const result of results) {
if (result.processed) {
console.log(
`[Poker] Auto-folded ${result.userId} after turn timeout.`
);
await refreshResultGame(
client,
result
);
if (
result.game
?.data
?.hand
?.state ===
"PAYOUT"
) {
try {
await recordPokerHandProfiles(
result.game
);
const context =
await createPokerProfileContext(
client,
result.game
);
if (context) {
await announcePokerHandProgress(
context,
result.game
);
}
}
catch (error) {
console.error(
`[PokerProfile] Timeout profile processing failed for ${result.game.gameId}:`,
error
);
}
}
}
}
}
catch (error) {
console.error(
"[Poker] Turn watcher error:",
error
);
}
}
async function processPokerLobbies(
client
) {
if (!client.isReady()) {
return;
}
try {
const results =
await getPokerService()
.processExpiredLobbies();
for (const result of results) {
if (result.expired) {
console.log(
`[Poker] Expired waiting Poker table ${result.game?.gameId ?? "unknown"}.`
);
await refreshResultGame(
client,
result
);
}
}
}
catch (error) {
console.error(
"[Poker] Lobby watcher error:",
error
);
}
}
async function recoverPoker(
client
) {
if (!client.isReady()) {
return;
}
try {
const service =
getPokerService();
const result =
await service.recover();
const activeTables =
await service.listActiveTables();
/*
* Rehydrate every still-active public Poker table after
* restart, even when no recovery action was necessary.
* Mongo remains authoritative; the Discord embed is only
* a projection of the current stored game state.
*/
for (const game of activeTables) {
try {
await refreshPokerTableMessage(
client,
game
);
if (
game.data?.hand?.state ===
"PAYOUT"
) {
await recordPokerHandProfiles(
game
);
const context =
await createPokerProfileContext(
client,
game
);
if (context) {
await announcePokerHandProgress(
context,
game
);
}
}
}
catch (error) {
console.error(
`[Poker] Failed to refresh active table ${game.gameId}:`,
error
);
}
}
for (
const turn
of result.turns ?? []
) {
if (turn.processed) {
await refreshResultGame(
client,
turn
);
}
}
for (
const lobby
of result.lobbies ?? []
) {
if (lobby.expired) {
await refreshResultGame(
client,
lobby
);
}
}
for (
const closing
of result.closingTables ?? []
) {
await refreshResultGame(
client,
closing
);
}
for (
const cashout
of result.cashouts ?? []
) {
try {
const game =
await service.getTable({
gameId:
cashout.gameId
});
await refreshPokerTableMessage(
client,
game
);
}
catch {}
}
console.log(
`[Poker] Recovery complete: ` +
`${result.memberships.length} membership(s), ` +
`${result.cashouts.length} cash-out(s), ` +
`${result.closingTables.length} closing table(s), ` +
`${result.turns.length} expired turn(s), ` +
`${result.lobbies.length} expired lobby/lobbies, ` +
`${activeTables.length} active table(s) refreshed.`
);
}
catch (error) {
console.error(
"[Poker] Startup recovery error:",
error
);
}
}