# Telegram Adapter Diagnostics

## Quick Health Check

```bash
# 1. Is the adapter connected?
journalctl --user -u hermes-gateway --since '10 min ago' --no-pager | grep -i "telegram" | tail -5

# 2. Is the proxy working?
curl -s -o /dev/null -w "%{http_code}" --proxy http://127.0.0.1:7890 https://api.telegram.org

# 3. Is the bot alive?
# (replace TOKEN with actual bot token from ~/.hermes/.env)
curl -s "https://api.telegram.org/bot<TOKEN>/getMe" --proxy http://127.0.0.1:7890

# 4. Is the bot in the target group?
curl -s "https://api.telegram.org/bot<TOKEN>/getChatMember?chat_id=<GROUP_ID>&user_id=<BOT_ID>" --proxy http://127.0.0.1:7890
```

## Diagnosing "Bot Not Processing Group Messages"

### Step 1: Check if adapter is alive
```bash
journalctl --user -u hermes-gateway --no-pager --since '1 hour ago' | grep "\[Telegram\]" | tail -10
```
- If only crash/reconnect-failed logs → adapter is dead, restart gateway
- If "Reconnecting telegram" entries → watcher is alive, proxy may be down
- If no Telegram logs at all → either adapter connected silently or watcher died

### Step 2: Check if proxy is the issue
```bash
curl -s -o /dev/null -w "%{http_code}" --proxy http://127.0.0.1:7890 https://api.telegram.org
# 302 = proxy working, timeout/ConnectError = proxy down
```

### Step 3: Check bot group membership
```bash
# Get bot info
python3 -c "
import json, urllib.request
with open('/home/openclaw/.hermes/.env') as f:
    token = [l.split('=',1)[1].strip() for l in f if l.startswith('TELEGRAM_BOT_TOKEN')][0]
proxy = urllib.request.ProxyHandler({'https': 'http://127.0.0.1:7890'})
opener = urllib.request.build_opener(proxy)
resp = opener.open(f'https://api.telegram.org/bot{token}/getChatMember?chat_id=<GROUP_ID>&user_id=<BOT_ID>')
print(json.loads(resp.read()))
"
```

### Step 4: Check config
In `~/.hermes/config.yaml`, under `telegram:`:
- `free_response_chats` or `free_response_channels` must include the group ID
- `channel_prompts` can be set per group for custom behavior
- `allowed_chats` when set acts as a whitelist (empty = no restriction)
- `require_mention: true` means bot only responds when @mentioned (unless in `free_response_chats`)

**Editing channel_prompts**: Values are unicode-escaped with YAML line continuations (`\` at line end). Don't use `patch()` — it breaks on multi-line unicode. Use Python regex instead:
```python
import re
with open('/home/openclaw/.hermes/config.yaml') as f:
    content = f.read()
# Match across line breaks with [\s\\]* for YAML continuations
pattern = re.compile(r'\\uXXXX[\s\\]*\\uYYYY')
content = pattern.sub(r'\\uNEW1\\uNEW2', content)
with open('/home/openclaw/.hermes/config.yaml', 'w') as f:
    f.write(content)
# Always verify YAML validity after
import yaml; yaml.safe_load(open('/home/openclaw/.hermes/config.yaml'))
```
The same prompt string is duplicated across `slack`, `discord`, `mattermost`, and `telegram` sections — replace in all 4.

### Step 5: Check forwarder
```bash
# Is the forwarder running?
docker ps | grep forwarder

# Check forwarder config
sqlite3 ~/TelegramForwarder/db/forward.db "SELECT * FROM chats;"
sqlite3 ~/TelegramForwarder/db/forward.db "SELECT * FROM forward_rules;"
sqlite3 ~/TelegramForwarder/db/forward.db "SELECT * FROM keywords;"

# Check recent forwarding activity
docker logs telegram-forwarder --tail 20 | grep "已发送"
```

## Gateway Restart Constraints

- **Cannot restart from inside the gateway**: `hermes gateway restart` is blocked when run from a gateway session (SIGTERM propagation prevention)
- **`kill <PID>` also blocked**: Security scanner intercepts `kill` commands targeting the gateway PID. Terminal returns `Blocked: cannot restart or stop the gateway from inside the gateway process.`
- **Workaround 1**: User runs `hermes gateway restart` from a separate SSH shell (recommended)
- **Workaround 2**: `systemctl --user restart hermes-gateway` from a separate shell
- **Workaround 3**: Write a small helper script to `/tmp/` and ask user to run it manually

## Reconnect Watcher Architecture

When a platform adapter hits a fatal error:
1. `_set_fatal_error()` → sets `adapter._running = False`
2. `_handle_adapter_fatal_error()` → disconnects adapter, removes from `self.adapters`
3. If `fatal_error_retryable=True` → adds to `self._failed_platforms` with backoff
4. `_platform_reconnect_watcher()` → background asyncio task, retries with exponential backoff (30s→300s cap)

**Known bug**: The watcher task can silently die. No upstream fix yet. Workaround: external gateway restart.

**Root cause (observed 2026-06-23)**: When multiple platforms crash simultaneously (Telegram + WeChat + QQBot), the watcher may die. Evidence: `journalctl` shows the fatal error at 09:27 but ZERO "Reconnecting telegram" or "Reconnect telegram error" entries afterward — the watcher's `while self._running` loop exited without logging. On a previous crash (2026-06-22) with only Telegram affected, the watcher worked correctly and kept retrying with backoff.

**Edge case — already-queued platform**: `_handle_adapter_fatal_error()` checks `adapter.platform not in self._failed_platforms` before queueing. If Telegram was already in the queue from a prior failure, the new fatal error doesn't re-queue or reset the backoff. The existing entry's `next_retry` timer may be at the 300s cap, causing long silent gaps between reconnection attempts even when the watcher IS alive.

**Definitive diagnosis**: If after a crash you see zero reconnection attempt logs for >10 minutes, the watcher is dead. Restart the gateway from an external shell.
