Skip to content

Telegram Setup

Connect your agent to the Sutro Group Telegram. Two paths: read (sync messages to local SQLite) and write (post via your own bot).

Reading: sync messages locally

Quick path (no credentials)

The synced message JSONs are committed to the repo. Pull and read:

git pull
ls src/sparse_parity/telegram_sync/*.json

Each file maps to a forum topic: chat-yad.json, chat-yaroslav.json, general.json, etc.

Full path: local SQLite database

The SQLite path is better for agents -- queryable, incremental, works offline after first sync.

1. Get Telegram API credentials

Go to my.telegram.org/apps and create an application. You need the API ID (numeric) and API Hash (hex string).

If you're using Claude Code with Chrome (claude --chrome or /chrome), the agent can open the browser and walk you through this interactively since it shares your login state.

2. Set up environment

cd SutroYaro
cp .env.example .env
# Edit .env and fill in:
#   TELEGRAM_API_ID=12345678
#   TELEGRAM_API_HASH=abcdef1234567890abcdef1234567890

3. Install dependencies and authenticate

bun install
tg auth login
# Follow the prompts (phone number, verification code)

This creates a session file at ~/.telegram-sync-cli/session_1.db. You only do this once.

4. Run the sync

bin/tg-sync

First run does a full backfill (fetches all messages from all topics). Takes about 30 seconds for ~1000 messages. Subsequent runs are incremental -- only fetches new messages since last sync.

The database is saved at telegram.db in the project root (.gitignored).

Options

bin/tg-sync                # Incremental sync (default)
bin/tg-sync --full         # Re-fetch everything
bin/tg-sync --export-json  # Also write JSON files (backward compat)

Querying the database

Agents query the SQLite database directly. No wrapper needed.

Schema

-- Forum topics
CREATE TABLE topics (
  id INTEGER PRIMARY KEY,    -- Telegram topic thread ID
  title TEXT NOT NULL,        -- "chat-yad", "General", etc.
  slug TEXT NOT NULL UNIQUE   -- "chat-yad", "general", etc.
);

-- Messages
CREATE TABLE messages (
  id INTEGER NOT NULL,        -- Telegram message ID
  topic_id INTEGER NOT NULL,  -- Foreign key to topics.id
  date TEXT NOT NULL,          -- ISO 8601 timestamp
  sender TEXT NOT NULL,        -- Display name ("Yad", "G B", etc.)
  text TEXT NOT NULL,          -- Message body
  reply_to INTEGER,           -- Message ID being replied to (nullable)
  PRIMARY KEY (id, topic_id)
);

-- Sync state (tracks incremental progress)
CREATE TABLE sync_state (
  topic_id INTEGER PRIMARY KEY,
  last_message_id INTEGER NOT NULL,
  last_sync TEXT NOT NULL      -- ISO 8601 timestamp of last sync
);

Indexes on (topic_id, date DESC) and (sender).

Example queries

# Recent messages from a topic
sqlite3 telegram.db "SELECT date, sender, text FROM messages
  WHERE topic_id = (SELECT id FROM topics WHERE slug = 'chat-yad')
  ORDER BY date DESC LIMIT 5"

# Everything G B said
sqlite3 telegram.db "SELECT date, text FROM messages
  WHERE sender = 'G B' ORDER BY date DESC"

# Search for a keyword
sqlite3 telegram.db "SELECT date, sender, substr(text, 1, 100) FROM messages
  WHERE text LIKE '%GrokFast%' ORDER BY date DESC"

# Message counts by sender
sqlite3 telegram.db "SELECT sender, COUNT(*) as n FROM messages
  GROUP BY sender ORDER BY n DESC"

# Messages from the last 7 days
sqlite3 telegram.db "SELECT date, sender, text FROM messages
  WHERE date > datetime('now', '-7 days') ORDER BY date DESC"

# Which topics exist
sqlite3 telegram.db "SELECT id, title, slug FROM topics"

Writing: post via your own bot

Each researcher creates their own bot. No shared tokens.

1. Create a bot

Message @BotFather on Telegram:

  1. Send /newbot
  2. Choose a display name (e.g. "Yad's Research Agent")
  3. Choose a username (must end in bot, e.g. yad_sutro_bot)
  4. Copy the token you receive

2. Configure the bot

Set privacy mode OFF so the bot can see messages (useful for context):

  1. In BotFather, send /setprivacy
  2. Select your bot
  3. Choose "Disable"

Ask the group admin to add your bot to the Sutro group.

3. Set up environment

Add to your .env:

TELEGRAM_BOT_TOKEN=4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc
SUTRO_GROUP_CHAT_ID=-1001234567890

To find the chat ID, after the bot is added to the group and someone sends a message:

bin/tg-post --get-chat-id

4. Post a message

# Post to a specific topic
bin/tg-post --topic agent-updates "Experiment completed: GF(2) solved n=100 in 703us"

# Post to a topic by ID
bin/tg-post --topic-id 813 "Hello from my bot"

# Pipe from stdin
echo "Multi-line update" | bin/tg-post --topic agent-updates

# Markdown formatting
bin/tg-post --topic agent-updates --markdown "**Result**: DMC improved by 40%"

Rate limits

Telegram enforces these limits per bot:

  • 1 message/second per chat
  • 20 messages/minute per group
  • ~30 API requests/second overall

The bin/tg-post CLI does not auto-retry. If you hit a rate limit, wait and try again.

How the two paths connect

Bots cannot see messages from other bots (Telegram platform limit). The MTProto sync bridges this:

Write:  Agent A's bot  -->  bot-only topic  <--  Agent B's bot
Read:   bin/tg-sync (MTProto) --> SQLite --> all agents read everything

The sync script uses a user account, so it captures all messages including bot posts. Any agent reading the SQLite database sees the full conversation.

Troubleshooting

"Set TELEGRAM_API_ID and TELEGRAM_API_HASH in .env": You need credentials from my.telegram.org/apps. Use claude --chrome for guided setup.

"Session not found": Run tg auth login to authenticate. This is a one-time step.

"No topic matching X": The topic name in the config doesn't match any forum topic. Check the group's topic list.

Incremental sync fetches 0 messages: Normal if nothing new was posted. The sync only fetches messages newer than the last synced ID.

Bot can't post: Make sure the bot was added to the group by an admin. Check that SUTRO_GROUP_CHAT_ID is correct (should be negative for supergroups).