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>
5.0 KiB
Phase 4: Command System Refactor
Status: ⬜ Not Started Parent plan: 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:- Find all CommandTrackerListener rows where listener_type="telegram_bot" AND listener_id=bot.id
- Load the associated CommandTracker for each (filter enabled=True)
- Load CommandConfig for each tracker
- 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
- Instead of reading
-
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 runningstop_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-commandsshould now:- Find all CommandTrackerListeners for this bot
- Collect all enabled commands across all linked CommandConfigs
- Merge command lists (union of enabled commands)
- Call setMyCommands with the merged list
- Use locale from the first CommandConfig (or a bot-level default)
-
Task 7: Update
services/__init__.pystartup 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 flowpackages/server/src/notify_bridge_server/commands/webhook.py— updated handlerpackages/server/src/notify_bridge_server/services/telegram_poller.py— ref-counted pollingpackages/server/src/notify_bridge_server/services/scheduler.py— startup logicpackages/server/src/notify_bridge_server/services/__init__.py— startup logicpackages/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)