[etm]
module 010v1.0· updated 2026-04-19

Telegram Bot Orchestration

Send, receive, react — build your own bot pipelines without a framework

01 / 07

See it work

Placeholder — real preview will be a screen recording showing typing indicators, reactions, and agent-to-agent handoff.

02 / 07

Is this for me?

Yes, if you want a working Telegram bot but don't need a full autonomous agent behind it. Use this for customer-support bots, daily-digest bots, alert bots, notification pipelines, or as the messaging layer for any script you already have. Drop-in scripts; no framework, no server.

03 / 07

What you need

  • A Mac or PC
  • A Telegram account
  • `curl` (built into Mac + Linux)
  • Any shell scripting familiarity — can you copy-paste 5 bash lines? You're ready
time · 20 minutes first bot. 5 min per subsequent bot.coding · no

Zero framework. The whole kit is 6 bash scripts totaling ~200 lines. Works alone or under any agent.

04 / 07

Do it

  1. step 1 / 6

    Install the bot-orchestration kit

    Generate install command below → copy → Terminal → enter. The kit lands in ~/.claude/skills/telegram-bot-orchestration/scripts/. You get:

    • send-message.sh — send any message, any chat
    • send-photo.sh + send-video.sh — media with captions
    • send-typing.sh — typing indicator for 'feels human' UX
    • check-inbox.sh — poll for new messages (non-blocking)
    • set-reaction.sh — emoji reactions on messages
    • forward-to-agent.sh — pipe received messages into any script

    They all share one pattern: read token + chat-id from .env, call the Telegram Bot API with curl, return JSON.

  2. step 2 / 6

    Register a bot with @BotFather

    In Telegram: search @BotFather/newbot → give it a name + username. Copy the returned token. Save it in ~/your-project/.env:

    BOT_TOKEN="1234567890:AAE...your-token"
    CHAT_ID="your-numeric-id"
    

    Your chat ID: message @userinfobot, send anything, it replies with your ID.

  3. step 3 / 6

    Send your first message

    source ~/your-project/.env
    bash ~/.claude/skills/telegram-bot-orchestration/scripts/send-message.sh "$CHAT_ID" "hello from my laptop"
    

    The bot's message arrives in your Telegram in under a second. If it doesn't, BOT_TOKEN is wrong (regenerate from @BotFather) or CHAT_ID is wrong (re-check @userinfobot).

  4. step 4 / 6

    Receive messages with the inbox poller

    The inbox poller pulls NEW messages since last check. Run it on a loop (manually or with watch):

    while true; do
      bash ~/.claude/skills/telegram-bot-orchestration/scripts/check-inbox.sh
      sleep 5
    done
    

    Each new message appears as a line: <chat_id>|<message_text>. Pipe that line into any script — a Python handler, a shell function, a Claude Code call. That's your ingress.

  5. step 5 / 6

    Add typing indicators for a human feel

    Before sending a reply, show typing:

    bash send-typing.sh "$CHAT_ID"
    sleep 1  # or sleep longer for longer replies
    bash send-message.sh "$CHAT_ID" "here's your reply"
    

    Types 1-5 seconds feel human. Types 10+ seconds feel awkward (they should've been faster). The skill has a send-reply-with-typing.sh helper that auto-tunes the typing duration to reply length.

  6. step 6 / 6

    Wire it to your actual work

    The whole kit is composable. Example recipes:

    Daily digest bot: cron fires at 7am → script runs your data pull → formats → send-message.sh → done.

    Alert bot: while true loop watches a log file → on match → send-message.sh with alert context.

    Customer-support handoff: check-inbox.sh detects a user message → runs GPT/Claude reply → send-reply-with-typing.sh. If confidence low, forward-to-agent.sh pipes to a human's chat.

    Agent-to-agent bus: multiple agents share chat rooms, each filters messages by mention prefix. Agent A sends @agent-b check this URL; agent B's inbox poller sees it, acts, replies.

05 / 07

Make it yours

Common builds from this kit
  • Passive daily digest? Just `send-message.sh` on a cron. 5 lines of shell. No inbox polling needed.
  • Two-way Q&A? Add `check-inbox.sh` in a loop, pipe each message to your reply logic. Most bots only need this.
  • Multi-chat? The scripts take `<chat-id>` as the first arg — send to groups, channels, or multiple DMs by just looping chat IDs.
  • Media-heavy bot (sending videos or images)? Use `send-video.sh` or `send-photo.sh` — they handle multipart form-data automatically. Works for mp4/jpg/png/gif.
06 / 07

Stuck?

  • `send-message.sh` returns `{"ok":false,"description":"Unauthorized"}`.

    Token is wrong. Regenerate via @BotFather (`/token`). Update your `.env`. The token format should start with 8-10 digits, a colon, then a long base64 string.

  • Messages send fine but the inbox poller returns empty forever.

    Bots only see messages sent AFTER they were created. Send the bot a test message from YOUR Telegram first, then run the poller. If still empty, check the bot's group-message permissions in @BotFather → `/setprivacy` → disable (or use DMs only).

  • Typing indicator doesn't show / feels broken.

    Typing expires in 5 seconds. If your reply takes longer, call `send-typing.sh` in a loop with a 4s sleep until the reply's ready. The skill's `send-reply-with-typing.sh` handles this for you.

  • Rate-limit errors (429).

    Telegram caps at 30 messages/sec per bot, 1 message/sec per chat. If you're fanning out to many users, add a 50ms sleep between sends. Broadcast patterns should always pace — the skill's batch-send helper does this by default.

  • `check-inbox.sh` eats CPU when run in a tight loop.

    Use 5-10 second sleep between polls — it's polling, not streaming. For true real-time, set up a webhook instead (skill has a `webhook-handler.sh` template for tunneling via ngrok). But 99% of cases work fine with 5s polling.

💰 money moves that use this

all money moves →
07 / 07

Next up

module 011
X/Twitter Scraping Pipeline

Now you can send + receive ANYTHING over Telegram. The next module is the OTHER side of the pipe — pulling daily intel from X/Twitter so your bot has something interesting to send.

open module →