feat: publish notable fight results to Nostr relays
NIP-01 kind:1 notes posted for KOs, perfects, upsets, and big Elo swings. Includes bot names, Elo changes, arena, and replay link. Configurable via BOTFIGHTS_NOSTR_RELAYS and BOTFIGHTS_NOSTR_NSEC env vars. Silently skips if NSEC not set. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
5e598e3987
commit
7761f713d7
@@ -11,6 +11,7 @@ import { setCooldown } from './queue.js'
|
||||
import { isHumanPlayer, waitForHumanResponse } from './human-responses.js'
|
||||
import { payWinner, refundEntry, ENTRY_FEE_SATS } from './payments.js'
|
||||
import { settleBets, lockBets } from './betting.js'
|
||||
import { publishFightResult } from './nostr-publish.js'
|
||||
|
||||
interface BotRecord {
|
||||
id: string
|
||||
@@ -333,12 +334,14 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
||||
let comboA = 0
|
||||
let comboB = 0
|
||||
let winnerId: string | null = null
|
||||
let lastRound = 0
|
||||
const usedTypes = new Set<string>()
|
||||
|
||||
// Pick a random round for retro mode (rounds 3-8, ensuring it's not too early or late)
|
||||
const retroRound = 3 + Math.floor(Math.random() * Math.min(6, MAX_ROUNDS - 4))
|
||||
|
||||
for (let round = 1; round <= MAX_ROUNDS; round++) {
|
||||
lastRound = round
|
||||
const challenge = round === retroRound
|
||||
? generateRetroChallenge()
|
||||
: pickChallenge(usedTypes, arena.modifier)
|
||||
@@ -447,6 +450,10 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
||||
// Finalize fight + update bot stats atomically
|
||||
const isMockFight = isMockBot(botA.webhookUrl) || isMockBot(botB.webhookUrl) || isClassicBot(botA.webhookUrl) || isClassicBot(botB.webhookUrl)
|
||||
const kFactor = isMockFight ? 12 : 32 // Dampened Elo for mock/classic fights
|
||||
let winnerEloChange = 0
|
||||
let loserEloChange = 0
|
||||
let newWinnerEloFinal = 0
|
||||
let newLoserEloFinal = 0
|
||||
|
||||
const finalize = sqlite.transaction(() => {
|
||||
// Mark fight finished
|
||||
@@ -463,6 +470,10 @@ async function executeFightRounds(fightId: string, botA: BotRecord, botB: BotRec
|
||||
const loser = winnerId === botA.id ? botB : botA
|
||||
|
||||
const { newWinnerElo, newLoserElo } = calculateElo(winner.eloRating, loser.eloRating, kFactor)
|
||||
winnerEloChange = Math.round(newWinnerElo - winner.eloRating)
|
||||
loserEloChange = Math.round(newLoserElo - loser.eloRating)
|
||||
newWinnerEloFinal = newWinnerElo
|
||||
newLoserEloFinal = newLoserElo
|
||||
const newWinStreak = winner.winStreak + 1
|
||||
const newBestStreak = Math.max(winner.bestStreak, newWinStreak)
|
||||
|
||||
@@ -510,6 +521,21 @@ 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)
|
||||
|
||||
Reference in New Issue
Block a user