1d445f3980
Rework entity schema: rename Tracker→NotificationTracker, add CommandConfig/ CommandTracker/CommandTrackerListener entities for decoupled command handling. Commands now resolve through CommandTracker→CommandConfig instead of TelegramBot.commands_config. Smart ref-counted bot polling based on active listeners. Add chat_action to telegram targets. Full frontend CRUD pages for command configs and command trackers. Idempotent SQLite migrations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
5.0 KiB
Markdown
88 lines
5.0 KiB
Markdown
# Phase 4: Command System Refactor
|
|
|
|
**Status:** ⬜ Not Started
|
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
|
**Domain:** backend
|
|
|
|
## Objective
|
|
Refactor the command handling system to resolve commands through CommandTracker → CommandConfig
|
|
instead of TelegramBot.commands_config. Implement smart ref-counted polling/webhook management
|
|
for TelegramBot when used as a commands listener. Handle multi-tracker routing (one bot serving
|
|
multiple command trackers for different providers).
|
|
|
|
## Tasks
|
|
|
|
- [ ] Task 1: Refactor `commands/handler.py` — `handle_command()`:
|
|
- Instead of reading `bot.commands_config`, resolve command config through CommandTrackerListeners:
|
|
1. Find all CommandTrackerListener rows where listener_type="telegram_bot" AND listener_id=bot.id
|
|
2. Load the associated CommandTracker for each (filter enabled=True)
|
|
3. Load CommandConfig for each tracker
|
|
4. Load ServiceProvider for each tracker
|
|
- For each incoming command, check which CommandConfig(s) have it enabled
|
|
- If multiple trackers enable the same command (e.g., two Immich providers with /latest), use the first match or let the user disambiguate (future enhancement — for now, use first enabled match)
|
|
- Pass the resolved provider config to command execution functions
|
|
|
|
- [ ] Task 2: Update `_get_bot_context()` in handler.py:
|
|
- Currently finds trackers/providers by matching bot_token in notification target configs
|
|
- New approach: resolve through CommandTracker → provider_id → ServiceProvider
|
|
- Return a list of (command_tracker, command_config, provider) tuples
|
|
|
|
- [ ] Task 3: Implement smart ref-counted polling/webhook in `services/telegram_poller.py`:
|
|
- Track active listener count per bot: when a CommandTrackerListener is added for a bot, increment ref count; when removed, decrement
|
|
- `start_bot_if_needed(bot_id)` — start polling/webhook only if not already running
|
|
- `stop_bot_if_unused(bot_id)` — stop polling/webhook only if ref count reaches 0
|
|
- Export these functions for use by the command_trackers API (when adding/removing listeners)
|
|
|
|
- [ ] Task 4: Update `commands/webhook.py`:
|
|
- Webhook handler already receives messages for a specific bot (by webhook_path_id)
|
|
- Update to use the new command resolution flow from Task 1
|
|
- Ensure chat auto-discovery still works
|
|
|
|
- [ ] Task 5: Update `services/scheduler.py`:
|
|
- On startup, instead of starting polling for all bots with update_mode="polling", start polling only for bots that have active CommandTrackerListeners
|
|
- Use ref-counting logic from Task 3
|
|
|
|
- [ ] Task 6: Update telegram bot sync-commands endpoint:
|
|
- `POST /api/telegram-bots/{id}/sync-commands` should now:
|
|
1. Find all CommandTrackerListeners for this bot
|
|
2. Collect all enabled commands across all linked CommandConfigs
|
|
3. Merge command lists (union of enabled commands)
|
|
4. Call setMyCommands with the merged list
|
|
5. Use locale from the first CommandConfig (or a bot-level default)
|
|
|
|
- [ ] Task 7: Update `services/__init__.py` startup logic:
|
|
- On startup, enumerate all enabled CommandTrackers with listeners
|
|
- For each unique bot referenced, call `start_bot_if_needed(bot_id)`
|
|
|
|
## Files to Modify/Create
|
|
- `packages/server/src/notify_bridge_server/commands/handler.py` — new command resolution flow
|
|
- `packages/server/src/notify_bridge_server/commands/webhook.py` — updated handler
|
|
- `packages/server/src/notify_bridge_server/services/telegram_poller.py` — ref-counted polling
|
|
- `packages/server/src/notify_bridge_server/services/scheduler.py` — startup logic
|
|
- `packages/server/src/notify_bridge_server/services/__init__.py` — startup logic
|
|
- `packages/server/src/notify_bridge_server/api/telegram_bots.py` — sync-commands update
|
|
|
|
## Acceptance Criteria
|
|
- Commands resolve through CommandTracker → CommandConfig instead of TelegramBot.commands_config
|
|
- Bot polling/webhook starts only when at least one CommandTrackerListener references the bot
|
|
- Bot polling/webhook stops when last listener is removed
|
|
- Multiple command trackers can share the same bot — commands are merged
|
|
- Telegram bot sync-commands syncs the merged command set
|
|
- Existing command functionality (search, latest, random, etc.) still works end-to-end
|
|
|
|
## Notes
|
|
- Rate limiting can stay in-memory per (bot_id, chat_id, category) — no schema change needed.
|
|
- The handler currently uses `_get_bot_context()` to find providers via notification targets. The new flow resolves providers via CommandTracker.provider_id — this is cleaner and decouples commands from notification targets.
|
|
- Edge case: a bot with no CommandTrackerListeners should not poll/webhook. If a user deletes all command trackers referencing a bot, polling should stop.
|
|
- Edge case: a command tracker can be disabled (enabled=False) — disabled trackers don't count for ref-counting.
|
|
|
|
## Review Checklist
|
|
- [ ] All tasks completed
|
|
- [ ] Code follows project conventions
|
|
- [ ] No unintended side effects
|
|
- [ ] Build passes
|
|
- [ ] Tests pass (new + existing)
|
|
|
|
## Handoff to Next Phase
|
|
<!-- Filled in by the implementation agent after completing this phase. -->
|