Skip to content
SlachPublic

About

LLM based games in telegram

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

AI Game Master

AI-powered cooperative game delivered through Telegram bot. Each turn, an AI generates a unique story, personalized action images, and NPC interactions based on player choices.

The game is finite and can be lost. A doom clock (threat level 0-100) ticks every turn (+8 base, accelerated by hesitations, a critically damaged hull and mission stagnation) — at 100 the game ends. Hull damage accumulates turn over turn (hull 0 = ship destroyed), wounds escalate minor → moderate → critical (critical + any new wound kills), NPC loyalty drops with losses and can end in mutiny, and the crew is never replenished. The final verdict is computed by code from the end-state into five outcomes: triumph / victory / pyrrhic / stalemate / defeat.

Architecture

graph TD
    A[Telegram API] --> B[telegram-bot]
    B --> C[game-server]
    D[game-scheduler] --> C
    C --> E[comfyui]
    C --> G[llama.cpp<br/>external]

    style B fill:#E1F5FE
    style C fill:#E8F5E9
    style E fill:#FFF3E0
    style G fill:#E0E0E0
    style D fill:#F3E5F5
Loading

Services

Service Port Description
game-server 8000 FastAPI backend with SQLite persistence
telegram-bot N/A Telegram bot interface
comfyui 8188 Image/Video generation backend
game-scheduler N/A Turn generation scheduler (run manually for debugging)

Game Master API (game-server/)

FastAPI service that orchestrates the game:

  • Turn story generation using OpenAI API
  • NPC dialogue generation
  • Personalized action-image generation via ComfyUI
  • Player action processing
  • Text/voice message handling
  • SQLite persistence for game state

Ports: 8000

Key Endpoints:

  • POST /onboarding/start - Start onboarding for a player
  • POST /onboarding/{session_id}/reroll - Reject the proposed character and roll another
  • POST /onboarding/{session_id}/complete - Accept the proposed character
  • GET /players/{player_id}/profile - Get player profile
  • GET /game/current-turn - Get current turn's episode
  • GET /game/turn-deadline/{game_id} - Get the playable turn's deadline
  • POST /game/remind-turn/{game_id}/{turn} - Send a T-2h/T-30m reminder
  • POST /game/actions - Submit player action
  • POST /game/messages - Send message to game master
  • POST /admin/continue-game - Generate the next turn
  • GET /admin/win-rate - Outcome telemetry over ended games
  • POST /admin/generate-comic/{player_id} - Generate personalized comic

Telegram Bot (telegram-bot/)

Player interface via Telegram:

  • /start - Begin onboarding or return to game
  • /profile - Show player role and traits
  • /turn - View current turn episode
  • /help - Show help information
  • Interactive keyboards for action selection
  • Voice message support
  • Text chat with Game Master

Ports: None (outbound Telegram API only)

Game Master Scheduler (game-scheduler/)

Scheduled task runner that triggers daily episode generation. Can be run manually for debugging.

Usage:

# Run single generation cycle for testing
GAME_SCHEDULER_MODE=single docker compose run --rm game-scheduler

Ports: None

ComfyUI (comfyui/)

Image/video/3D generation backend. Requires GPU.

Ports: 8188

Setup

Prerequisites

  • Docker and Docker Compose
  • NVIDIA GPU (for ComfyUI)
  • NVIDIA Container Toolkit
  • Telegram Bot Token (get from @BotFather)
  • External spark-network Docker network
  • External llama.cpp service running on port 8090

Configuration

  1. Copy .env.example to .env:
cp .env.example .env
  1. Edit .env and set your Telegram bot token:
TELEGRAM_BOT_TOKEN=your_bot_token_here

Running the Services

  1. Build and start services:
docker compose up -d
  1. Check logs:
docker compose logs -f game-server
docker compose logs -f telegram-bot
  1. Run single generation cycle (for testing):
GAME_SCHEDULER_MODE=single docker compose run --rm game-scheduler

Onboarding Flow

  1. Player sends /start to the bot
  2. Bot starts an onboarding session (POST /onboarding/start); the first player of a game also triggers the game concept (title + welcome + mission)
  3. In the background the server rolls a random character — a free ship role, species (20% chance of a hybrid) and gender — generates all flavour text in one LLM call and renders the avatar via ComfyUI
  4. The bot shows a proposal card (avatar + role, species, gender, flavour) with Accept / Reroll buttons; already-rejected roles and species are not repeated while alternatives exist
  5. After 3 rerolls the next character is assigned automatically
  6. On accept (POST /onboarding/{session_id}/complete) the profile is created; once enough players have joined (GAME_START_MIN_PLAYERS), the game starts automatically

Turn Game Loop

A turn is driven by game-scheduler (the timer), produced by game-server (LLM + image orchestration), and delivered to players by telegram-bot (its push server on port 9090). There is no fixed daily clock — turns are generated on a configurable interval (GAME_SCHEDULE, default 8h), and the previous turn's outcome is published the moment all live players have chosen their actions. Each turn has a deadline shown to players («Ход закроется: ...» UTC); the scheduler sends T-2h/T-30m reminders and auto-picks actions for absentees when the deadline passes.

sequenceDiagram
    participant S as game-scheduler
    participant GS as game-server
    participant TB as telegram-bot
    participant P as Players

    Note over S: interval elapses (GAME_SCHEDULE)
    S->>GS: GET /game/started   (need >= 3 players)
    S->>GS: GET /game/state     (active? current turn N)
    Note over S: if N>1: auto-pick actions for<br/>absentee players on turn N-1<br/>(LLM pick, or a recorded "delay")
    S->>GS: POST /admin/continue-game?game_id=...&language=en&deadline=...
    GS-->>S: 202 accepted (generation runs in background)

    Note over S: T-2h / T-30m before the deadline:<br/>GET /game/turn-deadline, POST /game/remind-turn

    Note over GS: background: LLM global circumstances -><br/>ComfyUI scene image -><br/>per-player briefings + 5 choices -><br/>NPC choices auto-selected -><br/>character/NPC images -> crew dialogues

    opt turn N > 1
        Note over GS: resolve & push turn N-1 outcome
        GS->>GS: _analyze_turn_outcome(N-1)
        GS->>TB: POST /push/outcome   (results of last turn)
        opt mission done / ship destroyed / crew wiped /<br/>threat 100 / NPC mutiny
            GS->>TB: POST /push/game-over  (finale)
            GS->>TB: POST /push/game-summary  (post-game stats)
        end
    end

    GS->>TB: POST /push/briefings  (new turn intro + choices + deadline)
    TB->>P: deliver outcome (N-1), then briefing (N)

    P->>TB: select an action
    TB->>GS: POST /game/actions
    Note over GS: record choice -> generate chosen-action image
    opt all live players have chosen
        GS->>GS: _analyze_turn_outcome(N) immediately
        GS->>TB: POST /push/outcome
    end
Loading

Per-turn sequence (on game-server)

  1. Trigger — game-scheduler fires POST /admin/continue-game. The endpoint returns 202 immediately; all generation runs in the background.
  2. Global circumstances — LLM produces the turn's setting, narrative, conflict and a scene prompt.
  3. Scene & turn background images — ComfyUI renders the turn's briefing scene from that prompt, plus a character-free turn background (empty environment built from the setting + conflict) that is reused as the shared canvas for every character/action image of the turn.
  4. Briefings & choices — every live player and NPC gets a personalized briefing with 5 action choices with hidden consequence kinds in a fixed quota — 2 progress / 2 injury / 1 fatal (configurable via GAME_TURN_PROGRESS_ACTIONS / GAME_TURN_INJURY_ACTIONS / GAME_TURN_FATAL_ACTIONS). NPC choices are auto-selected by the LLM; players' choices stay open until the turn deadline.
  5. Character & NPC images — ComfyUI renders each player's personal briefing image as an identity-preserving edit: the player's avatar as reference image 1 placed into the turn background as reference image 2.
  6. Crew dialogues — LLM generates NPC banter for the turn.
  7. Game state advances — the turn record is created (with its deadline), state moves to turn N+1.
  8. Previous turn outcome (turn N > 1) — _analyze_turn_outcome(N-1) combines every player + NPC decision into one outcome narrative, applies the LLM's ship deltas (hull/shields/systems) to the persistent ship state, resolves wounds and deaths by entity_id through the rules ladder, ticks the doom clock and NPC loyalty, generates an outcome scene image, then pushes /push/outcome. The outcome risk is reweighted by code (damaged hull / high threat / reckless choices make catastrophes likelier). If the game ends (mission complete, hull 0, crew wiped, threat 100, or NPC mutiny), the code-computed verdict (triumph / victory / pyrrhic / stalemate / defeat) drives a finale pushed via /push/game-over, followed by a post-game stats summary via /push/game-summary.
  9. New turn delivery — /push/briefings sends the new turn's intro, choices and deadline to each player. (Outcomes of N-1 are always delivered before the briefing for N.)

Action selection & outcome resolution

  • Players pick an action in the bot → POST /game/actions. The server records the choice and kicks off a chosen-action image: an edit-model render placing the player's avatar (reference 1) into the turn's shared background (reference 2), staged per the action text.
  • The moment all live players have chosen, the outcome is analyzed immediately — it does not wait for the next scheduled tick. (The per-turn outcome analysis is idempotent, so a turn already resolved on time is a no-op at the start of the next turn.)
  • Absentee players are handled by game-scheduler: at the start of turn N it auto-selects an action for anyone who did not choose on turn N-1 (POST /game/auto-action/{player}/{turn}, LLM-picked) before triggering the next turn. If the LLM pick fails, a delay (hesitation) is recorded honestly: it does not advance the mission and accelerates the doom clock.

Manual control (Game Master)

  • /gm_start <game_id> — generate the first turn (POST /admin/start-game).
  • /gm_continue <game_id> — generate the next turn immediately (POST /admin/continue-game), bypassing the scheduler.
  • /gm_turn <game_id> — regenerate the current turn (/admin/regenerate-turn).
  • /gm_restart <game_id>, /gm_pause, /gm_schedule, /gm_status, /gm_kick, /gm_list, /gm_lang — additional game management commands.

NPC System

At game start, free seats are filled with a bounded pool of exactly up to 4 NPCs (NPC_COUNT), preferring the key roles chief_engineer, medical_officer, pilot and science_officer. A bounded pool keeps the crew-wipe ending reachable — and since a kicked or dead player's seat is never refilled, every loss is permanent.

  • Chief Engineer - Technical systems maintenance
  • Medical Officer - Crew health and wound treatment
  • Pilot - Navigation and flight operations
  • Science Officer - Research and analysis

NPCs have distinct personalities and speech styles. Each NPC also has a loyalty score (0-100, starting at 70) maintained by code: deaths, heavy hull damage and mission regressions lower it; progress and healing raise it. Loyalty is visible to players in /game/team (loyalty band) and shapes NPC decisions. When two or more active NPCs drop to loyalty <= 15, the crew mutinies — one of the five ways the game can end.

Content Generation

Image pipeline

All images are rendered by ComfyUI. Workflows are built programmatically in game-server/image_generator.py (one builder per model family); per-kind model routing lives in game-server/comfyui_config.py and is configured via COMFYUI_TXT2IMG_MODEL / COMFYUI_AVATAR_MODEL / COMFYUI_EDIT_MODEL in .env. Image prompts are LLM-authored from templates in game-server/prompts.py.

  • txt2img — turn_background (the shared stage of the current LOCATION — generated with standable surfaces and character-scale anchor objects, cached per (game, location_id) so the world stays consistent while the crew stays put; it is also the briefing header image), splash, loading, finale.
  • avatar — RGBA character cutout on a transparent background (avatar, npc_avatar_*), currently MOSS-Image-SFT. Prompts carry a crew presentation contract (role uniform, ship-grade lighting, no civilian props).
  • identity-preserving edit — character (personal briefing image), player_action / npc_action (chosen action), player_death: the avatar (reference image 1) is placed into the location background (reference image 2) following a short LLM-written staging instruction. For humanoid characters the LLM also picks a pose from pose_library.py (OpenPose skeletons rendered in pure Python) and the skeleton goes to the edit model as reference image 3 — "the character adopts the pose of Picture 3".
  • multi-reference — bridge (crew on the bridge) and outcome (turn outcome scene) render all crew avatars in one scene via Qwen-Image-2.1 multi-reference.

Global circumstances carry a stable location object (id, name, description); the LLM is told to reuse the same location.id while the crew stays in the same place, and the backdrop is cached per location. Consistency comes from the shared location background, the avatar reference and the pose skeleton; there is no fixed seed.

Development

Running Locally

# Game Server API
cd game-server
pip install -r requirements.txt
uvicorn main:app --reload --host 0.0.0.0 --port 8000

# Telegram Bot
cd telegram-bot
pip install -r requirements.txt
export TELEGRAM_BOT_TOKEN=your_token
python bot.py

# Game Scheduler (for debugging)
cd game-scheduler
pip install -r requirements.txt
GAME_SCHEDULER_MODE=single python main.py

After running this, you must generate a new turn via Telegram:

Running Tests

Activate the project virtual environment and run:

cd game-server && ../.venv/bin/python -m unittest discover -s tests

Or specific modules:

cd game-server && ../.venv/bin/python -m unittest tests.test_game_rules tests.test_mission_db

API Documentation

Visit http://localhost:8000/docs for Swagger UI.

Troubleshooting

API Connection Failed

Check game-server health:

curl http://localhost:8000/health
docker compose logs game-server

GPU Not Available

docker compose logs comfyui
# Check if NVIDIA runtime is configured
nvidia-smi

Telegram Bot Not Responding

# Check if bot token is set
docker compose exec telegram-bot env | grep TELEGRAM
# Verify API connectivity
docker compose exec telegram-bot ping game-server

Configuration Details

Variable Default Description
LLM_URL http://llama.cpp:8090/v1 LLM endpoint
LLM_API_KEY placeholder-key-for-llama-cpp Required by OpenAI client
COMFYUI_URL http://comfyui:8188 Image gen endpoint
TELEGRAM_BOT_TOKEN (required) Telegram bot token
GAME_SCHEDULE 8h Turn schedule: Nh/Nm/Ns, HH:MM, HH:MM,..., DAY-HH:MM
GAME_SCHEDULER_MODE scheduled single/simulation/scheduled

Future Enhancements

  • Voice message transcription
  • 3D scene generation
  • Video clips for key moments
  • Multi-player cooperation
  • Cross-group events
  • Telegram Mini App integration

About

LLM based games in telegram

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages