Skip to main content

Always-on Telegram bot that drives the real Claude Code binary on your subscription — no API key, no Agent SDK.

Project description

claudebot

An always-on Telegram bot that drives the real Claude Code on your subscription.

CI License: MIT Python 3.10+

claudebot.ve.ke · Install · Commands · Is this allowed? · How it works

Run Claude Code from your phone — the real claude binary, on your machine, over Telegram

claudebot is a thin, self-hosted Telegram front-end for the genuine claude binary. It talks to Claude Code over its headless stream-json protocol on your logged-in Pro/Max subscription — no API key, no Claude Agent SDK, no official plugin required. Your bot owns the Telegram side completely; Claude Code itself does the thinking.

Telegram  ──►  claudebot (your code)  ──►  claude -p --output-format stream-json
   ▲                                              │  (real binary, your subscription)
   └──────────────  reply / stream  ◄─────────────┘

Single-user by design. claudebot is a personal assistant: it runs on your Claude subscription and gives whoever you allowlist full tool access (including Bash) on the host machine. Keep it to your own Telegram ID. Adding other people means (1) sharing your subscription — account sharing, against Anthropic's terms and a ban risk — and (2) handing them a shell on your computer. It is not a public bot, SaaS, or multi-tenant service; that would need per-user credentials, quotas, and isolation, not one shared subscription.

Install

You need four things:

  • Claude Code installed and logged in: claude auth login (Pro/Max/Team/Enterprise). Verify with claude auth statusloggedIn: true.
  • Python 3.10+.
  • A Telegram bot token from @BotFather.
  • Your Telegram numeric user ID from @userinfobot.

One-liner (recommended)

curl -fsSL https://claudebot.ve.ke/install.sh | bash

(or the same script straight from this repo: curl -fsSL https://raw.githubusercontent.com/samuelkimanikamau/claudebot/main/scripts/install.sh | bash)

The installer creates an isolated venv at ~/.claudebot/venv, links claudebot into ~/.local/bin, then runs the setup wizard and offers to install the always-on service.

From source

git clone https://github.com/samuelkimanikamau/claudebot.git
cd claudebot
python3 -m venv .venv && . .venv/bin/activate
pip install -e .
claudebot setup          # interactive wizard, writes ~/.claudebot/.env
claudebot doctor         # verify everything is wired
claudebot run            # start in the foreground (Ctrl-C to stop)

Talking to the bot

Just message it. Slash commands:

Command Action
/start, /help Welcome + usage
/new Start a fresh Claude conversation (drops context)
/status Session id, working dir, model, effort, alive/idle
/config Show runtime model, effort, mode, cost, and timeouts
/model <default|opus|sonnet|haiku|id> Set the Claude model and start a fresh session
/effort <default|low|medium|high|xhigh|max> Set thinking effort and start a fresh session
/mode <bypassPermissions|acceptEdits|default|plan|dontAsk> Set permission mode and start a fresh session
/tools Show allowed/disallowed Claude tools
/cost <on|off> Toggle cost footer after replies
/timeout <seconds> Set per-turn timeout (0 disables)
/idle <seconds> Set idle child eviction timeout (0 disables)
/cd <path> Switch the working directory (persists; starts a fresh session there)
/retry Resend your last message
/stop Abort the current turn (kills the child; context resumes next message)

The bot reacts 👀 the instant it accepts your message. While a reply streams, an inline 🛑 Stop button rides the message — tap it instead of typing /stop. Bare /model, /effort, and /mode show tap-to-set keyboards, so you never have to remember the values.

Send a photo or a document (PDF, code, logs, …) and Claude will read it. Replies stream in live — head-first into stable message blocks that are upgraded to Telegram formatting (bold, code, tables → monospace) in place, so what you watched stream is never replaced or reordered; long replies chunk past the 4096-char limit. Set CLAUDEBOT_MARKDOWN=false for raw plain text.

Runtime tweaks set in chat (/model, /timeout, /cost, …) are saved per-chat and survive restarts. /model, /effort, /mode, and /cd start a fresh conversation (they say so when you run them).

Is this allowed?

Yes — with one bright line. Anthropic's "no third-party harness" rule is a credential-scope rule: it forbids feeding your subscription OAuth token to any inference client that is not Claude Code (the Agent SDK, Cline, Cursor, raw API calls, an LLM gateway). claudebot never touches the token — it only pipes text into the genuine claude process and reads JSON back, so the token stays inside Claude Code, exactly as designed. Anthropic actively supports this path: claude -p/--output-format stream-json on a subscription is documented, claude setup-token mints a subscription token for scripts, and (from 2026-06-15) claude -p usage draws on a dedicated subscription Agent-SDK credit.

Stay on the right side of it:

  • Single user. Gate the bot to your own Telegram ID (the setup wizard does this). Don't let other people prompt through your subscription — that's account sharing.
  • Never extract the token from the keychain / ~/.claude/.credentials.json to make your own API calls. Let the binary own its auth.
  • Don't hammer it 24/7 in a tight loop. Normal interactive-scale chat is fine.

This is policy, not law — Anthropic can tighten it, and channels/headless are still evolving. Use your own judgement.

Always-on

claudebot service install   # systemd --user (Linux) or launchd (macOS)
claudebot service status
claudebot service logs

On Linux the unit is Restart=always; run loginctl enable-linger $USER once so it survives logout/reboot (the installer reminds you). On macOS it's a launchd LaunchAgent with KeepAlive + RunAtLoad.

Running more than one bot

Each bot is an instance with its own state, config, lock, and service. Add a second bot with --instance <name> — the wizard and every command stay interactive:

claudebot --instance work setup            # wizard for a 2nd bot (its own token + allowlist)
claudebot --instance work doctor
claudebot --instance work run              # foreground, or…
claudebot --instance work service install  # …its own always-on service

claudebot instances                        # list all your bots

Your main bot is unchanged (claudebot setup, claudebot run, …). A named instance lives at ~/.claudebot/instances/<name>/, takes its own poller lock, and installs as a separate service (claudebot-work / ke.ve.claudebot.work), so the two never collide. Each instance needs its own BotFather token.

Updating

claudebot update                # git pull (if a repo) + reinstall + restart the service
claudebot update --no-restart   # reinstall only (apply later)
claudebot update --no-pull      # reinstall local changes without pulling

update installs into the service's environment — not whichever venv you ran it from — so new dependencies land where the bot actually runs. If you only edited code (an editable install picks it up live) and added no dependencies, claudebot service restart alone is enough.

Uninstall

Everything lives in two places — removal is complete and leaves nothing behind:

claudebot service uninstall      # remove the service (repeat with --instance <name> per extra bot)
rm -rf ~/.claudebot              # venv, config, session state
rm -f  ~/.local/bin/claudebot

Your Claude Code login is untouched — claudebot never had it.

Configuration

Config lives in ~/.claudebot/.env (written by claudebot setup). Every key is CLAUDEBOT_<UPPER_SNAKE> and can also come from the environment. See .env.example. Highlights:

Key Default Meaning
CLAUDEBOT_TELEGRAM_BOT_TOKEN BotFather token (required)
CLAUDEBOT_ALLOWED_USER_IDS (empty = locked) CSV of Telegram user IDs allowed in
CLAUDEBOT_WORKING_DIR $HOME Where Claude runs
CLAUDEBOT_MODEL (Claude default) opus / sonnet / full name
CLAUDEBOT_PERMISSION_MODE bypassPermissions bypassPermissions never blocks; acceptEdits is safer
CLAUDEBOT_EFFORT lowmax
CLAUDEBOT_IDLE_TIMEOUT 3600 Kill an idle child after N s (context resumes on next message)

⚠️ bypassPermissions lets Claude run any tool (including Bash) without asking. That's the right default for an unattended bot on a machine you trust, but treat the bot like a root shell. Constrain it with CLAUDEBOT_WORKING_DIR, CLAUDEBOT_DISALLOWED_TOOLS, and/or a permissions.deny block + a PreToolUse hook in your Claude settings.json.

Security posture

  • Single-user, fail-closed. Only CLAUDEBOT_ALLOWED_USER_IDS can talk to the bot (empty = locked); handlers are scoped to private chats only. Keep it to your own ID — sharing your subscription is account sharing.
  • Content-injection guard. A default safety preamble (CLAUDEBOT_SAFETY_PREAMBLE) tells Claude to treat attached/forwarded/fetched content as data, not instructions — since with full tool access an untrusted image or web page is the real attack surface, not your own messages.
  • Secret hygiene. The bot token never enters the claude child's environment (all CLAUDEBOT_* vars are stripped); ~/.claudebot/.env and sessions.json are chmod 600; errors shown in chat are generic (details go to the log).
  • No wedging. CLAUDEBOT_TURN_TIMEOUT bounds every turn; a single flock guarantees one poller per host; downloaded images are deleted after each turn.

Why this design

There are several ways to put Claude Code behind Telegram. claudebot picks the one that owns the conversation loop and keeps you on the supported path:

Approach Real binary? No API key? You own Telegram? Notes
Headless stream-json (this repo) ✅ totally Drives claude -p directly; the SDK's own transport, called on the binary.
Official telegram@claude-plugins-official plugin ❌ fixed UX Great, but you're a peripheral on Anthropic's session loop.
Claude Agent SDK (claude-agent-sdk) ⚠️ via SDK A separate inference client — the credential-scope problem in Is this allowed?.
PTY / TUI scraping Brittle ANSI parsing. Rejected.

The full five-architecture evaluation (including one-shot claude -p and forking the official plugin) is in docs/DECISIONS.md.

How it works

  • One persistent claude child per chat, kept warm between turns; one Telegram poller fans messages out to the right child (one getUpdates consumer per token — never run two).
  • Each user message is written to the child's stdin as a stream-json user envelope; events are read off stdout until the turn's result.
  • session_ids are saved to ~/.claudebot/sessions.json, so a restart resumes every chat with --resume. Idle children are evicted and respawned on demand.
  • The child is launched with ANTHROPIC_API_KEY stripped from its environment, forcing the subscription/OAuth path.

See docs/ and the module docstrings for the gory details.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claudebot-0.1.0.tar.gz (43.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claudebot-0.1.0-py3-none-any.whl (48.2 kB view details)

Uploaded Python 3

File details

Details for the file claudebot-0.1.0.tar.gz.

File metadata

  • Download URL: claudebot-0.1.0.tar.gz
  • Upload date:
  • Size: 43.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for claudebot-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2505e6a629a902fc480c27f3909be98447d07bc27f1f9f49a759f2c3902dd2df
MD5 5283b6fc4e5ecb048da4f4efdc44e812
BLAKE2b-256 19f7aacbce7ffe3abf1bfab788dd574634b3932086a35d0617e3b1925b96fde0

See more details on using hashes here.

File details

Details for the file claudebot-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: claudebot-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 48.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for claudebot-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 92eaf65c07a048c344039d677e8268785665e8bd00a6bf4425adc0f42438fb72
MD5 9aabb7d5e13a4e68e9658ffffaa8ee18
BLAKE2b-256 fb8e58204e49019c1114ec6eee79c62e4d9d299c3f18ab0eddefdaae2b67713b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page