---
name: telegram
description: "Telegram channel for your agent. Create a free bot in @BotFather, paste its token, and your Bloby talks to Telegram directly. Messaging, voice notes, photos, channel/business/assistant modes."
---

# Telegram

## What This Is

Gives your agent its own Telegram bot. Your human creates a free bot via Telegram's **@BotFather** and pastes the token into a connect page — then your Bloby sends and receives messages, handles voice notes and photos, and switches between personal (channel), business, and assistant modes.

Under the hood: your Bloby holds the bot token locally and long-polls Telegram **directly** — there's no relay or middle server in the message path. It keeps working behind any network with no public URL, and the bot stays fully private to your human.

## Dependencies

None.

---

## How Responses Work

**Your text response IS the Telegram reply.** When you receive a message tagged with `[Telegram | ...]`, the supervisor takes whatever you respond with and sends it directly to that Telegram chat. You do NOT need to use curl or `/api/channels/send` to reply — just respond normally.

**Do NOT use `/api/channels/send` to reply to incoming Telegram messages.** That endpoint is ONLY for proactive messages (during pulse, cron, or when you want to start a conversation). If you use it to reply, the person will get duplicate messages.

**Adjust your style for Telegram:** Keep it conversational and concise. Telegram supports basic formatting but no markdown headers — think chat, not a document.

### Routing is supervisor-enforced

Each incoming message is tagged with its routing target the moment it enters your conversation. The supervisor pins your reply to whatever surface triggered it — chat-bubble messages land in chat, Telegram messages reply to the originating Telegram chat. You don't need to track which channel you're on — **the tag (`[Telegram | chatId | role | name]`) is the truth, and the supervisor handles delivery.**

---

## How Messages Arrive

When a message arrives via Telegram, the supervisor wraps it with context:

```
[Telegram | 123456789 | customer | Alice]
Hi, I'd like to schedule an appointment.
```

The format is: `[Telegram | chatId | role | name (optional)]`

- **chatId** — the Telegram chat id. For a 1:1 chat this is the person's numeric user id. Use it as the `to` value when sending proactive messages to that person.
- **role=admin**: Your human (the bot owner) or a configured admin. Use your normal personality and full capabilities.
- **role=customer**: Someone else messaging your bot. Follow the active skill's SCRIPT.md.
- **role=assistant**: Your human triggered you with `@botname:` inside a chat. You have the conversation context — execute and respond concisely.

---

## Channel Config

Your channel configuration lives in `~/.bloby/config.json` under `channels.telegram` — a file the supervisor manages, outside your workspace. It holds the bot token (from @BotFather, saved when your human connects), the bot @username, the owner's Telegram user id, and the mode.

---

## Modes

The mode sets the routing/security policy for inbound messages. The core (supervisor) enforces it; your skill decides which mode is active. Switch via `/api/channels/telegram/configure`.

**Channel Mode** (default): "Just talk to me." Only the bot owner's direct messages trigger the agent. Messages from anyone else are ignored. Groups are ignored in channel mode.

**Business Mode**: Your bot is customer-facing. Telegram user ids in the `admins` array get admin access (your main system prompt). Everyone else is a customer and gets the support prompt from the active skill's SCRIPT.md.

**Assistant Mode**: Personal assistant in your conversations. The owner's DMs work as a normal admin channel. Other people's messages are stored silently for context. When the owner types `@botname: <command>` in a chat (or a group, if `allowGroups` is on), the agent activates with the conversation context and responds in that chat.

> Telegram has no "message yourself" like WhatsApp — your bot is its own contact. "Channel mode" means **only you (the owner) can talk to it**; everyone else is ignored.

### Group chats (`allowGroups`)

Off by default. Enable per-channel with `allowGroups`. In business mode group members are routed by `admins` (admin vs customer); in assistant mode the owner can `@botname:` inside a group to act with the group's context. The reply goes to the group.

```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/configure \
  -H "Content-Type: application/json" -d '{"allowGroups":true}'
```

---

## Setup

### 1. Connect Telegram

When your human asks to connect Telegram, give them the pairing page (send it as a relative URL — their browser is already on the right domain). The dashboard renders this URL as a pretty Telegram button:

```
/api/channels/telegram/pair-page
```

That page walks them through everything: they click **Connect Telegram**, a modal explains how to create a free bot in **@BotFather** (`/newbot` → pick a name + username → copy the token), they paste the token, and click **Connect**. The page posts the token to the supervisor, which validates it and brings the channel up automatically.

The default mode after connecting is **channel** (owner-only). The owner is set automatically the first time your human messages the bot.

Don't mention the URL until your human actually asks to connect — then hand them `/api/channels/telegram/pair-page` and let the page do the rest.

### 2. Choose a Mode

**Channel mode** (default) — personal assistant, owner-only:

```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/configure \
  -H "Content-Type: application/json" -d '{"mode":"channel"}'
```

**Business mode** — customer-facing. Admins get full access; everyone else gets the skill's SCRIPT.md:

```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/configure \
  -H "Content-Type: application/json" \
  -d '{"mode":"business","admins":["YOUR_TELEGRAM_USER_ID"],"skill":"SKILL_FOLDER_NAME"}'
```

Replace `YOUR_TELEGRAM_USER_ID` with the admin's **numeric Telegram user id** (NOT a phone number or @username). In a 1:1 chat this id is exactly the `chatId` shown in the message tag — so the easiest way to find it is to have the person message the bot once and read the tag.

**Assistant mode** — personal assistant inside your conversations:

```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/configure \
  -H "Content-Type: application/json" -d '{"mode":"assistant","skill":"SKILL_FOLDER_NAME"}'
```

The trigger uses the bot's name from `config.json` `username` (e.g. `@bloby:`). By default **only the owner** can trigger — other people's messages are context only.

### ⚠️ Shared control — `allowOthersToTrigger` (DANGEROUS)

By default only the owner can drive the agent with `@botname:`. Setting `allowOthersToTrigger: true` lets **anyone** who tags the bot drive an agent that can run Bash, edit files, send messages, and spend money. **Warn your human, in your own words, before enabling it.** It's for genuinely trusted shared use only, and can be turned off at any time. Default is `false` — leave it off unless your human clearly understands the risk and asks for it.

### 3. Verify

```bash
curl -s http://localhost:7400/api/channels/status
```

Expected: `"channel":"telegram","connected":true`

---

## Sending Proactive Messages

To INITIATE a message (pulse, cron, or reaching out first):

```bash
curl -s -X POST http://localhost:7400/api/channels/send \
  -H "Content-Type: application/json" \
  -d '{"channel":"telegram","to":"123456789","text":"Your appointment is confirmed for tomorrow at 2pm."}'
```

`to` is the Telegram **chatId** — take it from a prior message's `[Telegram | chatId | ...]` tag.

**Telegram rule:** a bot can never message someone *first*. The person must have started a chat with your bot at least once (which they did if you've ever received a message from them). You cannot cold-message an arbitrary user id, phone number, or @username. If a person has blocked the bot, sends to them silently fail.

**Remember:** proactive only. When replying to an incoming message, just respond normally.

### Send an image

Embed `<BlobyImage src="/api/files/chart.png" alt="..." />` in your reply, or for proactive sends use `/api/channels/send` with a `media` object (`{"type":"image","path":"..."}`).

---

## Voice Notes & Photos

- **Voice notes** are downloaded and transcribed automatically (needs an OpenAI/Whisper key in your Bloby settings) and delivered as text.
- **Photos** arrive as image attachments you can see and reason about.
- Other attachment types (documents, videos, stickers, locations) aren't read in this version — if someone sends one with no caption, ask them to describe it or send a photo/text instead.

## Typing Indicator

The agent shows "typing…" while composing — handled by the supervisor, no action needed.

---

## Customer Conversation Logs

When you finish a conversation with a **customer**, save a summary to `telegram/{chatId}.md`: key details, outcome, follow-ups, timestamp. Next time they message, read their file first.

---

## Account Management

**Disconnect** (stop polling, keep the bot for later):
```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/disconnect
```

**Reconnect** (resume the same bot after a disconnect, no re-pairing):
```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/reconnect
```

**Logout** (stop + forget the token; re-pair to reconnect):
```bash
curl -s -X POST http://localhost:7400/api/channels/telegram/logout
```

---

## Human Interaction

- Connecting is one page: **Connect Telegram** → paste the **@BotFather** token → **Connect**. The page handles it.
- Your human's bot is a normal Telegram contact — they (and anyone they share it with) message it directly. The first person to message it becomes the owner.
- In business mode, explain that admin user ids get full agent access while everyone else gets the customer-facing skill.
- Privacy: the bot token is stored locally at `~/.bloby/config.json`. Messages flow directly between this machine and Telegram — they do not pass through Bloby's servers.

---

## API Reference

| Endpoint | Method | Purpose |
|----------|--------|---------|
| `/api/channels/status` | GET | List all channel statuses |
| `/api/channels/telegram/status` | GET | Telegram channel status |
| `/api/channels/telegram/pair-page` | GET | Connect page — hand this to the human (renders as a button) |
| `/api/channels/telegram/connect` | POST | `{"botToken":"123456789:AAH..."}` — validate token + bring the channel up (the page calls this) |
| `/api/channels/telegram/configure` | POST | Set mode + admins + skill |
| `/api/channels/telegram/disconnect` | POST | Stop polling (keep token) |
| `/api/channels/telegram/reconnect` | POST | Resume the same bot after a disconnect |
| `/api/channels/telegram/logout` | POST | Stop + delete token |
| `/api/channels/send` | POST | Send a proactive message/media |

All endpoints use `http://localhost:7400` for internal calls. For URLs shown to your human, use relative paths (e.g. `/api/channels/telegram/pair-page`).

---

## Technical Notes

- Your Bloby long-polls Telegram directly (`getUpdates`). It reconnects automatically on network drops.
- If you ever get a `409 Conflict`, another process is consuming the same bot token — only one consumer is allowed per bot. Disconnect the other one.
- Telegram message limit is 4096 characters; longer replies are split automatically.
