Guides

Telegram Adapter

Production-quality Telegram adapter using ORO Bridge SDK — the reference implementation.

Telegram Adapter

The oro-tg-adapter is the reference implementation of a platform adapter. It connects a Telegram supergroup with forum topics to ORO Claude Bridge.

Architecture

Telegram Supergroup (forum topics)
       ↕ Bot API
┌──────────────────────────────┐
│      oro-tg-adapter          │
│  ┌────────────────────────┐  │
│  │ TelegramPlatformAdapter│  │  ← PlatformAdapter impl
│  │ TelegramFormatter      │  │  ← Formatter impl
│  └────────────┬───────────┘  │
│               │              │
│  ┌────────────┴───────────┐  │
│  │ bridge-adapter-sdk     │  │  ← Runtime, Store, Orchestrators
│  └────────────────────────┘  │
└──────────────────────────────┘
       ↕ Socket.IO + REST
    ORO Claude Bridge

Key Mapping

Bridge ConceptTelegram Concept
SessionForum Topic
Session TitleTopic Name
MessageTopic Message
AttachmentDocument/Photo
Session DeleteTopic Close

Project Structure

oro-tg-adapter/
├── src/
│   ├── index.ts              # Entry point + runtime setup
│   ├── config.ts             # Environment variable loading
│   └── telegram/
│       ├── platform.ts       # TelegramPlatformAdapter
│       ├── formatter.ts      # TelegramFormatter (SemanticBlock → HTML)
│       └── markdown.ts       # Markdown → Telegram HTML converter
├── package.json
└── tsconfig.json

Just 5 files. Everything else (bridge transport, store, reconciliation, replay, streaming, dedup) is handled by the SDK.

Environment Variables

TELEGRAM_BOT_TOKEN=123456:ABC-DEF   # Bot token from @BotFather
TELEGRAM_CHAT_ID=-100123456789      # Supergroup chat ID
BRIDGE_URL=http://localhost:3000    # Bridge server URL
DATA_DIR=./data                     # SQLite database directory
SKIP_REPLAY=false                   # Skip historical replay on start

PlatformAdapter Highlights

Container → Forum Topic

async createContainer(session: SessionSummary): Promise<ContainerId> {
  const topic = await this.bot.createForumTopic(this.chatId, {
    name: truncate(title, 128),
    icon_color: this.hashColor(session.projectName),
  })
  return String(topic.message_thread_id)
}

Incoming Message Parsing

The adapter parses Telegram updates and calls handlers.onMessage() with normalized data:

// Text message with optional photos/documents
handlers.onMessage({
  updateId: String(update.update_id),
  containerId: String(msg.message_thread_id),
  platformMessageId: String(msg.message_id),
  text: msg.text || msg.caption || '',
  files: this.extractFiles(msg),
  sender: {
    id: `tg:${msg.from.id}`,
    username: msg.from.username,
    displayName: buildDisplayName(msg.from),
  },
})

Attachment Round-Trip

Incoming (TG → Bridge): Download via Bot API → temp file → upload to bridge.

Outgoing (Bridge → TG): Read from bridge attachment path → send via Bot API as photo/document based on type.

Formatter Highlights

SemanticBlock → Telegram HTML

The formatter renders semantic blocks to Telegram-compatible HTML:

  • text with inlineFormat: 'md' → markdown-to-HTML conversion
  • tool_card → bold name + italic summary + expandable blockquote for input/result
  • code<pre><code class="language-X"> blocks
  • result_card → usage stats in monospace
  • user_message_card → formatted conversation buffer

Message Chunking

Telegram messages have a 4096-character limit. The formatter automatically chunks long messages:

render(blocks: SemanticBlock[]): FormattedContent[] {
  const html = this.renderBlocks(blocks)
  return chunkText(html, 4000).map(chunk => ({
    kind: 'text',
    text: chunk,
    parseMode: 'html',
  }))
}

Running

# Install dependencies
npm install

# Build
npx tsc

# Run
node dist/index.js

The adapter will:

  1. Connect to the bridge
  2. Fetch all sessions
  3. Create forum topics for each session
  4. Start listening for both bridge events and Telegram messages
  5. Route messages bidirectionally