// 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: p.nudge, 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 (Stage 17), 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 } } // gameOverText renders the "game over" body (Stage 17) 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* entries are fmt format // strings: yourTurnPlay takes (name, word, scoreLine); yourTurnExchange/Pass/Moved 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 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: "Game over — you won! Score %s", gameOverLost: "Game over — you lost. Score %s", gameOverDraw: "Game over — a draw. Score %s", nudge: "You were nudged — it's your turn.", 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: "Вас поторопили — ваш ход.", matchFound: "Игра найдена.", invitation: "Вас пригласили в игру.", friendRequest: "Вам пришла заявка в друзья.", openGame: "Открыть игру", open: "Открыть", }