Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save netandreus/0c8842ec5329f1fcaaec295106624bba to your computer and use it in GitHub Desktop.

Select an option

Save netandreus/0c8842ec5329f1fcaaec295106624bba to your computer and use it in GitHub Desktop.
Telegram Group Auto-Provisioning for OpenClaw — zero code changes, config only

Telegram Group Auto-Provisioning for OpenClaw

Zero code changes. Config only. Works with any OpenClaw bot.

The Problem

Adding your OpenClaw bot to a new Telegram group used to require:

  1. Create the group
  2. Add the bot
  3. Find the chat ID somehow
  4. Manually write a systemPrompt
  5. Edit openclaw.json, add the group entry
  6. Restart the gateway

That's 6 steps with context switches between Telegram and your terminal. Annoying.

The Solution

Create group → add bot → /setup → answer one question → done. The bot configures itself.

How It Works

flowchart TD
    A["Create group<br>in Telegram"] --> B["Add bot<br>as member"]
    B --> C["Tap /setup<br>from menu"]
    C --> D["Bot asks context"]
    D --> E["User answers"]
    E --> F["Bot runs config.patch<br>systemPrompt + settings"]
    F --> G["✅ Group is live"]
Loading

The wildcard entry "*" in the config catches any unknown group. Its systemPrompt is a bootstrap script that teaches the agent how to handle /setup. Once setup completes, the bot writes a permanent group-specific entry that replaces the wildcard for that chat ID.

Step-by-Step Setup (from scratch)

1. Configure OpenClaw

Add these settings to your openclaw.json (or use gateway config.patch):

{
  "channels": {
    "telegram": {
      "enabled": true,
      "groupPolicy": "allowlist",
      "groupAllowFrom": ["+1234567890"],
      "customCommands": [
        {
          "command": "setup",
          "description": "Configure this group for the bot"
        }
      ],
      "groups": {
        "*": {
          "requireMention": false,
          "systemPrompt": "<bootstrap prompt — see below>"
        }
      }
    }
  }
}

Key settings explained:

Setting Value Why
groupPolicy "allowlist" Only phone numbers in groupAllowFrom can trigger the bot. Security gate.
groupAllowFrom ["+1234567890"] Your phone number(s). The bot ignores messages from anyone else.
groups."*" {...} Wildcard — matches ANY group the bot is added to.
requireMention false Critical. The user's reply to "what's the context?" won't have an @mention. If true, the bot never sees it.
customCommands [{setup}] Registers /setup with OpenClaw's command router and with Telegram's menu (via setMyCommands at startup).

OpenClaw automatically calls Telegram's setMyCommands on startup, merging its native commands (/new, /status, /model, etc.), skill commands, and your customCommands. You never need to call setMyCommands manually.

2. The Bootstrap Prompt

This is the systemPrompt for the "*" wildcard entry. It's what teaches the agent to handle /setup:

## New Group — Auto-Setup Handler

You are in a new unconfigured Telegram group. Your ONLY job right now is to handle /setup.

### When you see /setup (or /setup@yourbotusername):

1. Ask: "What is this group about?"
2. Wait for the user's answer.
3. Once you have the context, do ONE config.patch that includes BOTH:
   - requireMention: false
   - a systemPrompt tailored to the group context
4. Extract the chat ID from your session key (the number after 'group:')
5. Apply via gateway config.patch:
   {"channels":{"telegram":{"groups":{"<chatId>":{"requireMention":false,"systemPrompt":"<generated>"}}}}}
6. Confirm to the user with the chat ID and a summary.

### Before /setup is run:
If someone writes anything other than /setup, respond:
"This group isn't configured yet. Tap /setup to get started."

### IMPORTANT:
- Do NOT patch requireMention alone first — do everything in ONE patch
  (a restart between patches would lose the session context)
- The systemPrompt you generate will REPLACE this bootstrap prompt permanently
- Generate prompts in the same style: topic header, what the group is for,
  relevant paths/skills, behavioral rules

3. Understanding the Two Security Layers

OpenClaw has two independent gates for Telegram groups:

flowchart TD
    A["Message arrives"] --> B{"Gate 1: groups section<br>Which GROUPS are allowed?"}
    B -->|"'*' = all groups"| C{"Gate 2: groupPolicy<br>Which SENDERS are allowed?"}
    B -->|"Unknown group, no '*'"| X["❌ Rejected"]
    C -->|"Phone in groupAllowFrom"| D["✅ Bot processes message"]
    C -->|"Unknown sender"| Y["❌ Rejected"]
Loading

Both must pass. The wildcard "*" opens Gate 1 to all groups, while groupPolicy: "allowlist" keeps Gate 2 locked to your authorized numbers. This is the right security tradeoff — any group is fine, but only you can talk to the bot.

4. The Chicken-and-Egg Bug (and fix)

There's a subtle issue with requireMention:

  1. User taps /setup → bot receives it (commands always go through)
  2. Bot asks "What's the context?"
  3. User types "Project X development" (no @mention)
  4. If requireMention: true on wildcard → message silently dropped 💀

The fix: set requireMention: false on the "*" wildcard. This is safe because groupPolicy: "allowlist" already prevents randos from triggering the bot.

5. Why ONE config.patch (not two)

You might think: first patch requireMention, then patch systemPrompt after getting context. Don't.

config.patch triggers a gateway restart. The restart kills the session. When the session resumes, it loads the wildcard bootstrap prompt again — but the user's context answer is gone. The bot asks again. Infinite loop.

Solution: Wait until you have everything, then do ONE patch with both requireMention: false and the final systemPrompt.

The Full Flow (what actually happens)

1. User creates "Project X Dev" group in Telegram
2. User adds @yourbotusername to the group
3. User taps the "/" menu → "setup"
4. Bot (using wildcard "*" prompt): "What's this group about?"
5. User: "Project X development — React frontend, Python backend"
6. Bot generates a systemPrompt, runs:
   gateway config.patch {
     "channels": {
       "telegram": {
         "groups": {
           "-501234567": {
             "requireMention": false,
             "systemPrompt": "## Project X Dev — Development...\n\n..."
           }
         }
       }
     }
   }
7. Gateway restarts with the new permanent entry
8. Bot confirms: "✅ Group configured (chat ID: -501234567)"
9. Next message in this group uses the new systemPrompt

Minimal Config (copy-paste ready)

For a fresh OpenClaw bot that only needs Telegram group provisioning:

{
  "channels": {
    "telegram": {
      "enabled": true,
      "botToken": "YOUR_BOT_TOKEN",
      "dmPolicy": "allowlist",
      "allowFrom": ["+1234567890"],
      "groupPolicy": "allowlist",
      "groupAllowFrom": ["+1234567890"],
      "customCommands": [
        {
          "command": "setup",
          "description": "Configure this group"
        }
      ],
      "groups": {
        "*": {
          "requireMention": false,
          "systemPrompt": "## New Group — Auto-Setup Handler\n\nYou are in a new unconfigured Telegram group. Your ONLY job is to handle /setup.\n\n### When you see /setup:\n1. Ask: \"What is this group about?\"\n2. Wait for the answer.\n3. Generate a systemPrompt tailored to the context.\n4. Extract chat ID from your session key (number after 'group:').\n5. Apply ONE config.patch: {\"channels\":{\"telegram\":{\"groups\":{\"<chatId>\":{\"requireMention\":false,\"systemPrompt\":\"<generated>\"}}}}}.\n6. Confirm to user.\n\n### Before /setup:\nRespond: \"This group isn't configured yet. Tap /setup to start.\"\n\n### Rules:\n- ONE patch only (restart between patches loses context)\n- Generated prompt replaces this bootstrap permanently"
        }
      }
    }
  },
  "plugins": {
    "entries": {
      "telegram": {
        "enabled": true
      }
    }
  }
}

Gotchas

  1. The wildcard stays forever — After setup, the "*" entry still exists. New groups still get the bootstrap flow. That's the point. Don't remove it.

  2. groupPolicy: "allowlist" checks phone numbers — The user must have a phone number linked to their Telegram account that matches groupAllowFrom. Telegram user IDs alone won't work here.

  3. No my_chat_member support (yet) — Ideally the bot would detect being added to a group and auto-ask for context. OpenClaw doesn't expose Telegram's my_chat_member events yet, so /setup is the workaround.

TL;DR — The Magic Block

Copy-paste this into your openclaw.json under channels.telegram and restart. That's it.

{
  "customCommands": [
    {
      "command": "setup",
      "description": "Configure this group"
    }
  ],
  "groupPolicy": "allowlist",
  "groupAllowFrom": ["+YOUR_PHONE_NUMBER"],
  "groups": {
    "*": {
      "requireMention": false,
      "systemPrompt": "## New Group — Auto-Setup Handler\n\nYou are in a new unconfigured Telegram group. Your ONLY job right now is to handle /setup.\n\n### When you see /setup:\n\n1. Ask: \"What is this group about?\"\n2. Wait for the user's answer.\n3. Once you have the context, do ONE gateway config.patch that includes BOTH requireMention and systemPrompt:\n   - Extract the chat ID from your session key (the number after 'group:')\n   - Generate a systemPrompt tailored to the group context (topic header, what this group is for, relevant skills/data paths, behavioral rules)\n   - Apply: gateway config.patch with {\"channels\":{\"telegram\":{\"groups\":{\"<chatId>\":{\"requireMention\":false,\"systemPrompt\":\"<your generated prompt>\"}}}}}\n4. Confirm to the user with the chat ID and a summary of the generated prompt.\n\n### Before /setup is run:\nIf someone writes anything other than /setup, respond: \"This group isn't configured yet. Tap /setup to get started.\"\n\n### Rules:\n- Do everything in ONE config.patch (a gateway restart between patches loses session context)\n- The systemPrompt you generate will permanently replace this bootstrap prompt for this group\n- Never patch requireMention separately from systemPrompt"
    }
  }
}

Replace +YOUR_PHONE_NUMBER with your actual number. Create a group, add the bot, tap /setup, answer one question. Done.

License

Do whatever you want with this. It's config, not code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment