Files
Timmy-time-dashboard/src/dashboard/routes/telegram.py
Claude bb93697b92 feat: add Telegram bot integration
Bridges Telegram messages to Timmy via python-telegram-bot (optional
dependency). The bot token can be supplied through the TELEGRAM_TOKEN
env var or at runtime via the new POST /telegram/setup dashboard
endpoint, which (re)starts the bot without a restart.

Changes:
- src/telegram_bot/bot.py — TelegramBot singleton: token persistence
  (telegram_state.json), lifecycle (start/stop), /start command and
  message handler that forwards to Timmy
- src/dashboard/routes/telegram.py — /telegram/setup and /telegram/status
  FastAPI routes
- src/dashboard/app.py — register telegram router; auto-start/stop bot
  in lifespan hook
- src/config.py — TELEGRAM_TOKEN setting (pydantic-settings)
- pyproject.toml — [telegram] optional extra (python-telegram-bot>=21),
  telegram_bot wheel include
- .env.example — TELEGRAM_TOKEN section
- .gitignore — exclude telegram_state.json (contains token)
- tests/conftest.py — stub telegram/telegram.ext for offline test runs
- tests/test_telegram_bot.py — 16 tests covering token helpers,
  lifecycle, and all dashboard routes (370 total, all passing)

https://claude.ai/code/session_01CNBm3ZLobtx3Z1YogHq8ZS
2026-02-22 17:16:12 +00:00

52 lines
1.3 KiB
Python

"""Dashboard routes for Telegram bot setup and status."""
from fastapi import APIRouter
from pydantic import BaseModel
router = APIRouter(prefix="/telegram", tags=["telegram"])
class TokenPayload(BaseModel):
token: str
@router.post("/setup")
async def setup_telegram(payload: TokenPayload):
"""Accept a Telegram bot token, save it, and (re)start the bot.
Send a POST with JSON body: {"token": "<your-bot-token>"}
Get the token from @BotFather on Telegram.
"""
from telegram_bot.bot import telegram_bot
token = payload.token.strip()
if not token:
return {"ok": False, "error": "Token cannot be empty."}
telegram_bot.save_token(token)
if telegram_bot.is_running:
await telegram_bot.stop()
success = await telegram_bot.start(token=token)
if success:
return {"ok": True, "message": "Telegram bot started successfully."}
return {
"ok": False,
"error": (
"Failed to start bot. Check the token is correct and that "
'python-telegram-bot is installed: pip install ".[telegram]"'
),
}
@router.get("/status")
async def telegram_status():
"""Return the current state of the Telegram bot."""
from telegram_bot.bot import telegram_bot
return {
"running": telegram_bot.is_running,
"token_set": telegram_bot.token_set,
}