feat(commands): per-chat album scope derived from notification routing

The "per-chat album scope" feature stored on CommandTrackerListener was
really per-bot: listener_id = bot.id, and every chat that bot served
shared the same scope.  Commands like /albums, /random, /status,
/events leaked the full provider catalog into chats that were never
wired up to receive notifications from those trackers.

New model: the album scope for /commands in a given chat is derived
from the notification-routing graph.  For a (provider, bot, chat_id)
triple we walk TargetReceiver (chat_id match, enabled) →
NotificationTarget (telegram or broadcast parent) →
NotificationTrackerTarget → NotificationTracker (provider match) and
union their collection_ids.  That's the natural "what does this chat
get notifications about" set, and it becomes the command scope.

- New helper: command_utils.resolve_chat_album_scope(provider_id,
  bot_id, chat_id) -> set[str].  Empty set is the default for chats
  with no routing — commands return nothing rather than leaking the
  provider's catalog.
- Dispatcher computes the scope per (tracker, bot, chat) and threads
  it through handler.handle(..., allowed_album_ids=...).  Explicit
  CommandTrackerListener.allowed_album_ids override, when set, still
  wins verbatim (kept as an escape hatch for users who want a divergent
  scope for a whole bot).
- /status, /albums, /events, and all /_cmd_immich-routed commands
  (/random, /search, /find, /latest, /memory, /summary, /favorites,
  /place, /person) now intersect with the resolved scope.
- UI scope modal relabeled: it's an explicit *override for this bot*,
  not a per-chat setting.  Default is "derive from notification
  routing", which matches what users already configured elsewhere.

Also:
- /search, /find, /person, /place — _enrich_assets return value was
  discarded, dropping public_url enrichment.  Assign the return value.
- search_smart / search_metadata — consolidated into _search_items
  helper that logs non-200 responses and transport errors instead of
  silently returning [].  Makes "always no results" bugs actually
  diagnosable.  Also accepts the alternate {"assets": [...]} flat-list
  shape from older Immich versions.
- Immich search error bodies go through _redact_body so credentials
  echoed by authenticating proxies don't land in server logs.
This commit is contained in:
2026-04-22 03:20:51 +03:00
parent 4ff3876e49
commit 3b76a09759
13 changed files with 242 additions and 58 deletions
+4 -4
View File
@@ -802,11 +802,11 @@
"selectBot": "Select bot...",
"listenerType": "telegram_bot",
"editScope": "Edit album scope",
"scopeAll": "all albums",
"scopeAll": "derived from notification routing",
"albumsShort": "albums",
"scopeTitle": "Album Scope for This Chat",
"scopeDescription": "Restrict which tracked albums this chat can query via commands. Leave on \"inherit\" to allow all albums from the tracker.",
"scopeInherit": "Inherit: allow all tracked albums",
"scopeTitle": "Album Scope Override for This Bot",
"scopeDescription": "By default this bot's commands see only the albums that actually deliver notifications to the chats it speaks to (computed from your notification trackers). Set an explicit override here to widen or narrow that set for every chat this bot serves.",
"scopeInherit": "Inherit: derive from notification routing",
"noCollections": "No albums available."
},
"snackbar": {
+4 -4
View File
@@ -802,11 +802,11 @@
"selectBot": "Выберите бота...",
"listenerType": "telegram_bot",
"editScope": "Изменить область альбомов",
"scopeAll": "все альбомы",
"scopeAll": "из маршрутизации уведомлений",
"albumsShort": "альбомов",
"scopeTitle": "Область альбомов для этого чата",
"scopeDescription": "Ограничить, какие отслеживаемые альбомы доступны в этом чате через команды. Оставьте \"наследовать\", чтобы разрешить все альбомы трекера.",
"scopeInherit": "Наследовать: разрешить все отслеживаемые альбомы",
"scopeTitle": "Переопределение области альбомов для этого бота",
"scopeDescription": "По умолчанию команды этого бота видят только альбомы, уведомления которых приходят в его чаты (вычисляется из ваших трекеров уведомлений). Задайте явный список здесь, чтобы расширить или сузить этот набор для всех чатов данного бота.",
"scopeInherit": "Наследовать: вычислить из маршрутизации уведомлений",
"noCollections": "Нет доступных альбомов."
},
"snackbar": {