/
Mihaham
/
Table-Time
Обзор
Документация
Войти
/
Mihaham
/
Table-Time
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
services/api/app/plugins/parol/plugin.py
122 строки
5 KB
MihahamYT
feat: full game catalog — 46 plugins, docs nav, web UI kit
14 июн 2026, 12:43
14 июн 2026, 12:43
2cd5f50
Код
Авторство
О чём код?
"""Пароль — team password guessing (Table Time S5).""" from copy import deepcopy from app.plugins.base import GamePlugin, TelegramViewModel, ValidationResult, WebViewModel from app.plugins.shared.secret_word import pick_secret_word from app.plugins.shared.timer import phase_deadline, remaining_seconds TARGET = 25 class ParolPlugin(GamePlugin): plugin_id = "parol" display_name = "Пароль" min_players = 4 max_players = 8 version = "0.1" def initial_state(self, player_ids: list[str], player_names: dict[str, str], config: dict) -> dict: team_ids = list({player_ids[i % 2] for i in range(min(2, len(player_ids)))}) or player_ids[:1] if len(team_ids) < 2 and len(player_ids) >= 2: team_ids = [player_ids[0], player_ids[1]] teams = {pid: team_ids[i % len(team_ids)] for i, pid in enumerate(player_ids)} seed = config.get("seed", 42) return { "phase": "Hinting", "teams": teams, "team_ids": team_ids, "passwords": {t: pick_secret_word(seed=seed + i) for i, t in enumerate(team_ids)}, "scores": {t: 0 for t in team_ids}, "current_team": team_ids[0], "guesser_team": team_ids[1] if len(team_ids) > 1 else team_ids[0], "hints": [], "player_order": player_ids, "player_names": player_names, "deadline": phase_deadline(config.get("hint_seconds", 60)), "winner_id": None, } def validate_action(self, state: dict, action_type: str, payload: dict, player_id: str) -> ValidationResult: if state.get("winner_id"): return ValidationResult(ok=False, error_code="GAME_FINISHED") team = state["teams"].get(player_id) if action_type == "give_hint": if state.get("phase") != "Hinting": return ValidationResult(ok=False, error_code="WRONG_PHASE") if team != state.get("current_team"): return ValidationResult(ok=False, error_code="NOT_YOUR_TEAM") if not str(payload.get("hint", "")).strip(): return ValidationResult(ok=False, error_code="EMPTY_HINT") return ValidationResult(ok=True) if action_type == "guess_password": if state.get("phase") != "Guessing": return ValidationResult(ok=False, error_code="WRONG_PHASE") if team != state.get("guesser_team"): return ValidationResult(ok=False, error_code="NOT_GUESSER_TEAM") if not str(payload.get("guess", "")).strip(): return ValidationResult(ok=False, error_code="EMPTY_GUESS") return ValidationResult(ok=True) return ValidationResult(ok=False, error_code="UNKNOWN_ACTION") def apply_action(self, state: dict, action_type: str, payload: dict, player_id: str) -> dict: ns = deepcopy(state) if action_type == "give_hint": ns["hints"].append({"team": ns["current_team"], "text": payload["hint"].strip()}) ns["phase"] = "Guessing" return ns if action_type == "guess_password": guess = payload["guess"].strip().lower() pwd = ns["passwords"][ns["current_team"]].lower() if guess == pwd: ns["scores"][ns["guesser_team"]] = ns["scores"].get(ns["guesser_team"], 0) + 5 if ns["scores"][ns["guesser_team"]] >= TARGET: ns["winner_id"] = ns["guesser_team"] ns["phase"] = "GameOver" return ns ns["phase"] = "Hinting" ns["hints"] = [] ti = ns["team_ids"] ci = ti.index(ns["current_team"]) ns["current_team"] = ti[(ci + 1) % len(ti)] ns["guesser_team"] = ti[(ci + 1) % len(ti)] return ns return ns def get_current_player(self, state: dict) -> str | None: return None def is_finished(self, state: dict) -> bool: return state.get("phase") == "GameOver" or state.get("winner_id") is not None def get_winner(self, state: dict) -> str | None: return state.get("winner_id") def allowed_actions(self, state: dict, player_id: str) -> list[str]: if self.is_finished(state): return [] team = state["teams"].get(player_id) if state.get("phase") == "Hinting" and team == state.get("current_team"): return ["give_hint"] if state.get("phase") == "Guessing" and team == state.get("guesser_team"): return ["guess_password"] return [] def render_web(self, state: dict, player_names: dict[str, str], viewer_id: str | None = None) -> WebViewModel: team = state["teams"].get(viewer_id) if viewer_id else None extra = { "scores": state.get("scores"), "hints": state.get("hints"), "remaining": remaining_seconds(state.get("deadline")), "teams": state.get("teams"), } if team == state.get("current_team") and viewer_id: extra["password"] = state["passwords"].get(team) return WebViewModel( phase=state.get("phase", ""), actions_available=self.allowed_actions(state, viewer_id) if viewer_id else [], extra=extra, ) def render_telegram(self, state: dict, player_names: dict[str, str]) -> TelegramViewModel: return TelegramViewModel(caption=f"Пароль — очки: {state.get('scores')}")