---
name: hermes-telegram
description: Configure, troubleshoot, and manage Telegram integration for Hermes Agent — bot setup, .env configuration, gateway restart, and common failure modes.
version: 1.0.0
author: Hermes Agent
license: MIT
platforms: [linux, macos]
metadata:
  hermes:
    tags: [hermes, telegram, messaging, gateway, bot]
    related_skills: [hermes-agent]
related_skills: [hermes-agent]
---

# Hermes Telegram Integration

Configure the Hermes Agent gateway to send and receive messages on Telegram. Covers bot creation, .env setup, allowed users, home channel, gateway restart, and troubleshooting.

## Prerequisites

- Hermes Agent gateway installed and running (`hermes gateway status` or `ps aux | grep 'hermes gateway run'`)
- A Telegram account to create and manage the bot

## Setup Steps

### 1. Create a Bot on Telegram

Message [@BotFather](https://t.me/BotFather) on Telegram:

```
/newbot
```

Follow the prompts to choose a name and username. BotFather will return a bot token like:
```
8971430276:AAFu...Amq4
```

Save this token — you'll need it in the next step.

### 2. Find Your Numeric IDs

You need numeric Telegram IDs, not usernames. Message [@userinfobot](https://t.me/userinfobot) on Telegram — it will reply with:

- **Your user ID** (a number like `123456789`) — used for `TELEGRAM_ALLOWED_USERS`
- For groups/channels: add the bot to the group, send a message, then use @userinfobot in the group to get the **chat ID**

Note: `TELEGRAM_ALLOWED_USERS` and `TELEGRAM_HOME_CHANNEL` require **numeric IDs**, not `@username` strings.

### 3. Configure .env

Edit the `.env` file. The default template has all Telegram variables **commented out** — you must uncomment and fill them in:

```bash
hermes config env-path     # prints the path (typically ~/.hermes/.env)
```

The relevant lines (find them under the `# TELEGRAM INTEGRATION` section):

```env
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_ALLOWED_USERS=123456789           # Comma-separated numeric user IDs
TELEGRAM_HOME_CHANNEL=123456789            # Numeric chat ID for cron deliveries
TELEGRAM_HOME_CHANNEL_NAME=@yourusername   # Display name (optional, username is fine here)
```

**Important:** The default template uses a combined single-line format:
```
# TELEGRAM_BOT_TOKEN=*** TELEGRAM_ALLOWED_USERS=
```
When uncommenting, split this into **separate lines** as shown above — one variable per line. Use your editor or `sed` to do it cleanly.

For the `HOME_CHANNEL`, if you only want DM delivery (bot messages you directly), set it to your own user ID — same as `ALLOWED_USERS`.

### 4. Restart the Gateway

Configuration changes in `.env` are read at startup. Restart the gateway to pick them up:

```bash
hermes gateway restart
```

Or if running as a systemd user service:
```bash
systemctl --user restart hermes-gateway
```

#### Manual Restart (no systemd service)

If `systemctl --user list-units --type=service --all | grep hermes` returns nothing, the gateway was started manually. Kill and restart it:

```bash
# Find the gateway PID
ps aux | grep 'hermes gateway run'

# Kill it (use -9 if plain kill won't work)
kill -9 <PID>

# Wait for it to die
sleep 2

# Start fresh — MUST use background=true (nohup/disown is blocked by tool policy)
# In the Hermes CLI session:
#   terminal(background=true, notify_on_complete=true, command="hermes gateway run")
```

**Important:** Do not use `nohup`, `disown`, or trailing `&` in a foreground terminal() call — the tool policy blocks shell-level background wrappers. Always use `terminal(background=true)` so Hermes tracks the process.

### 5. Verify It's Working

Send a message to your bot on Telegram. It should respond. Check the gateway logs for confirmation:

```bash
grep -i telegram ~/.hermes/logs/gateway.log | tail -20
```

Look for these lines to confirm a successful connection:

```
Connecting to telegram...
[Telegram] Auto-discovered Telegram fallback IPs: 149.154.166.110
[Telegram] set_my_commands OK for scope BotCommandScopeDefault (30 cmds)
[Telegram] Connected to Telegram (polling mode)
✓ telegram connected
```

If you see `Connected to Telegram (polling mode)` and `✓ telegram connected`, the bot is live.

For a full reference of restart commands, log signatures, curl test-message patterns, and sed snippets, see [`references/gateway-restart-and-test.md`](references/gateway-restart-and-test.md).

#### Sending a Test Message from the CLI

The Hermes `send_message` tool only works in sessions initiated from a messaging platform (Telegram, Discord, etc.). In a CLI session, it returns `"No messaging platforms connected"` even when the gateway has Telegram active.

**Workaround — use the Telegram Bot API directly via curl:**

```bash
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
  -d "chat_id=${CHAT_ID}" \
  -d "text=Hello from Hermes! 👋"
```

The `${TELEGRAM_BOT_TOKEN}` is the value set in `.env` and `${CHAT_ID}` is the numeric user/chat ID. This sends a message bypassing the Hermes gateway — useful for testing or one-off notifications from the CLI.

## Gateway Check

To see if the gateway is running before troubleshooting:

```bash
ps aux | grep 'hermes gateway run'
```

## Key Configuration Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `TELEGRAM_BOT_TOKEN` | Yes | Bot token from @BotFather |
| `TELEGRAM_ALLOWED_USERS` | Recommended | Comma-separated numeric user IDs allowed to chat. **Leave empty to allow anyone** (not recommended for production). |
| `TELEGRAM_HOME_CHANNEL` | For cron | Default chat ID for cron job deliveries |
| `TELEGRAM_HOME_CHANNEL_NAME` | No | Display name for the home channel |
| `TELEGRAM_CRON_THREAD_ID` | No | Forum topic ID for cron deliveries in topic-mode groups |

## Long-Running Tasks & Blocking Behavior

The gateway processes **one turn at a time per session**. While the agent is inside a turn (thinking, running tools, waiting for a terminal command), it cannot receive or process new messages from you. Telegram shows "typing..." the entire time.

### Why the bot blocks

| Phase | What's happening | Can you message? |
|-------|-----------------|-----------------|
| 🔄 Agent calls a tool (e.g. `terminal()`) | Blocks waiting for result | ❌ Queued |
| 🤖 Agent calls the LLM again with tool output | Generating next action | ❌ Queued |
| 📨 Agent sends you a progress update | Brief mid-turn message | ❌ Still in same turn |
| ✅ Agent sends final response and ends turn | **Done** | ✅ Next message processed |

### "Typing..." = active LLM calls = cost

Every iteration of the agent loop makes a new API call to your LLM provider. While the bot shows "typing...", it is actively making LLM calls — you are being charged for input + output tokens each round. Input tokens grow each iteration as tool results are appended to the conversation context.

### How to avoid blocking the bot

Three approaches, in order of preference:

| Approach | How | Best for |
|----------|-----|----------|
| ⏰ **Cron job** | `cronjob(action='create', schedule='...', prompt='run my script', no_agent=True)` — script runs independently, stdout delivered verbatim to Telegram | Standalone scripts that run on a schedule or fire-and-forget |
| 🏃 **Background terminal** | `terminal(command='python long_script.py', background=true, notify_on_complete=true)` — agent returns immediately, pings you when done | One-off long scripts the user triggers |
| 📋 **/queue** | In CLI: `/queue <prompt>` — queues work for after current turn ends | Quick follow-ups during an active session |

The cron approach is cleanest: the bot never blocks, and `no_agent=True` means zero LLM calls during execution — the script's stdout is delivered verbatim.

### Verifying the bot is stuck mid-turn

Check the gateway logs for the last activity:
```bash
tail -20 ~/.hermes/logs/gateway.log
```

Look for:
- `inbound message: ... msg='your prompt'` — last message received
- `Flushing text batch ... (N chars)` — last update sent (but turn not over)
- No new `inbound message` entries — session is blocked, new messages are queued

## Pitfalls

- **Usernames are not IDs.** `TELEGRAM_ALLOWED_USERS` and `TELEGRAM_HOME_CHANNEL` need **numeric** Telegram IDs, not `@username` strings. Use @userinfobot to get them.
- **All vars start commented out.** The `.env` template ships with `#` prefix on every Telegram line. You must uncomment them.
- **Multi-variable line trap.** The default `.env` template writes `TELEGRAM_BOT_TOKEN` and `TELEGRAM_ALLOWED_USERS` on the same line: `# TELEGRAM_BOT_TOKEN=*** TELEGRAM_ALLOWED_USERS=`. This is NOT valid env format with the token value — split into two separate lines when configuring.
- **Gateway restart required.** `.env` is read at process startup. Simply running `hermes` in a new CLI session does not reload the gateway's env vars. Use `hermes gateway restart` (or `systemctl --user restart hermes-gateway`) every time you change `TELEGRAM_*` variables.
- **Config schema warnings can block clean startup.** If gateway status/logs show `custom_providers is a dict — it must be a YAML list`, fix it in `~/.hermes/config.yaml` by making `custom_providers` a list (or clear it with `hermes config set custom_providers "[]"`), then restart the gateway.
- **Token secrecy.** The bot token is a secret — anyone with it can control your bot. Keep it out of version control and shell history.
- **Gateway API server must be running.** The gateway also starts the API server (typically port 8642). If tools or web UI can't reach the agent, the root cause is often a stopped gateway, not Telegram itself.
- **send_message tool is CLI-blind to Telegram.** From a CLI session, `send_message(action=list)` returns `No messaging platforms connected` even when the gateway has Telegram active. This is by design — the tool only routes through gateway-originated sessions. Use the Telegram Bot API directly via curl to send messages from CLI sessions (see the Verification section for the exact curl command).

### Bot Commands: Registration & Limits

The gateway auto-registers bot commands from `hermes_cli.commands.telegram_bot_commands()` on startup. Commands come from three sources: built-in CommandDef entries, plugin slash commands, and skill entries. Gateway caps the command list at `MAX_COMMANDS_PER_SCOPE = 30` (telegram.py:108) to stay under Telegram's undocumented ~4KB payload limit.

**Adding a custom command persistently:**

1. Edit `~/.hermes/hermes-agent/hermes_cli/commands.py` -- add your (name, description) pair at the end of `telegram_bot_commands()`:
   ```python
   result.append(("imagine", "Generate an image from a text prompt"))
   ```
2. Add the command to `_TELEGRAM_MENU_PRIORITY` if the cap is tight -- commands outside the priority list get trimmed first.
3. If you go over 30 commands, raise `MAX_COMMANDS_PER_SCOPE` in `telegram.py`.
4. Restart the gateway to pick up changes.

Without these three changes, custom commands get overwritten on gateway restart or silently truncated at the 30-command cap.

## Troubleshooting

### Diagnostic Flow: Bot Not Responding

Start with a **status check first** — it's the fastest way to narrow the cause.

```
hermes gateway status    # Primary check
```

If that's unavailable, fall back to:
```
ps aux | grep 'hermes gateway run'   # Second — gateway process exists?
systemctl --user status hermes-gateway 2>/dev/null   # Third — systemd service?
```

#### Branch A: Gateway is not running
The most common cause of "stopped working" is the gateway having been shut down. Check the logs to find out why:

```
grep -i "telegram\|error\|fail\|warn\|sigterm\|sigkill\|oom" ~/.hermes/logs/gateway.log | tail -20
```

Common shutdown signatures in the logs:

| Log pattern | Likely cause | Action |
|-------------|-------------|--------|
| `WARNING ... Shutdown context: signal=SIGTERM under_systemd=yes parent_pid=1` | systemd sent SIGTERM (user logout, reboot, service stop) | Restart gateway or install as permanent service |
| `ERROR ... signal=SIGKILL` | OOM killer or forced kill | Check memory pressure, `dmesg | grep -i oom` |
| `ERROR ... Traceback (most recent call last)` | Crash / unhandled exception | Read the traceback, fix the root cause |
| Logs end abruptly with no shutdown message | Process was killed externally (shell session closed, SSH disconnect) | Restart gateway |
| No Telegram-related log entries at all | Telegram may not be configured or enabled in config | Check `.env` for `TELEGRAM_*` vars, check `config.yaml` → `telegram` section |

If the log shows the gateway was working fine then received SIGTERM, the fix is straightforward — restart it:

```bash
hermes gateway run          # foreground (session-dependent)
hermes gateway install      # as permanent systemd user service (auto-restarts)
```

If no systemd service exists (`systemctl --user list-units --type=service --all | grep hermes` returns empty), the gateway was started manually and won't survive a logout/reboot without the service.

#### Branch B: Gateway is running but not responding

```
grep -i telegram ~/.hermes/logs/gateway.log | tail -30
```

Look for:

- `[Telegram] Connected to Telegram (polling mode)` — connection was established
- `inbound message: platform=telegram user=...` — messages are being received
- `Sending response (... chars) to 1498679692` — responses are being sent
- `ERROR` or `WARNING` entries — something went wrong

If messages arrive but responses fail, check:
1. `TELEGRAM_BOT_TOKEN` is correct and uncommented in `.env`
2. The bot hasn't been blocked by Telegram (try sending a test from a different bot)
3. Network/firewall: Telegram polling needs outbound connectivity to `api.telegram.org`

#### Branch C: Never worked (first-time setup)

If the user says the bot never responded:
1. Follow the full Setup Steps section above
2. Check that `TELEGRAM_BOT_TOKEN` is on its own line (not merged with another var)
3. Verify with `curl` directly: `curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMe"` — should return `{"ok":true,"result":{"id":...,"is_bot":true,"first_name":"...","username":"..."}}`
4. Check `TELEGRAM_ALLOWED_USERS` — without it, the bot defaults to allowing everyone; if it's set to the wrong numeric ID, the bot ignores your messages
5. After fixing, restart the gateway
6. Wait 5-10 seconds, then send a message to the bot on Telegram

### Symptom: "Bot responds to everyone / ignores me"
Check `TELEGRAM_ALLOWED_USERS` is set to your numeric user ID. Without this, the bot is open to anyone who finds it.

### Symptom: "Cron messages don't arrive on Telegram"
1. Ensure `TELEGRAM_HOME_CHANNEL` is set to the correct numeric chat ID
2. The home channel is where cron delivers by default — if you DM'd the bot manually but set the home channel to a group, cron goes to the group, not your DMs

### Symptom: "send_message tool says 'No messaging platforms connected' from CLI"
This is expected behavior — the `send_message` tool only routes through a session that was initiated from that platform. From a CLI session, it cannot discover Telegram even if the gateway has it connected.

**Workaround:** Call the Telegram Bot API directly:
```bash
curl -s -X POST "https://api.telegram.org/bot${TOKEN}/sendMessage" \
  -d "chat_id=${CHAT_ID}" \
  -d "text=Your message"
```

### Symptom: "Can't restart — no systemd service found"
If `systemctl --user list-units` shows no hermes service, the gateway was started manually (e.g., via `hermes gateway run` in a background terminal). See the **Manual Restart (no systemd service)** section in step 4 above.
