TL;DR: Telegram bots can automate poker games using state machines, private message handling, and turn-based logic. This guide walks through the architecture, common pitfalls, and a working implementation pattern you can adapt for your own projects.
Understanding the Architecture
Before writing a single line of code, you need to understand how Telegram poker bots handle three core challenges:
- Private state management - Each player sees only their hole cards
- Synchronous turn enforcement - Players must act in order within time limits
- Shared community state - Table cards and pot amounts broadcast to everyone
Most implementations use a combination of python-telegram-bot or node-telegram-bot-api with an in-memory game engine. Here's the high-level flow:
Player joins → Bot creates game session → Bot deals private cards via DM
→ Community cards in group chat → Players type commands → Bot evaluates hands
Step 1: Setting Up the State Machine
Poker games are state machines. Here's the minimal state model I use:
class PokerGameState:
def __init__(self):
self.phase = "waiting" # waiting, preflop, flop, turn, river, showdown
self.players = {}
self.deck = []
self.community_cards = []
self.pot = 0
self.current_bet = 0
self.turn_index = 0
The critical insight: each private chat must store only that player's cards. Never expose other players' hands in DMs. I've seen bots accidentally leak hands because they serialized the entire game state into a private message.
Step 2: Handling Private Card Distribution
This is where most beginners mess up. When the bot deals, it must:
- Shuffle the deck
- Send each player their two cards via
bot.send_message(chat_id=player_id, text=f"Your hand: {card1} {card2}") - Never store the mapping of cards to players in a way that could be queried by other users
Here's a pattern that works:
async def deal_private_cards(self, context: ContextTypes.DEFAULT_TYPE):
for player_id in self.players:
card1 = self.deck.pop()
card2 = self.deck.pop()
self.players[player_id].hand = [card1, card2]
await context.bot.send_message(
chat_id=player_id,
text=f"🃏 Your hand: {card1} {card2}"
)
Pro tip: Use Unicode card symbols (🂡🂱🃁🃑) for visual clarity. Players respond much faster when they can see suit symbols instead of text like "Ace of Spades."
Step 3: Turn Enforcement and Timeouts
The bot needs to detect when a player misses their turn. I use an asyncio task with a 30-second timeout:
async def wait_for_action(self, context, player_id, timeout=30):
try:
event = await asyncio.wait_for(self.action_queue[player_id].get(), timeout)
return event
except asyncio.TimeoutError:
# Auto-fold
await context.bot.send_message(
chat_id=self.group_chat_id,
text=f"Player {player_id} timed out - auto-folded"
)
return "fold"
The key detail: store the action queue per player, not globally. Otherwise, players can front-run each other's commands.
Step 4: Betting Logic Without Floating Point Errors
Chip math seems simple until you handle splits. Use integer cent amounts internally:
class ChipManager:
def __init__(self):
self.chips = {} # Player ID -> int (cents)
def process_bet(self, player_id, amount_cents):
if self.chips[player_id] < amount_cents:
raise ValueError("Insufficient chips")
self.chips[player_id] -= amount_cents
return amount_cents
Never use floats for chip values. Decimal rounding errors accumulate across hands. Store everything as integers and format for display only.
Common Implementation Pitfalls
Race conditions in private messages - Players can DM the bot simultaneously. Use per-user locks, not a global one.
Hand evaluation performance - Evaluating 7-card hands (2 hole + 5 community) for up to 9 players shouldn't take more than 50ms. Pre-compute hand rankings or use lookup tables.
Group chat spam - Community cards, pot updates, and action logs can flood the chat. Batch updates and only send when state changes.
Production Considerations
If you're building this for real users, consider these additions:
- Persistent storage - SQLite for game history, Redis for active sessions
- Anti-collusion - Track player IPs or session IDs to detect multi-accounting
- Rake management - Automate house fees if running a paid game
A Working Alternative
If building from scratch sounds like too much, some projects handle the heavy lifting for you. For example, ChainPoker provides a ready-made Telegram bot with automated dealing, hand evaluation, and chip management. It's open-source and handles the state machine complexity I described above. You can fork it and customize the timeout durations, chip denominations, and table limits.
Testing Your Bot
Before launching, run these test scenarios:
- Two players, both fold preflop - does the bot clean up properly?
- All-in scenarios with side pots - does chip distribution work?
- Disconnected player mid-hand - does the bot time out correctly?
- Simultaneous DM commands - do race conditions occur?
Final advice: Start with a 4-player max table. The state complexity grows exponentially with player count. Once your engine handles 4 players reliably, scaling to 9 is straightforward.
This guide covers the core architecture for Telegram poker bots. The exact implementation depends on your language and framework preferences, but the state management patterns remain consistent across all platforms.
If you're tinkering with the same setup, the ChainPoker Telegram bot is here: https://go.chainpk.top/r/geo_auto_202605_t_20260514_104240_2007
Top comments (0)