Telegram 3-Bot Fleet: Build Plan¶
Created: 2026-04-23 Owner: Claude Code (this session) Context: Justin asked for dedicated bots for UPDATES (push-driven roundups + 2-way replies) and INBOX (voice/text quick capture with smart routing), alongside the existing @GregTwoPointO chat bot. This plan supersedes notify.sh for user-facing alerts.
Target State¶
| Bot | Role | Context Model | Cost Profile |
|---|---|---|---|
| @GregTwoPointO_Bot | Conversation, architecture, deep work | INFINITE (persistent session, auto-compact) | Opus/Sonnet on demand |
| @ForgeUpdates_Bot (NEW) | Morning roundup + scheduled pushes + reactive replies | WINDOWED (24h rolling via session restart) | Haiku composer + Sonnet for replies |
| @ForgeInbox_Bot (NEW) | Voice/text capture → Whisper → intent route → execute | EPHEMERAL (fresh Haiku per message) | Near-zero per message |
What Pushes to @ForgeUpdates_Bot¶
Morning Roundup (07:30 daily)¶
Single composed summary containing:
- 📧 Pressing emails, business accounts only (Nova, JWVR, Gus Outdoor, Wiebelhaus, Sip-N-Serve). Top 3 w/ sender + one-line summary. NEVER personal Gmail (hard security rule).
- 📰 News roundup, 3 bullets curated for Justin's interests (Shopify/e-com, AI, Austin tech, outdoor/overland)
- 🎯 Yesterday's wins, parsed from memory/daily/YYYY-MM-DD.md checkpoints
- 😴 Sleep, score, HRV, stages from Eight Sleep Context API
- 💪 Wellness: HRV trend, readiness, stress from Garmin Context API
- 📅 Today's schedule, next 12h from Google Calendar
- 🏠 Home state, presence, temp, anomalies from HA Context API
- 🔧 Fleet health, one-line status (green/yellow/red counts)
Event-Driven Pushes¶
- Sleep score on wakeup (Eight Sleep poller threshold trigger)
- Wellness alerts (HRV crash, high stress)
- Security events (Frigate object detections at unusual hours, camera offline)
- Fleet health critical (disk >90%, service down, failed workers)
- Scheduled nudges from inbox bot ("ping me at 6pm about X")
Evening Recap (22:00 daily)¶
- Today's accomplishments (synthesized from day's checkpoints)
- Tomorrow's schedule preview
- Wellness summary
What @ForgeInbox_Bot Captures¶
Text or voice note → Whisper transcribes → intent router dispatches:
| Intent | Example utterance | Action |
|---|---|---|
| TODO | "remind me to sign up for Austin marathon" | TickTick API create |
| CALENDAR | "haircut next Friday at 2pm" | Google Calendar event |
| NOTE | "save idea: rebuild JWVR landing with video hero" | Append to memory/general/ideas.md |
| NUDGE | "ping me at 6pm about calling mom" | Scheduled Telegram push via cron + at |
| SHOPPING | "add oat milk to grocery list" | TickTick shopping list |
| QUESTION | "how's Plex doing" | Spawn chat-style reply (reroute to @GregTwoPointO context) |
Reply with ✅ + one-line confirmation so Justin knows it landed.
Build Phases¶
PHASE 1: Retire notify.sh + build Updates Bot foundation (45 min)¶
1.1 New bots via BotFather
- Justin creates @yourname_updates_bot and @yourname_inbox_bot
- Pastes tokens → I save to ~/.forge-secrets/telegram-updates.env + ~/.forge-secrets/telegram-inbox.env
1.2 telegram-push helper (replaces notify.sh for user-facing pushes)
- scripts/integrations/telegram/push.sh <bot> <priority> <title> <body>
- bot ∈ {chat, updates, inbox}
- Direct Telegram sendMessage, no Claude involvement
- Retire notify.sh (or leave dormant, no dependencies break)
1.3 Updates bot plugin attach
- Separate TELEGRAM_STATE_DIR=~/.claude/channels/telegram-updates/
- Launch a dedicated Claude Code session (Sonnet) bound to Updates bot via --channels plugin:telegram@claude-plugins-official
- Systemd unit forge-claude-updates.service to keep it running
- Session restarts nightly via systemd timer → gives the "24h rolling context" effect
PHASE 2: Morning Roundup Worker (1 hr)¶
2.1 Build scripts/integrations/roundup/morning.sh
- Haiku claude -p with structured prompt
- Ingests from:
- curl http://127.0.0.1:7358/context?about=all&window=24h (wellness + home)
- Grep last checkpoint from memory/daily/$(date -d yesterday +%F).md
- Google Calendar API (need to wire OAuth, see 2.2)
- Gmail API (business accounts only, see 2.3)
- News via scripts/news/fetch.sh (new, simple RSS aggregator)
- Composes markdown summary
- Calls push.sh updates info "Morning Roundup" "$summary"
2.2 Google Calendar wiring
- Create Google Workspace OAuth app (business workspace, NOT personal)
- Store token at ~/.forge-secrets/google-calendar.env
- Helper scripts/integrations/gcal/list.py --window today
2.3 Gmail business wiring
- Per business account (Nova, JWVR, etc.), separate OAuth per account
- Tokens at ~/.forge-secrets/gmail-{brand}.env
- Helper scripts/integrations/gmail/pressing.py --account nova --limit 3
- Hard guard: script aborts if account domain is @gmail.com for justinwieb personal
2.4 News aggregator
- scripts/news/fetch.sh: RSS from ~5 curated feeds (TechCrunch, Shopify blog, Austin Business Journal, outdoor/overland, AI news)
- Haiku pass: pick top 3 most relevant to Justin's active projects
2.5 Cron trigger
- 30 7 * * * /home/justinwieb/forge/scripts/integrations/roundup/morning.sh
PHASE 3: Inbox Bot (1.5 hr)¶
3.1 Standalone bot service
- New: scripts/integrations/telegram/inbox_bot.py
- Own systemd unit forge-telegram-inbox.service
- Long-poll, single authorized user, auto-pair first /start
3.2 Voice pipeline
- Telegram voice note → getFile download → mp3/ogg
- Whisper transcription. Two options:
- Local: faster-whisper via uv venv, CPU-only (slower but private, free)
- Cloud: OpenAI Whisper API (fast, ~$0.006/min, not on Max plan)
- Recommend: local for privacy + free
3.3 Intent router
- claude -p --model haiku with JSON-output prompt
- Input: transcribed text
- Output: {intent, payload, confidence}
- Handler dispatches per intent table above
3.4 Action handlers
- TickTick: OAuth app + scripts/integrations/ticktick/create.py
- Google Calendar: reuse 2.2 helper
- Notes: append to memory/general/ideas.md
- Nudges: write file to ~/scheduled-nudges/, cron watches + fires via push.sh
PHASE 4: Plumbing + polish (30 min)¶
4.1 Event-driven pushes wired - Eight Sleep poller on score change → push.sh updates - Wellness threshold monitor → push.sh updates - Frigate unusual-hours detection (new monitor) → push.sh updates - fleet-status monitor critical → push.sh updates
4.2 MEMORY.md updates - Register all new scripts + services - Update Telegram Fleet Bot reference with 3-bot topology
4.3 Tests - Fire test morning roundup manually - Record a voice note to inbox bot, watch TickTick entry appear - Verify chat bot unchanged
Security Checklist¶
- Personal Gmail NEVER touched (hard-coded abort if account email matches)
- All tokens in
~/.forge-secrets/(outside repo, chmod 600) - Each bot has its own allowlist, only Justin's Telegram user ID
- Voice notes deleted after transcription (no audio retention)
- OAuth scopes minimized (Calendar: read events only; Gmail: read metadata + subject only for roundup, NOT full message body)
- Intent router has allowlist of intents, unknown intents refused, not executed
Open Questions for Justin¶
- Which bots, preferred names?
@j_updates_bot,@jw_inbox_bot, etc.? - Which business email accounts should roundup cover? (Nova, JWVR, Gus Outdoor, Wiebelhaus, Sip-N-Serve, pick)
- TickTick, does he already have account? Needs premium for API access.
- Local Whisper vs OpenAI API for voice? (Recommend local.)
- News feeds, wants me to pick defaults, or give a list?
- Morning roundup time, 07:30 default, or different? (Garmin/Eight Sleep poller already runs 07:30.)
- Evening recap, opt in? (Might be noise.)
Estimated Timeline¶
- Phase 1: 45 min (blocked on token creation, 2 min from Justin)
- Phase 2: 1 hr (blocked on Google OAuth if not already set up)
- Phase 3: 1.5 hr (blocked on TickTick API key)
- Phase 4: 30 min
Total work: ~4 hours active spread across sessions, but Phase 1+2 can land today if tokens arrive. Phase 3 (inbox) works standalone and can be deferred.
Rollback¶
If anything goes wrong:
- All bots are independent, killing one doesn't affect others
- systemctl stop forge-claude-updates or forge-telegram-inbox to disable
- crontab -e to remove morning roundup trigger
- notify.sh stays in tree as fallback (not removed, just retired)