- Trading skills (OKX, dividend, lottery, quantitative) - Creative skills (ASCII art, diagrams, video) - Development skills (GitHub, debugging, TDD) - Research skills (arXiv, blog monitoring) - Productivity skills (email, documents, notes) - MCP integration skills - Custom user skills
7.9 KiB
Hermes + TelegramForwarder Group Integration
Problem
Forwarder sends messages to a group, but Hermes bot (also in the group) ignores them.
Root Causes (check ALL of these)
| # | Cause | Symptom | Fix |
|---|---|---|---|
| 0 | Competing gateway process (old OpenClaw, duplicate Hermes) | Adapter shows "connected" but getUpdates returns 0 without 409 conflict. No "✓ telegram connected" in logs. Group messages NEVER worked. |
ps aux | grep gateway → kill stale process → restart Hermes |
| 1 | Bot Privacy Mode is ON | Bot receives 0 group messages | @BotFather → /mybots → Bot Settings → Group Privacy → Turn off |
| 2 | Wrong config key free_response_channels |
Bot sees @mentions but ignores regular messages | Use extra.free_response_chats (NOT top-level free_response_channels) |
| 3 | Group migrated to supergroup | An invalid Peer was used in forwarder logs |
Update chat ID (see below) |
| 4 | Privacy mode not propagated | can_read_all_group_messages=true but bot still can't see messages |
Kick bot from group, re-add it |
⚠️ Check #0 FIRST. A stale gateway process is invisible — the state file says "connected", no errors appear, but the adapter never actually polls. This was the actual root cause for a user whose group messages had NEVER worked for over a week (old OpenClaw Gateway PID 900 running since June 22). Always run ps aux | grep gateway | grep -v grep before investigating other causes.
⚠️ Privacy mode is the #1 cause. Even with free_response_chats correctly configured, if privacy mode is ON, the bot literally cannot see group messages. Fix privacy mode FIRST.
Step-by-step
0. Check for Competing Gateway Processes (DO THIS FIRST)
ps aux | grep "gateway\|openclaw" | grep -v grep
If you see any openclaw ... gateway or duplicate hermes gateway processes → kill them immediately. They steal the bot token polling session.
Verify nobody else is polling:
python3 -c "
import json, urllib.request
with open('/home/openclaw/.hermes/.env') as f:
for line in f:
if line.startswith('TELEGRAM_BOT_TOKEN'):
token = line.strip().split('=',1)[1]
break
proxy = urllib.request.ProxyHandler({'http': 'http://127.0.0.1:7890', 'https': 'http://127.0.0.1:7890'})
opener = urllib.request.build_opener(proxy)
try:
req = urllib.request.Request(f'https://api.telegram.org/bot{token}/getUpdates?timeout=1')
resp = opener.open(req, timeout=5)
print(f'getUpdates: {len(json.loads(resp.read()).get(\"result\",[]))} updates — nobody polling!' )
except Exception as e:
print(f'409 Conflict — adapter IS polling (normal)' if '409' in str(e) else f'Error: {e}')
"
- 0 updates without 409 → broken (check for competing processes)
- 409 Conflict → normal (adapter is polling)
1. Disable Bot Privacy Mode (MANDATORY)
Open Telegram → @BotFather:
/mybots → select Hermes bot → Bot Settings → Group Privacy → Turn off
Do this for the Hermes bot. If the forwarder bot also needs to read messages in the group, do it for that bot too.
After disabling, kick the bot from the group and re-add it. The change does not apply to bots already in a group.
Verify: curl -s --proxy http://127.0.0.1:7890 "https://api.telegram.org/bot<TOKEN>/getMe" → can_read_all_group_messages must be true.
2. Find the destination group's chat_id
cd ~/TelegramForwarder
docker exec telegram-forwarder python3 -c "
import sqlite3
conn = sqlite3.connect('/app/db/forward.db')
c = conn.cursor()
c.execute('SELECT id, telegram_chat_id, name FROM chats')
for ch in c.fetchall():
print(ch)
conn.close()
"
The column is telegram_chat_id (NOT chat_id).
⚠️ Supergroup IDs: If the group was migrated, the old ID is stale. Use -100 + channel_id format. To find the real channel_id:
entity = await bot.get_entity(old_id)
# If entity has migrated_to:
new_id = -100 * entity.migrated_to.channel_id # e.g., -1003966251111
3. Edit Hermes config (CORRECT KEY!)
~/.hermes/config.yaml — add inside the telegram: → extra: section:
telegram:
extra:
free_response_chats: '-1003966251111' # ← CORRECT key
# free_response_channels: '...' ← WRONG — code ignores this!
⚠️ CRITICAL: The Hermes source code reads self.config.extra.get("free_response_chats"). The top-level free_response_channels is a DIFFERENT field that is NOT read by the group-message processing logic. Using the wrong key = silently ignored, bot only responds to @mentions.
Multiple groups: comma-separated '-1003966251111,-1001234567890'
4. Restart Hermes
hermes gateway restart # from a separate shell, NOT inside the gateway
If refused ("Refusing to restart from inside the gateway"), ask the user to SSH in and run it.
Config Field Reference
| Field | Location | Default | Purpose |
|---|---|---|---|
require_mention |
telegram: |
true |
If true, bot only responds when @mentioned in groups |
free_response_chats |
telegram:extra: |
'' |
Comma-separated chat_ids exempt from @mention requirement |
free_response_channels |
telegram: |
'' |
NOT used by group message processing — legacy field, ignore |
allowed_chats |
telegram: |
'' |
If set, ONLY these chats can interact with the bot (whitelist) |
Signal Forwarding with channel_prompts
When you want Hermes to process group messages (e.g., trading signals) and forward results to a user DM instead of replying in the group:
telegram:
channel_prompts:
"-1003966251111": "你是交易信号处理器。收到群里的交易信号后,解析【币种】【方向】【仓位】,然后用send_message工具发送到telegram:656685714(用户DM),格式简洁:信号摘要+当前价格+建议操作+风险提示。不要在群里回复。"
This configures a per-group system prompt. The agent in that group session will follow these instructions instead of the default behavior.
End-to-End Verification
After setting up the forwarding → Hermes chain, verify each link:
- Forwarder listening:
docker logs telegram-forwarder 2>&1 | grep "收到新消息"— should show messages from source channel - Forwarder sending:
docker logs telegram-forwarder 2>&1 | grep "已发送"— should show successful sends to target group - Hermes receiving:
hermes logs 2>&1 | grep "inbound message"— should show messages from the group (chat ID should match the group, not the user DM) - Hermes responding: Check the group for Hermes's reply
Common failure chain:
- Forwarder logs show "收到新消息" but no "已发送" → keyword filter issue or invalid Peer (check group ID)
- Forwarder logs show "已发送" but Hermes shows no inbound from group → privacy mode still on, or
free_response_chatswrong key - Hermes shows inbound but no response → check
require_mentionandfree_response_chats
⚠️ For testing, use a human account (not the forwarder bot) to send messages to the source channel or directly to the group. Bot-sent messages may be filtered by Hermes.
Pitfalls
free_response_chatstakes string values, not integers:'-1003966251111'not-1003966251111- Config changes require Hermes restart to take effect
- If
allowed_chatsis set (non-empty), the group must also be in that list - The forwarder's
handle_mode: FORWARDuses Telegram's native forward (shows "Forwarded from"). If user wants clean copies, change toCOPYmode. - Don't test by sending directly to the target group — this bypasses the forwarding chain and proves nothing. Test by sending to the SOURCE group and verifying the full pipeline: source → forwarder → target → Hermes response.
- Bot-to-bot messaging FAILS: Telegram prohibits bot→bot messaging. Forward to a GROUP where both bots reside, not directly to a bot.
- Session database locked: If you see
sqlite3.OperationalError: database is locked, stop the container first, edit DB, then restart.