Files
scrabble-game/platform/telegram/internal/render/render.go
T
Ilia Denisov 1fe25a3a15
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Failing after 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 50s
CI / gate (pull_request) Failing after 0s
CI / deploy (pull_request) Has been skipped
chore: game-over phrases
2026-06-17 18:04:48 +02:00

158 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package render turns a backend push event into a localized Telegram message with
// a Mini App deep-link. Only the out-of-app push set is rendered (your_turn, game_over,
// nudge, match_found, and the invitation / friend_request notify sub-kinds); every other
// kind returns ok=false so the connector skips it (the in-app stream still carries
// it).
package render
import (
"fmt"
"scrabble/pkg/fbs/scrabblefb"
"scrabble/platform/telegram/internal/deeplink"
)
// Message is a rendered notification: the body text, the launch-button label and
// the deep-link start parameter (empty opens the lobby).
type Message struct {
Text string
ButtonText string
StartParam string
}
// Render builds the localized message for a backend push event of the given kind
// and FlatBuffers payload, in language lang ("ru" selects Russian; anything else
// is English). It returns ok=false for a kind that is not delivered out-of-app.
func Render(kind string, payload []byte, lang string) (Message, bool) {
p := english
if lang == "ru" {
p = russian
}
switch kind {
case "your_turn":
ev := scrabblefb.GetRootAsYourTurnEvent(payload, 0)
return Message{Text: yourTurnText(ev, p), ButtonText: p.openGame, StartParam: deeplink.Game(string(ev.GameId()))}, true
case "game_over":
ev := scrabblefb.GetRootAsGameOverEvent(payload, 0)
return Message{Text: gameOverText(ev, p), ButtonText: p.openGame, StartParam: deeplink.Game(string(ev.GameId()))}, true
case "nudge":
ev := scrabblefb.GetRootAsNudgeEvent(payload, 0)
return Message{Text: nudgeText(ev, p), ButtonText: p.openGame, StartParam: deeplink.Game(string(ev.GameId()))}, true
case "match_found":
ev := scrabblefb.GetRootAsMatchFoundEvent(payload, 0)
return Message{Text: p.matchFound, ButtonText: p.openGame, StartParam: deeplink.Game(string(ev.GameId()))}, true
case "notify":
ev := scrabblefb.GetRootAsNotificationEvent(payload, 0)
switch string(ev.Kind()) {
case "invitation":
return Message{Text: p.invitation, ButtonText: p.open}, true
case "friend_request":
return Message{Text: p.friendRequest, ButtonText: p.open}, true
}
}
return Message{}, false
}
// yourTurnText renders the enriched "your turn" body, voiced as the opponent who
// just moved ("{name}: my move — «WORD». Score 120:95"). It falls back to the plain phrase when
// the opponent name is missing (an older backend, or an unresolved name).
func yourTurnText(ev *scrabblefb.YourTurnEvent, p phrases) string {
name := string(ev.OpponentName())
if name == "" {
return p.yourTurn
}
switch string(ev.LastAction()) {
case "play":
if word := string(ev.LastWord()); word != "" {
return fmt.Sprintf(p.yourTurnPlay, name, word, string(ev.ScoreLine()))
}
return fmt.Sprintf(p.yourTurnMoved, name)
case "exchange":
return fmt.Sprintf(p.yourTurnExchange, name)
case "pass":
return fmt.Sprintf(p.yourTurnPass, name)
default:
return p.yourTurn
}
}
// nudgeText renders the nudge body, voiced as the sender who is waiting ("{name}: Waiting for
// your move 🤭"). It falls back to the plain phrase when the sender name is missing (an older
// backend, or an unresolved name).
func nudgeText(ev *scrabblefb.NudgeEvent, p phrases) string {
if name := string(ev.SenderName()); name != "" {
return fmt.Sprintf(p.nudgeBy, name)
}
return p.nudge
}
// gameOverText renders the "game over" body from the recipient's own perspective.
func gameOverText(ev *scrabblefb.GameOverEvent, p phrases) string {
score := string(ev.ScoreLine())
switch string(ev.Result()) {
case "won":
return fmt.Sprintf(p.gameOverWon, score)
case "lost":
return fmt.Sprintf(p.gameOverLost, score)
default:
return fmt.Sprintf(p.gameOverDraw, score)
}
}
// phrases is one language's message catalog. The yourTurn*/gameOver*/nudgeBy entries are fmt
// format strings: yourTurnPlay takes (name, word, scoreLine); yourTurnExchange/Pass/Moved and
// nudgeBy take (name); the gameOver* entries take (scoreLine).
type phrases struct {
yourTurn string
yourTurnPlay string
yourTurnExchange string
yourTurnPass string
yourTurnMoved string
gameOverWon string
gameOverLost string
gameOverDraw string
nudge string
nudgeBy string
matchFound string
invitation string
friendRequest string
openGame string
open string
}
var english = phrases{
yourTurn: "It's your turn.",
yourTurnPlay: "%s: my move — «%s». Score %s",
yourTurnExchange: "%s: swapping tiles, your turn.",
yourTurnPass: "%s: passing, your turn.",
yourTurnMoved: "%s moved, your turn.",
gameOverWon: "🏆 You won! Score %s",
gameOverLost: "You lost. Score %s",
gameOverDraw: "A draw. Score %s",
nudge: "You were nudged — it's your turn.",
nudgeBy: "%s: Waiting for your move 🤭",
matchFound: "Your game is ready.",
invitation: "You have a new game invitation.",
friendRequest: "You have a new friend request.",
openGame: "Open game",
open: "Open",
}
var russian = phrases{
yourTurn: "Ваш ход.",
yourTurnPlay: "%s: мой ход — «%s». Счёт %s",
yourTurnExchange: "%s: меняю фишки, ваш ход.",
yourTurnPass: "%s: пропускаю ход, ваш ход.",
yourTurnMoved: "%s сходил(а), ваш ход.",
gameOverWon: "🏆 Вы выиграли! Счёт %s",
gameOverLost: "Вы проиграли. Счёт %s",
gameOverDraw: "Ничья. Счёт %s",
nudge: "Вас поторопили — ваш ход.",
nudgeBy: "%s: Жду Вашего хода 🤭",
matchFound: "Игра найдена.",
invitation: "Вас пригласили в игру.",
friendRequest: "Вам пришла заявка в друзья.",
openGame: "Открыть игру",
open: "Открыть",
}