# Realtime Telegram Bingo Bot (PHP + MySQL)

Multiplayer 75-ball BINGO over Telegram: players pick a card number (1–400),
a shared 10-second countdown starts, then the system calls numbers on a
central clock and auto-marks every player's card in real time.

## How the "central timer" works

Telegram webhooks are request/response only — they can't run a live clock.
So this project splits into two processes:

1. **`webhook.php`** — stateless, handles `/start`, wallet, and card
   selection. Runs behind your normal web server.
2. **`worker.php`** — a long-running CLI process that is the *only* thing
   that advances rounds, calls numbers, and checks for winners. Because
   it's one process ticking once a second, every player in a round sees
   the exact same countdown and the exact same sequence of called numbers.

Only one round is ever `status='waiting'` at a time — that's the shared
lobby every new player joins until the countdown fires.

## Setup

1. **Database**
   ```bash
   mysql -u root -p < sql/schema.sql
   ```

2. **Config** — set via environment variables or edit `config.php` directly:
   ```bash
   export BOT_TOKEN="123456:ABC-your-bot-token"
   export DB_HOST=127.0.0.1
   export DB_NAME=bingo_bot
   export DB_USER=root
   export DB_PASS=yourpassword
   ```

3. **Webhook** — point Telegram at `webhook.php` over HTTPS:
   ```bash
   curl -F "url=https://yourdomain.com/webhook.php" \
        https://api.telegram.org/bot<TOKEN>/setWebhook
   ```

4. **Worker** — keep it running permanently, e.g. with systemd:
   ```ini
   # /etc/systemd/system/bingo-worker.service
   [Unit]
   Description=Bingo game worker
   After=network.target mysql.service

   [Service]
   Environment=BOT_TOKEN=123456:ABC-your-bot-token
   Environment=DB_HOST=127.0.0.1
   Environment=DB_NAME=bingo_bot
   Environment=DB_USER=root
   Environment=DB_PASS=yourpassword
   WorkingDirectory=/path/to/telegram-bingo-bot
   ExecStart=/usr/bin/php worker.php
   Restart=always

   [Install]
   WantedBy=multi-user.target
   ```
   ```bash
   sudo systemctl enable --now bingo-worker
   ```

## Game flow

1. `/start` → user is registered, wallet created at 0 balance.
2. **Play Bingo** → paginated keyboard of card numbers 1–400 (❌ marks ones
   already taken this round). Tapping a number debits the stake and reserves
   that card.
3. The **first** card picked in a fresh round starts a 10s countdown
   (`game_settings.countdown_seconds`) shared by everyone who joins after.
4. When the countdown ends, `worker.php` flips the round to `active` and
   starts calling numbers every `call_interval_seconds` (default 4s),
   editing each player's card message in place so it auto-marks (auto-daub)
   called numbers — no manual tapping needed.
5. After every call, all cards are checked for a win:
   **any row, any column, either diagonal, or all four corners.**
6. First call that produces one or more winners ends the round immediately.

## Payouts

- `total_pool = stake_amount × number_of_cards_in_round`
- **80%** of `total_pool` → winner pool, split evenly across all winning
  cards found at the winning call (`game_settings.winner_pool_percent`)
- **10%** → system/house commission (`game_settings.system_commission_percent`)
- The remaining **10%** is left undistributed in this reference build — wire
  it to a house-reserve account, a rolling jackpot, or add it to the winner
  pool, per your business rules. Look for the comment in
  `finishRoundWithWinners()` in `worker.php`.
- If all 75 numbers are called with no winner, all stakes are refunded.

All wallet movement is logged in `wallet_transactions` for auditing.

## Adjustable settings

Everything in the `game_settings` table can be changed live (no deploy
needed) since `worker.php` and `webhook.php` read it on every request:

| Key | Meaning | Default |
|---|---|---|
| `stake_amount` | Cost per card | 10.00 |
| `countdown_seconds` | Lobby countdown before a round starts | 10 |
| `call_interval_seconds` | Seconds between number calls | 4 |
| `winner_pool_percent` | % of pool paid to winners | 80 |
| `system_commission_percent` | % of pool kept by system | 10 |

## Notes / things to adapt for production

- **Deposits**: `/deposit` is a placeholder — wire it to Telegram Payments,
  a mobile-money API, or an admin-credit panel.
- **Card visuals**: cards render as monospace text (`<code>` block) for
  reliability across all Telegram clients. If you want an actual image-based
  BINGO card, generate it with GD/Imagick in `BingoCard::render()` and send
  via `sendPhoto` instead of `editMessageText`.
- **Scaling**: the worker loops over all active rounds every second. For
  very high concurrency, consider one worker per shard of rounds, or move
  number-calling into a queue (Redis/RabbitMQ).
- **Security**: validate Telegram webhook requests (secret token via
  `setWebhook`'s `secret_token` param, checked against the
  `X-Telegram-Bot-Api-Secret-Token` header) before trusting `webhook.php` input.

## File map

```
config.php          Bot token + DB credentials
sql/schema.sql       Full database schema + default settings
src/Database.php     PDO connection singleton
src/Telegram.php     Minimal Bot API wrapper (send/edit/answer)
src/BingoCard.php    Deterministic 5x5 card generator + text renderer (auto-daub)
src/WinChecker.php   Row / column / diagonal / four-corners detection
src/Wallet.php       Credit/debit with transaction log
src/Settings.php     game_settings reader
src/GameManager.php  Round + card-selection logic (shared lobby, countdown)
webhook.php          Telegram webhook: /start, wallet, card picker
worker.php           Central real-time engine: countdown, calling, wins, payouts
```
