---
phase: 38-telegram-bridge
plan: "01"
subsystem: api
tags: [telegram, grammy, long-polling, bot, chat, relay]

requires:
  - phase: 36-voice-pipeline
    provides: nexus-settings.ts with telegramToken schema field

provides:
  - grammY bot lifecycle (start/stop/isRunning) via telegramService factory
  - Text message relay: Telegram user message -> agent reply with [AgentName] prefix
  - In-memory session map chatId:agentId -> conversationId
  - POST /api/telegram/token endpoint (validate token via getMe, save, restart bot)
  - GET /api/telegram/status endpoint
  - Conditional auto-start of bot in app.ts when telegramToken is configured

affects:
  - 38-02 (voice pipeline integration uses same telegramService)
  - 38-03 (onboarding step that calls POST /telegram/token)

tech-stack:
  added: [grammy@^2]
  patterns:
    - telegramService(db) factory returns { start, stop, isRunning } — stateful module-level bot ref
    - Route receives service instance as second param: telegramRoutes(db, svc)
    - deleteWebhook() called before bot.start() to clear any stale webhook registration
    - bot.start() not awaited (never-resolving long-poll promise)

key-files:
  created:
    - server/src/services/telegram.ts
    - server/src/routes/telegram.ts
  modified:
    - server/src/app.ts
    - server/package.json
    - pnpm-lock.yaml

key-decisions:
  - "telegramRoutes accepts service instance as second param (not module singleton) — enables testability and restart from token route"
  - "bot variable assigned before handlers registered — allows bot.on() calls to reference the outer ref cleanly"
  - "nexusSettingsService re-exported from telegram.ts removed — routes import directly from nexus-settings.ts to avoid circular potential"

patterns-established:
  - "Service factory receives db, returns lifecycle object { start, stop, isRunning } — matches voicePipelineService pattern"
  - "Long-polling: deleteWebhook first, then bot.start() fire-and-forget with catch logger"

requirements-completed: [TGRAM-01, TGRAM-02, TGRAM-05, TGRAM-06]

duration: 15min
completed: 2026-04-04
---

# Phase 38 Plan 01: Telegram Bridge Core Summary

**grammY long-polling bot with text relay, [AgentName] prefix, session map, and /api/telegram/token + /status management routes wired into app.ts**

## Performance

- **Duration:** ~15 min
- **Started:** 2026-04-04T02:59:18Z
- **Completed:** 2026-04-04T03:14:18Z
- **Tasks:** 2 of 2
- **Files modified:** 5

## Accomplishments
- Installed grammY v2 and created `telegramService(db)` factory with full text relay handler
- Agent reply prefixed `[AgentName]:` per TGRAM-02; session map tracks chatId:agentId -> conversationId
- `telegramRoutes` provides POST /telegram/token (getMe validation) and GET /telegram/status
- `app.ts` auto-starts bot on server boot when `telegramToken` is present in nexus settings

## Task Commits

1. **Task 1: Install grammY, create telegramService + telegramRoutes** - `8dbc7674` (feat)
2. **Task 2: Wire telegram service and routes into app.ts** - `3973fa08` (feat)

## Files Created/Modified
- `server/src/services/telegram.ts` (187 lines) — grammY bot lifecycle, text relay, session map, agent prefix
- `server/src/routes/telegram.ts` — POST /telegram/token, GET /telegram/status
- `server/src/app.ts` — imports, route mount, conditional startup
- `server/package.json` — grammy dependency added
- `pnpm-lock.yaml` — lockfile updated

## Decisions Made
- Route receives service instance as second param (`telegramRoutes(db, svc)`) rather than importing a singleton — enables the token route to call `svc.stop()` / `svc.start()` cleanly
- `bot` variable assigned at top of `start()` before handler registration so `bot.on()` uses the outer mutable ref
- `nexusSettingsService` import kept in `app.ts` directly (not re-exported from telegram.ts) to avoid any potential circular dependency

## Deviations from Plan

None - plan executed exactly as written.

## Issues Encountered
- Initial implementation used `const newBot = new Bot(token)` then `newBot.on(...)`, which failed the `bot.on.*message:text` acceptance check. Refactored to assign `bot = new Bot(token)` directly so the outer ref is set before handler registration. TypeScript compiled cleanly throughout.

## Known Stubs

None — text relay is fully wired to chatService and puterProxyService.

## User Setup Required

None - no external service configuration required beyond providing a Telegram bot token via POST /api/telegram/token.

## Next Phase Readiness
- telegramService is ready to accept voice handlers in Plan 02 (voice pipeline integration)
- Plan 03 onboarding step already exists and calls POST /telegram/token

---
*Phase: 38-telegram-bridge*
*Completed: 2026-04-04*
