A Python framework for building Telegram bots that use Claude Code to read, plan, and edit their own codebase - with human-in-the-loop approval, voice transcription via OpenAI Whisper, and self-restarting deployment.
anthropic claude-code claude-code-channels telegram-bot ai-agent self-improving-software whisper voice-to-code agentic-coding python
Your bot is a thin Telegram relay. Claude Code is the agentic part - it reads the codebase, proposes changes, and writes files when you approve. The bot just forwards your messages to Claude Code and sends back the response.
The result is a closed-loop development cycle: chat -> analyze -> plan -> approve -> edit -> restart. No IDE, no SSH, no deploy pipeline. Just a conversation with your running application from your phone - or even a voice message transcribed via Whisper.
What about Claude Code Channels? Anthropic's official Telegram/Discord/iMessage channels are a promising direction, but they're still in research preview - when we last tested them, the experience wasn't stable enough for production use. TeleClaude was built to fill that gap: your bot runs Claude Code as a subprocess on the server where it's deployed, with a plan/approve workflow and self-restart baked in. No local machine needed, no --channels flag, no Bun dependency - just pip install and go. If Channels matures into a solid production path, we'd love to add it as an alternative backend - contributions welcome.
+----------------+ +-----------------+ +--------------------+
| Telegram | msg | TeleClaude | stdin | Claude Code |
| (phone) |------->| (Python bot) |------->| (subprocess) |
| |<-------| |<-------| |
| | reply | plan/approve | stdout | reads/edits |
| | | workflow | | your codebase |
+----------------+ +-----------------+ +--------------------+
- You send a message in Telegram
- TeleClaude routes it to Claude Code in read-only plan mode
- Claude analyzes your codebase and proposes a plan
- You
/approveor/rejectfrom Telegram - On approve, Claude executes with file-edit permissions
/restartreloads the bot to pick up code changes- The application is now running its improved version of itself
- Python >= 3.10
- Claude Code CLI installed and authenticated
- A Telegram bot token from @BotFather
pip install teleclaudeimport os, time
from dotenv import load_dotenv
from teleclaude import ClaudeSession, TeleClaudeBot, kill_previous
load_dotenv()
kill_previous("bot.pid")
session = ClaudeSession(project_dir=".")
bot = TeleClaudeBot(
token=os.environ["TELEGRAM_BOT_TOKEN"],
chat_id=os.environ["TELEGRAM_CHAT_ID"],
claude_session=session,
)
bot.start_polling()
try:
while bot.running:
time.sleep(1)
except KeyboardInterrupt:
bot.stop_polling()That's it. Send any message in Telegram and Claude Code responds. Built-in commands:
| Command | What it does |
|---|---|
| Free text | Chat with Claude Code (read-only mode) |
| Voice msg | Transcribed via Whisper, then routed to Claude |
/claude |
Interactive menu: model switcher, session info, flush, approve/reject |
/approve |
Execute Claude's pending plan (allows file edits) |
/reject |
Discard the pending plan |
/session |
Session management (/session pin <id>, /session clear) |
/context |
Check Claude Code availability (rate-limit detection) |
/restart |
Restart the bot process (picks up code changes) |
/help |
Show all available commands |
Subclass TeleClaudeBot and override domain_commands() to register custom /commands:
import subprocess
from teleclaude import ClaudeSession, TeleClaudeBot, kill_previous
class MyBot(TeleClaudeBot):
def domain_commands(self):
return {
"/status": (self.cmd_status, "Show git status"),
}
def cmd_status(self):
result = subprocess.run(
["git", "log", "--oneline", "-5"],
capture_output=True, text=True, cwd=self._project_dir,
)
self.send(f"<pre>{result.stdout or 'No git history.'}</pre>")Other hooks you can override:
| Hook | Purpose |
|---|---|
on_domain_callback(data, message_id) |
Handle inline keyboard callbacks |
help_text() |
Customize /help output |
on_restart() |
Customize restart behavior |
plan_prompt_wrapper(text) |
Customize the prompt sent to Claude in plan mode |
Send a voice message in Telegram and it gets auto-transcribed via OpenAI Whisper, then routed to Claude. Install the optional dependency:
pip install openai-whisperThe Whisper model (base) is lazy-loaded on first voice message. If not installed, the bot replies with install instructions.
The /claude menu includes Flush & New Session, which:
- Asks Claude to write a
.handoff.mdsummary of the current session context - Clears the session pin
- On the next message, a new session bootstraps from
.handoff.mdautomatically
To enable automatic handoff bootstrap, pass bootstrap_file to ClaudeSession:
session = ClaudeSession(project_dir=".", bootstrap_file=".handoff.md")When Claude returns a rate-limit error, the bot automatically starts background polling (every 5 min, up to 12 h) and notifies you when Claude is back online. You can also manually check via /context or the /claude menu.
session = ClaudeSession(
project_dir=".", # repo root (resolved to absolute path)
model="opus", # "opus", "sonnet", or "haiku"
output_format="json", # "json" or "stream-json"
bootstrap_file=".handoff.md", # auto-inject on new sessions (None to disable)
auto_pin=True, # auto-save session ID on first use
session_name_prefix="mybot", # generates mybot-1, mybot-2, etc.
plan_max_turns=25, # max turns in plan (read-only) mode
edit_max_turns=25, # max turns in edit mode
plan_tools=["Read", "Grep"], # override default plan-mode tools
edit_tools=["Read", "Edit"], # override default edit-mode tools
on_session_fallback=callback, # called when pinned session expires
)git clone https://github.com/ofir5300/teleclaude.git
cd teleclaude
pip install -e .
cd example && cp .env.example .env # fill in your tokens
python main.pyContributions welcome! Open an issue or PR on GitHub.
MIT - Ofir Cohen

