Service-agnostic refinement of the owner's idea: the sign-in service returns a set of supported game languages with the user identity, and the lobby gates the New Game variant choice by it (en -> English; ru -> Russian + Эрудит). - Connector hosts two bots in one container (one per service language, each its own token + game channel; the same telegram_id spans both). ValidateInitData tries each token and returns the validating bot's service_language + supported_languages. Per-language config (TELEGRAM_BOT_TOKEN_EN/_RU, channels). - supported_languages rides the Session (fbs, session-scoped, not persisted); the UI offers only the matching variants on New Game — gating only the START of a new game (auto-match + friend invite), not accept/open/play; backend does not enforce. - service_language persisted (accounts.service_language, migration 00010, written every login, last-login-wins) and routes the user-facing Notify push back through the right bot (push-target coalesces with preferred_language). - Admin SendToUser/SendToGameChannel gain an operator-chosen language selector in the console (unrelated to ValidateInitData). - Non-Telegram logins carry the gateway default set (GATEWAY_DEFAULT_SUPPORTED_LANGUAGES, all variants). Wire (committed regen): ValidateInitDataResponse +service_language +supported_languages; Session +supported_languages; SendToUser/SendToGameChannel +language. Docs (ARCHITECTURE/FUNCTIONAL/_ru/READMEs) + PLAN updated; stage marked done.
16 KiB
Scrabble Game — Функциональная спецификация
Пользовательские сценарии по доменам: что делает каждая видимая пользователю
операция. Это зеркало FUNCTIONAL.md для владельца проекта;
авторитетна английская версия. Любую точечную правку переносим в том же
патче (переводим только изменённые абзацы). Разделы наполняются по мере этапов;
(Stage N) помечает, где пишется детализация.
Домены
Клиентское приложение (Stage 7 / 8)
Веб/приложение-клиент (Svelte + Vite) воплощает эти истории. Играбельный срез (Stage 7) покрывает вход (гость или email), лобби «мои игры», старт авто-подбора, игру на доске (постановка фишек перетаскиванием или тапом, пас, обмен, сдача), top-1 подсказку, безлимитную проверку слова с жалобой, чат и nudge в партии, обновления в реальном времени, переключение языка интерфейса (en/ru) и темы и профиль только для чтения. Stage 8 добавляет управление друзьями (в т.ч. одноразовые коды-приглашения) и блоками, дружеские приглашения в игру, редактирование профиля и привязку email, экран статистики и просмотр истории партии с экспортом GCG. В настройках также выбирается стиль подписей бонус-клеток (новичок / классика / без текста). Подсказка выставляет предложенные фишки на доску — игрок сам решает сделать ход, и подсказка не тратится, если ходов нет. Проверка слова принимает только алфавит варианта, запоминает ответы в рамках сессии и ограничивает частоту повторов.
Личность и сессии (Stage 1 / 6 / 9 / 15)
Игрок приходит с платформы (сначала Telegram), через email-вход или как
эфемерный гость. Gateway один раз валидирует доступ и выдаёт тонкий
session-токен; backend сопоставляет его с внутренним user_id. Запуск Telegram
Mini App авторизует по подписанным initData платформы, перекрашивает интерфейс
в цвета Telegram и — при первом контакте — задаёт язык интерфейса нового аккаунта по
языку Telegram-клиента. Сервис входа также объявляет языки игры, которые он
предлагает (набор из en/ru, минимум один), и они ограничивают выбор типа партии в
лобби. Telegram держит отдельного бота на язык (английский и русский, один игрок
охватывает обоих); бот, через которого игрок вошёл, задаёт его доступные языки и
является тем ботом, от которого приходят его внеприложенческие уведомления. Гость — только сессия, с урезанными функциями (только
авто-подбор; без друзей, статистики и истории); заброшенный гость, не вошедший ни
в одну игру и простаивавший дольше окна удержания, удаляется сборщиком. Пока приложение открыто, клиент
держит живой стрим и получает обновления в реальном времени — ход соперника, ваш ход,
чат, nudge и найденный матч. Когда приложение закрыто, выбранные внеприложенческие
события (ваш ход, nudge, найденный матч, приглашение или заявка в друзья) приходят
вместо этого уведомлением в Telegram — если только игрок не оставил уведомления
только в приложении (настройка профиля, включена по умолчанию).
Аккаунты, привязка и слияние (Stage 1 / 11)
Первый контакт с платформы заводит постоянный аккаунт. Из профиля игрок привязывает email (по confirm-коду) или свой Telegram (через веб-вход); гость, привязавший первую личность, становится постоянным аккаунтом. Факт «личность уже занята» не раскрывается до проверки кода/входа. Если привязываемая личность уже принадлежит другому аккаунту, игроку показывают явное необратимое подтверждение, и два аккаунта сливаются в тот, под которым он сейчас работает (статистика суммируется, игры и друзья переносятся, дубликаты убираются), — кроме случая, когда гость привязывает личность с уже существующим постоянным аккаунтом: тогда сохраняется постоянный аккаунт, а игры гостя переходят в него. Слияние запрещено, только пока у аккаунтов есть общая незавершённая игра.
Лобби и подбор (Stage 4 / 15)
Нижнее tab-меню: мои игры, профиль. Типы партий на экране Новая игра ограничены языками, которые поддерживает сервис входа игрока (английский → English; русский → Russian + Эрудит; двуязычный сервис показывает все три, а веб-клиент не ограничен). Это ограничивает только старт новой игры — и авто-подбор, и приглашение друга, — поэтому игрок по-прежнему видит и играет существующие игры на любом языке. Авто-подбор (всегда 2 игрока) встаёт в пул по варианту и сводится со следующим ожидающим человеком; через 10 с без человека подставляется робот (робот — в Stage 5). Игры с друзьями (2–4) формируются приглашением игроков из списка друзей (приглашение, как и код друга, можно отправить deep-link'ом в Telegram, который откроет его сразу): инициатор выбирает настройки, и партия стартует, когда приняли все приглашённые — любой отказ отменяет приглашение, а без ответа приглашение протухает через семь дней.
Игровой процесс (Stage 3)
Выкладывание фишек, пас, обмен или сдача. Ход проверяется по словарю партии при сдаче и считается; безлимитный предпросмотр сообщает, сколько принёс бы предполагаемый ход и легален ли он. Инструмент проверки слова безлимитный и предлагает пожаловаться на любой результат. Подсказки управляются настройками партии — разрешены ли они и сколько их у каждого игрока на старте — и расходуют личный кошелёк подсказок после исчерпания внутриигрового лимита. Партия завершается, когда мешок пуст и игрок выложил стойку, после 6 подряд бесплодных ходов, по сдаче, либо по таймауту хода (от 5 минут до 24 часов, дефолт 24 часа): пропущенный ход означает авто-сдачу, кроме как когда игрок внутри своего суточного окна отсутствия (away). В партии на двоих сдача или таймаут отдают победу другому игроку, а вышедший сохраняет свои очки. В партии на троих-четверых место вышедшего убирается, остальные играют дальше, и партия завершается, когда остаётся один активный игрок; что делать с фишками вышедшего (вернуть в мешок или убрать из игры) выбирается при создании партии, а его стойка никогда не показывается остальным.
Робот-соперник (Stage 5)
Если авто-подбор не находит человека за десять секунд, свободное место занимает робот-соперник, и партия стартует без ожидания. Он задуман неотличимым от человека: один раз за партию решает, играть ли на победу (примерно в 40% случаев, так что человек выигрывает большинство партий), целится в близкий счёт, а не в разгром или поддавки, и ходит с человеческим темпом — чаще короткие раздумья, изредка долгие, и ночная пауза, подстроенная под день игрока. На nudge отвечает за несколько минут и сам шлёт nudge, когда игрок надолго пропал. Носит человекоподобное имя, не общается в чате и не принимает заявки в друзья.
Социальное: друзья, блок, чат, nudge (Stage 4 / 8)
Подружиться можно двумя способами: погасить одноразовый код, который выпускает другой игрок (шесть цифр, действует двенадцать часов), либо отправить заявку тому, с кем вы играли — он принимает, игнорирует (заявка истекает через тридцать дней, после чего её можно отправить снова) или отклоняет (отказ блокирует ваши повторные заявки, пока он сам не передаст вам код). Отмена своей висящей заявки снимает её; удаление расторгает дружбу. Глобальная блокировка — отключить входящие чат и/или заявки — и блокировка конкретного игрока (пер-юзер блок скрывает его чат и запрещает заявки и приглашения в игру в обе стороны, а также расторгает уже имеющуюся дружбу). Чат партии — для быстрых реакций: сообщения короткие (до 60 символов) и не должны содержать ссылок, email и телефонов, даже завуалированных. Nudge ожидаемого соперника — не чаще раза в час (nudge — часть игрового чата); внеприложенческий push доставляется через платформу.
Профиль и настройки (Stage 4 / 8)
Редактирование отображаемого имени (буквы, разделённые одиночными пробелом / «.» / «_», до 32 символов), таймзоны (выбор смещения от UTC), суточного окна отсутствия (away; сетка по 10 минут, не более 12 часов, с переходом через полночь) и переключателей блокировок. Привязка email и Telegram, а также слияние аккаунтов вынесены в раздел «Аккаунты, привязка и слияние» (Stage 11).
История и статистика (Stage 3 / 8)
Завершённые партии архивируются в независимом от словаря виде и экспортируются
в GCG; экспорт доступен только после завершения партии (экспорт идущей партии
раскрыл бы журнал ходов), и клиент делится файлом .gcg там, где платформа это
поддерживает, иначе скачивает его. Статистика (только у постоянных аккаунтов):
победы, поражения, ничьи, макс. очков за партию и макс. очков за один ход (лучший
ход, уже включающий все образованные им слова и бонус за все фишки).
Администрирование (Stage 10)
Оператор открывает серверную админ-консоль по адресу ${DOMAIN}/_gm — её рендерит
backend; gateway закрывает её HTTP Basic Auth на публичном порту и проксирует
один-в-один. В консоли можно смотреть пользователей (профиль, статистика,
identity, их игры) и игры (сводка + места), разбирать очередь жалоб на слова —
закрывая каждую как reject / accept-add / accept-remove — и управлять словарём:
резидентные версии по вариантам, горячая перезагрузка новой версии из
BACKEND_DICT_DIR/<version>/ и список ожидающих правок, выведенный из принятых
жалоб (он питает офлайн-пересборку и отмечается применённым после перезагрузки). Если
подключён Telegram-коннектор, оператор также может написать пользователю (по его
Telegram-identity) или отправить пост в игровой канал. Изменяющие действия
защищены проверкой same-origin; личность оператора не отслеживается.