Pular para o conteúdo principal

Eventos

Toda mudança relevante numa batalha publica um evento. O mesmo vocabulário alimenta três consumidores, e cada um enxerga um subconjunto diferente.

O envelope

Todo evento, em todo canal, tem o mesmo formato:

{
"type": "spin.started",
"scope": "user",
"battle_id": "btl_01J8ZQ...",
"user_id": "seat_2",
"is_bot": false,
"ts": "2026-08-03T02:14:07.113Z",
"data": { }
}
CampoNotas
typeDo vocabulário abaixo
scopebattle ou user: se descreve a batalha inteira ou um assento
battle_idSempre presente
user_idPresente em eventos de escopo user. A referência do assento
is_botPresente em eventos de escopo user
tsUTC, RFC 3339
dataPayload específico do tipo. Ausente quando o tipo não carrega nenhum

O vocabulário

Escopo de batalha: a batalha inteira

TipoEmitido quando
lobby.updatedUm assento mudou: alguém entrou, saiu, promoveu, ou bots foram sentados
battle.startedA batalha saiu de OPEN e os assentos foram despachados
battle.completedTodos os assentos reportaram
battle.failedO lançamento falhou, um assento falhou sem recuperação, ou uma janela expirou
battle.cancelledO criador cancelou ou saiu, ou a janela do lobby expirou em OPEN

Escopo de usuário: um assento

TipoEmitido quando
user.startedO gravador do assento começou o trabalho
browser.readyO browser headless carregou o jogo
capture.startedA captura de vídeo começou
capture.armedO bônus começou, e a captura está gravando a rodada
click.started / click.done / click.failedUma interação roteirizada com o jogo
spin.startedUm giro começou
segment.newUm novo segmento de vídeo HLS está disponível
segment.failedUm segmento não pôde ser produzido ou enviado
playlist.updateA playlist HLS mudou
user.completedO assento terminou com resultado
user.failedO assento falhou
user.stoppedO assento foi parado antes de terminar

Só oito eventos chegam a um browser

O feed WebSocket é uma visão filtrada do vocabulário. Exatamente estes oito são públicos:

Públicos no WebSocket
lobby.updatedbattle.started
battle.completedbattle.failed
battle.cancelledspin.started
segment.newplaylist.update

Todo o resto, incluindo browser.ready, capture.*, click.*, user.* e segment.failed, é interno. Existe para operadores e para a coordenação do próprio gravador, e nenhum token de viewer vai expor isso.

Construa o seu front end contra esses oito. lobby.updated e battle.* dirigem o estado, spin.started dirige a animação por assento, e segment.new e playlist.update dirigem o player de vídeo.

user.completed não é público

Dados terminais de assento chegam ao seu backend pelo webhook, que é assinado e entregue de forma durável. Publicar desfechos por assento para todo browser conectado colocaria resultados nas mãos da plateia antes da sua própria liquidação, por um canal mais fraco. O evento terminal da batalha carrega o que a plateia pode saber.

Três consumidores, três subconjuntos

ConsumidorRecebeTransporte
Browsers dos espectadoresOs oito tipos públicos, de uma batalhaWebSocket, token de viewer
Seu backendSó eventos terminaisWebhook assinado, entrega durável
A própria instânciaTudoPub/sub no Redis, interno

Trate o webhook como fonte da verdade para resultados, não o WebSocket. O WebSocket é uma visão ao vivo que um espectador pode ter perdido, entrado tarde ou sido desconectado dela. O webhook retenta e é idempotente.

O que nunca aparece em nenhum evento

A URL de sessão de jogo. Nem em data, nem numa linha de log, nem em nenhum canal, nem em nenhuma resposta HTTP. Ela é criptografada em repouso e nunca cruza a fronteira do processo, e o teste ponta a ponta afirma a ausência dela.

Uma integração que precisa dela está atravessando a fronteira. Veja O que o SlotBattle não faz.

Ordem e entrega

Os eventos são publicados conforme acontecem e entregues em melhor esforço pelo WebSocket. Não há replay: um espectador que conecta no meio da batalha vê o que acontece dali em diante, não o histórico. Projete o front end para que uma conexão nova renderize a partir do snapshot atual da batalha mais os eventos novos, em vez de assumir que viu a sequência desde o começo.

A entrega terminal por webhook é o oposto: durável, retentada e idempotente por battle_id : status : version, então eventos terminais duplicados nunca geram dois POSTs.

Próximo