fix: clean up fight event listeners after completion (BUG-12)

Move fightEvents.cleanup(fightId) to finally block to ensure
cleanup runs even if post-fight operations (bets, payouts) fail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 08:39:42 +00:00
co-authored by Claude Opus 4.6
parent 0f606cc991
commit 0f95cbf881
+49 -47
View File
@@ -555,61 +555,63 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
potSats: mode === 'ranked' ? 42 : 0,
})
// Publish notable results to Nostr
if (winnerId) {
const winner = winnerId === botA.id ? botA : botB
const loser = winnerId === botA.id ? botB : botA
const isUpset = loser.eloRating - winner.eloRating > 150
const isKO = (winnerId === botA.id && hpB <= 0) || (winnerId === botB.id && hpA <= 0)
publishFightResult({
fightId, winnerName: winner.name, loserName: loser.name, winnerId,
winnerElo: newWinnerEloFinal, loserElo: newLoserEloFinal,
winnerEloChange, loserEloChange,
isPerfect: !!isPerfect, isKO, isUpset,
totalRounds: lastRound, arena: arena.name,
}).catch(err => console.warn('[nostr] publish failed:', err))
}
// Settle bets
try {
const settlements = await settleBets(fightId, winnerId)
for (const s of settlements) {
db.update(schema.bets).set({
status: s.won ? 'won' : winnerId ? 'lost' : 'refunded',
payoutSats: s.payoutSats,
payoutToken: s.payoutToken,
settledAt: new Date().toISOString(),
}).where(eq(schema.bets.id, s.betId)).run()
// Publish notable results to Nostr
if (winnerId) {
const winner = winnerId === botA.id ? botA : botB
const loser = winnerId === botA.id ? botB : botA
const isUpset = loser.eloRating - winner.eloRating > 150
const isKO = (winnerId === botA.id && hpB <= 0) || (winnerId === botB.id && hpA <= 0)
publishFightResult({
fightId, winnerName: winner.name, loserName: loser.name, winnerId,
winnerElo: newWinnerEloFinal, loserElo: newLoserEloFinal,
winnerEloChange, loserEloChange,
isPerfect: !!isPerfect, isKO, isUpset,
totalRounds: lastRound, arena: arena.name,
}).catch(err => console.warn('[nostr] publish failed:', err))
}
} catch (err) {
console.error(`[betting] settlement failed for fight ${fightId}:`, err)
}
// Ranked fight payout
if (mode === 'ranked') {
// Dev mode: always pay the human bot (not mock), regardless of win/loss
const devMode = process.env.NODE_ENV !== 'production'
const isMockA = botA.webhookUrl.startsWith('http://mock.local')
const isMockB = botB.webhookUrl.startsWith('http://mock.local')
const humanBotId = devMode ? (isMockA ? botB.id : isMockB ? botA.id : winnerId) : winnerId
// Settle bets
try {
const settlements = await settleBets(fightId, winnerId)
for (const s of settlements) {
db.update(schema.bets).set({
status: s.won ? 'won' : winnerId ? 'lost' : 'refunded',
payoutSats: s.payoutSats,
payoutToken: s.payoutToken,
settledAt: new Date().toISOString(),
}).where(eq(schema.bets.id, s.betId)).run()
}
} catch (err) {
console.error(`[betting] settlement failed for fight ${fightId}:`, err)
}
if (humanBotId) {
payWinner(fightId, humanBotId).catch(err => {
console.error(`[payments] payout failed for fight ${fightId}:`, err)
})
} else if (!winnerId) {
// Draw — refund both entry fees
const entryPayments = await db.select().from(schema.payments)
.where(sql`${schema.payments.fightId} = ${fightId} AND ${schema.payments.direction} = 'in' AND ${schema.payments.status} = 'confirmed'`)
for (const payment of entryPayments) {
refundEntry(payment.id).catch(err => {
console.error(`[payments] draw refund failed for ${payment.id}:`, err)
// Ranked fight payout
if (mode === 'ranked') {
// Dev mode: always pay the human bot (not mock), regardless of win/loss
const devMode = process.env.NODE_ENV !== 'production'
const isMockA = botA.webhookUrl.startsWith('http://mock.local')
const isMockB = botB.webhookUrl.startsWith('http://mock.local')
const humanBotId = devMode ? (isMockA ? botB.id : isMockB ? botA.id : winnerId) : winnerId
if (humanBotId) {
payWinner(fightId, humanBotId).catch(err => {
console.error(`[payments] payout failed for fight ${fightId}:`, err)
})
} else if (!winnerId) {
// Draw — refund both entry fees
const entryPayments = await db.select().from(schema.payments)
.where(sql`${schema.payments.fightId} = ${fightId} AND ${schema.payments.direction} = 'in' AND ${schema.payments.status} = 'confirmed'`)
for (const payment of entryPayments) {
refundEntry(payment.id).catch(err => {
console.error(`[payments] draw refund failed for ${payment.id}:`, err)
})
}
}
}
} finally {
fightEvents.cleanup(fightId)
}
fightEvents.cleanup(fightId)
}
export async function runFight(botAId: string, botBId: string, mode: 'free' | 'ranked' = 'free'): Promise<string> {