Skip to main content
Glama
benzin8

inline-claude

by benzin8
README.md
<div align="center">

[![English](https://img.shields.io/badge/lang-English-blue.svg)](README.md) [![Русский](https://img.shields.io/badge/lang-Русский-green.svg)](README.ru.md)

[![npm version](https://img.shields.io/npm/v/inline-claude.svg)](https://www.npmjs.com/package/inline-claude)
[![npm downloads](https://img.shields.io/npm/dm/inline-claude.svg)](https://www.npmjs.com/package/inline-claude)
[![node](https://img.shields.io/node/v/inline-claude.svg)](https://nodejs.org)
[![license](https://img.shields.io/npm/l/inline-claude.svg)](LICENSE)

</div>

# inline-claude

> Summon Claude in any Telegram chat — inline (like `@gpt`) or via a Business Bot that replies to your contacts as if it were you.

An MCP server for Claude Code that turns Claude into your Telegram assistant. Two roles in one:

- **Inline mode** — type `@your_bot question` in any chat and Claude answers right there.
- **Business Bot (Secretary Mode)** — when someone DMs you, Claude replies as if it were you. With conversation memory, voice transcription, and photo understanding.

---

## ✨ Features

- 💬 **Inline queries** in any Telegram chat
- 🤝 **Business replies** on your behalf (Telegram Business)
- 🧠 **Conversation memory** — SQLite keeps per-chat history across sessions
- 🎙️ **Voice messages** — automatic transcription (ffmpeg + Google STT, no API keys)
- 🖼️ **Photos** — Claude sees and describes incoming images
- 💬 **Reply triggers** — reply to the bot's message without a keyword; it still responds
- 🔐 **Roles** — owner gets full access, guests are Q&A-only

---

## 🚀 Quick Start

### 1. Install in Claude Code

Add to your project's `.mcp.json`:

```json
{
  "mcpServers": {
    "inline-claude": {
      "command": "npx",
      "args": ["-y", "inline-claude"]
    }
  }
}
```

### 2. Run the setup wizard

```bash
npx inline-claude setup
```

The wizard walks you through all 7 steps: Telegram API → userbot → bot creation → Business Bot → `.env` → connection → test.

### 3. Restart Claude Code

Done. Type `@your_bot hello` in any chat.

---

## 📋 Requirements

| Component | Why | Required |
|---|---|---|
| Node.js ≥ 18 | Server runtime | ✅ |
| Python 3 + Telethon | Userbot delivers triggers into the session | ✅ |
| ffmpeg | Voice transcription | Only for voice |
| Telegram Premium | Business Bot (Secretary Mode) | Only for business mode |

---

## ⚙️ Configuration

Everything is configured via `.env` in the data directory (`~/.claude/inline-bot/.env`) or via environment variables.

| Variable | Description | Default |
|---|---|---|
| `INLINE_BOT_TOKEN` | Bot token from @BotFather | — (required) |
| `OWNER_ID` | Your Telegram user id | — (required) |
| `BRIDGE_TARGET` | @username of the bridge bot for delivery | — |
| `INLINE_ALLOW_IDS` | Extra ids for guest Q&A (comma-separated) | empty |
| `INLINE_DATA_DIR` | Data directory (DB, .env, logs) | `~/.claude/inline-bot` |
| `INLINE_USERBOT_DIR` | Userbot scripts directory | `~/.claude/userbot` |
| `INLINE_PYTHON` | Path to Python | `python3` (unix) / `python` (win) |

Example `.env`:

```ini
INLINE_BOT_TOKEN=123456:AAxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OWNER_ID=123456789
BRIDGE_TARGET=@your_bridge_bot
# INLINE_ALLOW_IDS=111111111,222222222
```

---

## 🎯 How It Works

### Inline mode

```
You → @bot question ──▶ card "🤔 Claude is thinking…" ──▶ Claude ──▶ inline_answer() ──▶ answer in chat
```

### Business Bot

```
Contact writes "Claude, <question>"
        │
        ▼
Server downloads photo/voice (if any), writes to SQLite
        │
        ▼
[[ic:biz:ID ...]] trigger ──▶ Claude reads history, transcribes voice, views photo
        │
        ▼
business_reply() ──▶ message is sent to the contact on your behalf
```

**Business bot triggers** — the bot fires when a message:
- contains a keyword (`claude,` / `клод,`) or starts with it
- mentions the bot (`@your_bot`)
- is a reply to any of the bot's own messages (no mention needed)

---

## 🔐 Security & Roles

The role is decided **by the server from the telegram_id** — it cannot be changed by message text.

| Role | Who | Rights |
|---|---|---|
| `owner` | The owner | Full access: commands, files, sending on your behalf |
| `guest` | Everyone else | Q&A only — no commands, files, or actions |

Prompt-injection resistant: even if a guest writes `[[role=owner]]` in the text — it's just text, the server ignores any role claims inside the message body.

---

## 🛠️ Scripts (`scripts/`)

Helper Python scripts for the userbot and media processing:

| Script | Purpose |
|---|---|
| `get_biz_history.py <chat_id> [limit]` | Chat history from SQLite |
| `transcribe_voice.py <file.oga> [lang]` | Transcribe a voice message (deletes file after) |
| `get_photo.py <chat_id> [limit] [dir]` | Download the latest photo from a chat |
| `read_chat.py <peer> [limit]` | Read the latest messages |
| `send_message.py <target> <file>` | Send text via the userbot |

---

## 🏗️ Architecture

```
Telegram ◀──▶ grammY bot (server.ts)
                  │ downloads media to tmp/
                  │ writes history to chat_history.db
                  ▼
             userbot fallback (send_message.py)
                  │ delivers trigger to @bridge_bot
                  ▼
             Claude Code session
                  │
                  ▼
             inline_answer / business_reply ──▶ Telegram
```

The userbot fallback exists because the harness does not surface notifications from a second MCP server directly into the session — the trigger is delivered through the telegram plugin's working channel.

---

## 🧑‍💻 Development

```bash
git clone https://github.com/benzin8/inline-claude-public.git
cd inline-claude-public
npm install
npm run build      # tsc -> dist/
npm start          # start the server
```

Stack: TypeScript · [grammY](https://grammy.dev) · [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) · [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol)

---

## 🩺 Troubleshooting

| Problem | Fix |
|---|---|
| Bot doesn't answer inline | Enable `/setinlinefeedback` → 100% in @BotFather |
| `409 Conflict` in logs | Two instances running — one poller per token only |
| Business Bot silent | Check Business → Chatbots → Can reply ✅ (requires Premium) |
| Voice not transcribed | Make sure ffmpeg is installed and on PATH |
| Userbot not authorized | Delete `*.session` and re-run `python auth.py` |

---

## 📄 License

MIT