> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bumbleagi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Telegram

> Connect your entity to a Telegram bot.

## Setup

<Steps>
  <Step title="Create a bot">
    Talk to [@BotFather](https://t.me/BotFather) on Telegram and create a new bot. Copy the token.
  </Step>

  <Step title="Set the token">
    Copy `.env.example` to `.env` at the repo root and set:

    ```env theme={null}
    TELEGRAM_TOKEN=your_token_here
    ```

    The `.env` file is gitignored and loaded automatically.
  </Step>

  <Step title="Add the platform">
    In your entity YAML (`configs/entities/<name>.yaml`):

    ```yaml theme={null}
    presence:
      platforms:
        - type: "telegram"
          token_env: "TELEGRAM_TOKEN"
    ```
  </Step>

  <Step title="Run">
    ```bash theme={null}
    bumblebee run canary --ollama
    ```

    Use `run` (not `talk`) — the daemon owns the bot connection.
  </Step>
</Steps>

## Commands

Use `/commands` for the full catalog. Common entries:

| Command                     | What it does                                                                         |
| --------------------------- | ------------------------------------------------------------------------------------ |
| `/start`                    | Rich intro with quick-start actions                                                  |
| `/help`                     | Usage guide with examples                                                            |
| `/commands [page] [filter]` | Paginated, filterable command catalog                                                |
| `/status`                   | SOMA / GEN snapshot, runtime health, and **Bars** excerpt from `body.md`             |
| `/body`                     | **[Dump `soma/body.md`](#body-command)** from the execution host (not via the model) |
| `/feelings`                 | Detailed emotional read                                                              |
| `/memories [count]`         | Recent episodic memory summaries                                                     |
| `/me`                       | Relationship snapshot — familiarity, warmth, trust                                   |
| `/models`                   | Inference and runtime model configuration                                            |
| `/tools`                    | Active tools for this entity                                                         |
| `/routines`                 | Scheduled automations                                                                |
| `/ping`                     | Liveness check                                                                       |
| `/busy`                     | **[Toggle busy indicator](#busy-indicator)** for this chat (`on` / `off` / `status`) |
| `/whoami`                   | Your Telegram user ID                                                                |
| `/reset`                    | Clear in-memory conversation (episodic memory unchanged)                             |

Session, privacy, compaction, and self-update commands (`/session_*`, `/privacy`, `/compact`, `/update`, etc.) are documented in-app via `/commands`.

## Media

* **Photos and image documents** — downloaded and passed to the model as vision input. Caption optional.
* **Voice and audio messages** — downloaded and passed to the harness as audio input (handling depends on your cognition / transcription setup).

## Human-paced multi-bubble replies

Plain-text replies (the usual deliberate path) are **not** sent as a single giant message whenever they fit under Telegram’s 4096-character cap. The adapter splits outgoing text using harness **`presence`** settings so multi-line answers can read like a person texting in **several bubbles**.

| Setting                 | Default | Role                                                                                                                                                                    |
| ----------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message_chunk_max`     | 400     | Target maximum characters per bubble (clamped 120–3900). Lines are packed up to this size, similar to Discord’s line-aware chunking.                                    |
| `chunk_delay`           | 2.0     | Base seconds between bubbles before jitter and compose time are added.                                                                                                  |
| `typing_speed_base`     | 30      | Characters-per-second style baseline; used to add a **length-scaled** pause before the *next* bubble (shorter follow-ups feel snappier; longer ones wait a bit longer). |
| `typing_speed_variance` | 0.3     | Random jitter on the base pause between bubbles (`chunk_delay ×` roughly `1 ± variance`).                                                                               |

Between bubbles the bot also triggers a **typing** action (`send_chat_action`) so the gap does not look like a dead connection.

Configure these under **`presence:`** in `configs/default.yaml` or your entity YAML (same block as `heartbeat_interval` / `initiative_cooldown`).

**Hard cap:** individual Telegram messages are still limited by the API (\~4096 characters); very long lines without newlines are split mechanically so nothing is dropped.

**`say()` tool:** each `say()` is still **one** outbound message from the tool path. Natural multi-bubble *without* multiple `say()` calls comes from the **final** reply going through `send_plain_chunks` with the settings above.

## Busy indicator

While the harness runs **`perceive`** for an incoming Telegram message, the bot can show a short **busy line** (not limited to tool use — it covers the whole turn):

* **Monospace** (`<code>` in HTML mode) with a braille-style spinner and a single **random gerund** in the style of Claude Code (e.g. `thinking`, `reticulating`, `vibing`). The spinner updates every edit; the word **changes every \~20** spinner frames.
* Posted as a **reply** to the user's message when metadata allows.
* **Pinned** quietly (`disable_notification`) so it stays visible.
* The system **“pinned a message”** service line is **deleted** when it refers to this pin (whether Telegram attributes that line to the bot or the user).
* **Unpinned and deleted** when the turn finishes and the real reply is delivered.

This is **harness-only UX**. The line does **not** append to conversation history for the model, and it does **not** enter the **`observe` tool** message ring. Typing actions (`send_chat_action`) may still run in parallel.

### Turning it off

Some chats prefer not to see the pinned line. **`/busy`** toggles the indicator **per chat** (also **`/busy on`**, **`/busy off`**, **`/busy status`**). The choice is stored in the entity database and survives restarts.

Implementation: `TelegramPlatform.run_busy_indicator` + `main.py` `on_inp` (see the main repo).

## `/body` command

**`/body`** prints the raw **`soma/body.md`** file as one or more Telegram HTML `<pre>` blocks — the same file [Soma](/architecture/soma) flushes for the agent, under the entity workspace (`…/soma/body.md`).

* It uses the **execution client** (`read_file` on `soma/body.md`): **local disk** when the worker has a workspace, or **HTTP execution RPC** when `BUMBLEBEE_EXECUTION_RPC_URL` / `tools.execution.base_url` points at Railway — so hybrid setups read the **worker’s** file, not your laptop’s.
* It does **not** go through the main LLM; if your deployment blocks file tools (e.g. hybrid without RPC), `/body` is blocked with the same guidance as `read_file`.

Long files are split across multiple messages so each stays under Telegram’s size limit.

## Privacy and operators

Operators can lock the bot to approved users only.

### Configure operators

Set operator user IDs in entity YAML:

```yaml theme={null}
presence:
  platforms:
    - type: "telegram"
      token_env: "TELEGRAM_TOKEN"
      operator_user_ids: [123456789]
```

Or via environment variable (useful for Railway where `canary.yaml` isn't in the image):

```env theme={null}
BUMBLEBEE_TELEGRAM_OPERATOR_IDS=123456789,987654321
```

Values from both sources are merged. Use `/whoami` in chat to find your ID.

### Privacy commands

| Command               | Effect                          |
| --------------------- | ------------------------------- |
| `/private on`         | Lock to operators only          |
| `/private off`        | Open to everyone                |
| `/privacy lock`       | Same as `/private on`           |
| `/privacy open`       | Same as `/private off`          |
| `/privacy allow <id>` | Grant access to a specific user |
| `/privacy deny <id>`  | Revoke access                   |

The allowlist persists in the entity database and survives restarts. While locked, static `allowed_user_ids` in YAML are ignored.

## Optional settings

```yaml theme={null}
presence:
  platforms:
    - type: "telegram"
      token_env: "TELEGRAM_TOKEN"
      concurrent_updates: 1      # updates processed in parallel (1–256)
      poll_timeout: 25.0          # long-poll timeout (seconds)
      poll_interval: 0.35         # interval between polls (seconds)
      allowed_user_ids: []        # restrict DMs (empty = open)
      operator_user_ids: []       # /privacy operators
```

## Inline mode

In BotFather, run `/setinline` for your bot and pick a placeholder. Users can then type `@YourBot ...` in any chat for quick identity cards.
