---
title: Telegram
icon: paper-plane
---
The Telegram resource exposes Telegram chats as a virtual filesystem
mounted at some prefix such as `/telegram/`.

For token setup, see [Telegram Setup](/python/setup/telegram).

## Config

```python
import os

from mirage import MountMode, Workspace
from mirage.resource.telegram import TelegramConfig, TelegramResource

config = TelegramConfig(token=os.environ["TELEGRAM_BOT_TOKEN"])
resource = TelegramResource(config=config)
ws = Workspace({"/telegram": resource}, mode=MountMode.READ)
```

## Filesystem Layout

```text
/telegram/
  groups/
    <chat-title>__<chat-id>/
      <yyyy-mm-dd>.jsonl
      ...
  channels/
    <channel-title>__<chat-id>/
      <yyyy-mm-dd>.jsonl
      ...
  private/
    <user-name>__<chat-id>/
      <yyyy-mm-dd>.jsonl
      ...
```

Example:

```text
/telegram/
  groups/
    Dev Team__-1001234567890/
      2026-04-10.jsonl
      2026-04-11.jsonl
    Friends__-987654321/
      2026-04-11.jsonl
  channels/
    Announcements__-1009876543210/
      2026-04-11.jsonl
  private/
    alice__123456789/
      2026-04-10.jsonl
      2026-04-11.jsonl
```

Directory names embed the Telegram chat ID after `__` so that write
commands (`telegram-send-message --chat_id`, etc.) can reference the
correct resource without extra lookups.

### Groups

`/telegram/groups/` lists group and supergroup chats the bot has access
to. The chat ID is appended after `__`. Each chat directory contains
day-partitioned `.jsonl` history files.

### Channels

`/telegram/channels/` lists channel chats the bot has access to. The
chat ID is appended after `__`. Channels require the bot to be added
as an administrator.

### Private

`/telegram/private/` lists private (one-on-one) chats. The chat ID is
appended after `__`.

> **Note:** Telegram bots have a 24-hour message window limitation. The
> bot can only fetch messages from the last 24 hours of updates it has
> received. Older messages may not be available.

## Cache

The Telegram resource uses `IndexCacheStore` (same as Discord, Slack,
and other resources). Index entries store chat IDs and chat metadata
for directory listing. There is no separate content cache - file content
caching is handled by the workspace `IOResult` mechanism.

## Example

```python
import asyncio
import os

from dotenv import load_dotenv

from mirage import MountMode, Workspace
from mirage.resource.telegram import TelegramConfig, TelegramResource

load_dotenv(".env.development")

config = TelegramConfig(token=os.environ["TELEGRAM_BOT_TOKEN"])
resource = TelegramResource(config=config)


async def main():
    ws = Workspace({"/telegram": resource}, mode=MountMode.READ)

    # List categories
    r = await ws.execute("ls /telegram/")
    print(await r.stdout_str())

    # List groups
    r = await ws.execute("ls /telegram/groups/")
    print(await r.stdout_str())

    groups = r.stdout_str().strip().splitlines()
    if groups and groups[0].strip():
        chat = groups[0].strip()
        base = f"/telegram/groups/{chat}"

        # List date files
        r = await ws.execute(f'ls "{base}/"')
        print(await r.stdout_str())

        dates = r.stdout_str().strip().splitlines()
        if dates:
            target = dates[0].strip()
            file_path = f"{base}/{target}"

            # Read messages
            r = await ws.execute(f'cat "{file_path}" | head -n 5')
            print(await r.stdout_str())

            # Extract sender names with jq
            r = await ws.execute(
                f'jq ".from.first_name" "{file_path}" | head -n 5')
            print(await r.stdout_str())

    # Tree view
    r = await ws.execute("tree -L 2 /telegram/")
    print(await r.stdout_str())


if __name__ == "__main__":
    asyncio.run(main())
```

See `examples/chat/telegram.py` for the full working example.

## Finding Chat IDs

Chat IDs are embedded in directory names after `__`:

```bash
# Chat ID - embedded in directory name after "__"
ls /telegram/groups/
# -> Dev Team__-1001234567890   <- chat_id = -1001234567890

ls /telegram/channels/
# -> Announcements__-1009876543210   <- chat_id = -1009876543210

ls /telegram/private/
# -> alice__123456789   <- chat_id = 123456789

# Message ID - inside JSONL messages
jq -r '"\(.message_id) [\(.from.first_name)] \(.text)"' \
  "/telegram/groups/Dev Team__-1001234567890/2026-04-11.jsonl" | head -n 5
# -> 42 [Alice] hello world

# Find a message then reply
jq -r 'select(.text | test("hello")) | .message_id' \
  "/telegram/groups/Dev Team__-1001234567890/2026-04-11.jsonl"
# -> 42
telegram-send-message --chat_id -1001234567890 \
  --text "Reply" --reply_to_message_id 42
```

## Working with Chats

Tips for efficient access:

```bash
# Check message count per day
wc -l "/telegram/groups/Dev Team__-1001234567890/2026-04-11.jsonl"

# Read only recent messages
tail -n 10 "/telegram/groups/Dev Team__-1001234567890/2026-04-11.jsonl"

# Search across all dates
rg "keyword" "/telegram/groups/Dev Team__-1001234567890/"

# Extract specific fields to reduce output
jq -r '"\(.from.first_name): \(.text)"' \
  "/telegram/groups/Dev Team__-1001234567890/2026-04-11.jsonl" | head -n 20

# Count messages per user
cat "/telegram/groups/Dev Team__-1001234567890/2026-04-11.jsonl" \
  | jq -r '.from.first_name' | sort | uniq -c
```

## Shell Commands

Standard commands available on the mounted Telegram tree:

| Command         | Notes                                      |
| --------------- | ------------------------------------------ |
| `ls`            | List categories, chats, dates              |
| `cat`           | Read .jsonl history                        |
| `head` / `tail` | First/last N lines                         |
| `grep` / `rg`   | Pattern search (file or directory level)   |
| `jq`            | Query JSON; use `.[]` prefix for JSONL     |
| `wc`            | Line/word/byte counts                      |
| `stat`          | File metadata (name, size, type)           |
| `find`          | Recursive search with `-name`, `-maxdepth` |
| `tree`          | Directory tree view                        |

Resource-specific commands:

### `telegram-send-message`

Send a message to a chat, optionally as a reply.

```bash
telegram-send-message --chat_id -1001234567890 --text "Hello from MIRAGE"
telegram-send-message --chat_id -1001234567890 --text "Reply" --reply_to_message_id 42
```

| Option                  | Required | Description            |
| ----------------------- | -------- | ---------------------- |
| `--chat_id`             | yes      | Telegram chat ID       |
| `--text`                | yes      | Message text to send   |
| `--reply_to_message_id` | no       | Message ID to reply to |

The chat ID can be found in directory names under
`/telegram/groups/`, `/telegram/channels/`, or `/telegram/private/`
(after `__`). Returns the posted message JSON.
