• v0.3.0 155d25edf9

    Notify Bridge 0.3.0
    Release / release (push) Successful in 1m19s
    Stable

    alexei.dolgolyov released this 2026-04-22 18:59:15 +03:00 | 85 commits to master since this release

    v0.3.0 (2026-04-22)

    Major polling perf overhaul for large Immich libraries plus a UX fix for
    slow bot commands. Combined impact on idle albums: per-tick cost drops
    from ~150 MB fetched to a few hundred bytes; active albums now fetch
    O(changes) instead of O(library). Tested against a ~200k-asset library.

    Schema change: adds a meta_fingerprint JSON column to
    notification_tracker_state — applied automatically by the startup
    migration, no manual step required.

    Performance

    • Skip full album fetch on idle ticks — new ImmichAlbumMeta + get_album_meta() probe using ?withoutAssets=true as a cheap change-detection fingerprint. When the fingerprint matches and no pending assets are outstanding, poll() short-circuits and does no asset fetch at all. (fe38d20)
    • Delta-fetch active albums — when the fingerprint changes, poll with updatedAfter instead of refetching the whole album; falls back to a full fetch only on count decrease or mixed add+remove that delta can't reconcile. (fe38d20)
    • Parallel meta probesasyncio.gather over album meta probes so a 20-album tracker pays one round-trip of latency instead of 20. (fe38d20)
    • Tick-scoped shared-links cache — new get_all_shared_links_by_album() coalesces to one /api/shared-links request per tick instead of one per changed album. (fe38d20)
    • Module-level users cache — 1 h TTL, sha256-keyed, shared across providers that target the same Immich server. (fe38d20)
    • Skip asset_ids DB rewrite on idle ticks — watcher no longer rewrites the (potentially ~8 MB for huge albums) JSON column when the fingerprint didn't change. (fe38d20)
    • Adaptive polling — after 10 empty ticks the scheduler skips 1-in-2, after 30 empty ticks skips 1-in-4; resets on the first detected change or any schedule edit. (fe38d20)
    • APScheduler jitterinterval/4, capped at 30 s, to smooth thundering-herd bursts when many trackers share the same scan_interval. (fe38d20)
    • Event payload cap — 50 added / 200 removed assets per event so a bulk import can't explode a Jinja template or exceed Telegram message limits. (fe38d20)

    Features

    • Chat-action hint stays alive during slow command fetches — Telegram chat actions expire after ~5 s, so slow bot commands (/latest, /random, /favorites, /memory, /search, /find, /person, /place, /summary) previously showed a hint that vanished long before the media arrived and users saw nothing happening. New telegram_chat_action async context manager starts a keep-alive task that re-posts the action every 4 s until it exits; classify_command_chat_action maps each command to the right action (upload_photo for media-returning commands, typing for /summary, none for fast DB-only commands like /status / /events). Wired into both the webhook and long-poll paths. (69711bb)

    All Commits
    • 69711bb — feat(commands): keep chat-action hint alive during slow command fetches (alexei.dolgolyov)
    • fe38d20 — perf(immich): skip full album fetch on idle ticks; delta-fetch for active ones (alexei.dolgolyov)

    Changelog

    155d25e chore: release v0.3.0
    69711bb feat(commands): keep chat-action hint alive during slow command fetches
    fe38d20 perf(immich): skip full album fetch on idle ticks; delta-fetch for active ones

    Downloads
  • v0.2.8 d02616069d

    Notify Bridge 0.2.8
    Release / release (push) Successful in 1m58s
    Stable

    alexei.dolgolyov released this 2026-04-22 18:02:09 +03:00 | 88 commits to master since this release

    v0.2.8 (2026-04-22)

    Follow-up fix to v0.2.7's Telegram-send unification. The shared factory was
    in place, but commands and notifications were still writing into different
    cache-key namespaces — so in practice the caches never actually shared entries.

    Bug Fixes

    • Commands and notifications now share one file_id cache namespacecommon._format_assets was passing cache_key=<bare asset UUID>, while the notification dispatcher writes keys as <host>:<uuid> (derived from the URL by extract_asset_id_from_url). The two paths populated different keys for the same asset, so neither could hit the other's cached file_id and the Settings → cache-stats card only ever reflected the notification side. Dropped the explicit cache_keyTelegramClient now derives <host>:<uuid> from the URL on both paths, so one file_id cached by any dispatch or /random / /latest reply is reused by every later send. (7dae68f)

    All Commits
    • 7dae68f — fix(commands): match notification cache-key format so writes share one namespace (alexei.dolgolyov)

    Changelog

    d026160 chore: release v0.2.8
    7dae68f fix(commands): match notification cache-key format so writes share one namespace

    Downloads
  • v0.2.7 e6481605ca

    Notify Bridge 0.2.7
    Release / release (push) Successful in 4m23s
    Stable

    alexei.dolgolyov released this 2026-04-22 16:47:25 +03:00 | 90 commits to master since this release

    v0.2.7 (2026-04-22)

    Follow-up to v0.2.6: unifies the Telegram send routine across notifications
    and bot commands so both sides share the same aiohttp session, the same
    file_id caches, and the same rules for video / thumbnail URL construction.
    Eliminates repeat uploads that happened because single-asset sends and media
    groups were keyed in different caches.

    Bug Fixes

    • Single-asset sends now hit the asset cacheTelegramClient._get_cache_and_key treats cache_key values that look like asset UUIDs as asset-cache entries. Single-asset sends were storing file_ids in url_cache while the media-group path stored them in asset_cache, so repeat sends of the same asset never hit the cache and re-uploaded. (6de9a12)
    • Notifications and commands now share the Telegram client factory — new services/telegram_send.py is the single construction path for TelegramClient: always wires the shared aiohttp session and both file_id caches. send_reply and send_media_group in commands/handler.py now delegate to the factory instead of constructing their own uncached clients, so commands reuse file_ids populated by notification dispatches (and vice versa) instead of re-uploading the same bytes. (6de9a12)
    • Single rule for /video/playback vs thumbnail URLs — extracted build_asset_media_urls so the notification dispatcher's asset_to_media and the bot command handlers' common._format_assets agree on when to use the playback URL and when to use the thumbnail. Removes a subtle drift that could show stills in one path and video in the other for the same asset. (6de9a12)

    All Commits
    • 6de9a12 — fix(telegram): unify send routine across notifications and commands (alexei.dolgolyov)

    Changelog

    e648160 chore: release v0.2.7
    6de9a12 fix(telegram): unify send routine across notifications and commands

    Downloads
  • v0.2.6 325eabd751

    Notify Bridge 0.2.6
    Release / release (push) Successful in 1m19s
    Stable

    alexei.dolgolyov released this 2026-04-22 16:29:24 +03:00 | 92 commits to master since this release

    v0.2.6 (2026-04-22)

    Bug-fix release. Notably: saving settings was silently overwriting the
    Telegram webhook secret with its own display mask, invalidating HMAC on
    every webhook-mode bot after any settings save. Also fixes template-editor
    variable discovery for provider-specific command slots (/search, /status,
    /repos, /issues, /boards), asset enrichment (city / country / favorite)
    for Immich /search / /find / /person / /place, and video rendering
    in command media groups.

    Bug Fixes

    • Don't clobber the Telegram webhook secret with its mask on saveGET /settings returns the secret masked as ***<last4>; the frontend bound that masked value into state and shipped it back on any Save, so the PUT handler persisted the mask as the new secret. The next GET re-masked the mask to itself, so the UI showed no corruption while HMAC verification silently broke for every webhook-mode bot. Incoming values that begin with *** are now treated as unchanged; empty strings still clear the secret explicitly. Operators running webhook-mode bots should save the page once with a known-good secret after upgrading. (8531168)
    • Surface Variables button / autocomplete for provider-specific command slots — the command-template-configs UI only resolved slot variables against the shared catalog, so Immich's /search and /status, Gitea's /repos / /issues, and Planka's /boards offered no autocomplete. It now resolves against the active provider (varsRef[provider_type][slot]) first, falling back to shared entries. (fab6169)
    • Enrich raw Immich search results through build_asset_dict/search, /find, /person, /place previously handed raw API rows to templates, so city / country (from exifInfo) and is_favorite (mapped from isFavorite) were missing and templates couldn't render location or favorite indicators. Now normalised the same way as notification events. (fab6169)
    • Videos render correctly in command media groups/latest, /random, /favorites were sending videos as still thumbnails because the media-group path duplicated asset-typing logic. Extracted build_telegram_asset_entry into a shared helper so the notification dispatcher and command groups agree on video typing and /video/playback URLs. (fab6169)
    • Command media groups reuse the Telegram file_id cachesend_media_group was re-uploading assets on every repeat command instead of honoring the cache the notification dispatcher already populates. Now shares the cache, avoiding re-upload churn. (fab6169)

    All Commits
    • fab6169 — fix(commands): enrich search assets, surface variables for all command slots (alexei.dolgolyov)
    • 8531168 — fix(settings): don't clobber webhook secret with its mask on save (alexei.dolgolyov)

    Changelog

    325eabd chore: release v0.2.6
    fab6169 fix(commands): enrich search assets, surface variables for all command slots
    8531168 fix(settings): don't clobber webhook secret with its mask on save

    Downloads
  • v0.2.5 d7daadadc2

    Notify Bridge 0.2.5
    Release / release (push) Successful in 1m9s
    Stable

    alexei.dolgolyov released this 2026-04-22 15:56:20 +03:00 | 95 commits to master since this release

    v0.2.5 (2026-04-22)

    Hotfix release on top of v0.2.4 — the settings page couldn't save numeric
    fields after the cache-TTL / max-entries rework. See v0.2.4 notes for the
    main changes (thumbhash-validated cache, settings UX overhaul, mobile-nav
    parity).

    Bug Fixes

    • Accept numeric values in settings update payload — Svelte's bind:value on <input type="number"> coerces to a JS number, and Pydantic v2 wouldn't auto-coerce int → str, producing a 422 on every save that touched a numeric setting (TTL, max entries) after v0.2.4. Widened numeric fields to int | str | None in SettingsUpdate and normalized to str before persisting. (d7d0a5d)

    All Commits
    • d7d0a5d — fix(settings): accept numeric values in update payload (alexei.dolgolyov)

    Changelog

    d7daada chore: release v0.2.5
    e04ad16 docs: add hotfix note to v0.2.4 release notes
    d7d0a5d fix(settings): accept numeric values in update payload

    Downloads
  • v0.2.4 93df538819

    Notify Bridge 0.2.4
    Release / release (push) Successful in 1m18s
    Stable

    alexei.dolgolyov released this 2026-04-22 15:36:08 +03:00 | 98 commits to master since this release

    v0.2.4 (2026-04-22)

    Telegram media cache rebuilt around thumbhash validation — asset cache
    entries now invalidate when the visual content changes, not after a fixed
    TTL — plus a settings-page overhaul (cache stats, clear button, timezone /
    locale pickers) and full mobile-nav parity with the desktop sidebar.

    Features

    Telegram media cache

    • Thumbhash-validated asset cache — dispatcher builds an asset.id → thumbhash resolver from event.added_assets (Immich already populates thumbhash in extra) and passes it to TelegramClient. Asset-cache entries now invalidate on visual change rather than age. (2be608b)
    • Configurable cache cap & statsTelegramFileCache gets a max_entries LRU cap (applies in both TTL and thumbhash modes), ttl_seconds <= 0 disables TTL entirely, and a stats() method exposes per-bucket counts / sizes / oldest+newest timestamps. New settings: telegram_asset_cache_max_entries (default 5000); telegram_cache_ttl_hours default bumped 48 → 720 (30 days) and is now URL-only. (2be608b)
    • Cache admin endpointsGET /settings/telegram-cache/stats and POST /settings/telegram-cache/clear. PUT /settings now soft-resets the in-memory caches when cache-shaping keys change (on-disk file_ids preserved). (2be608b)

    Settings page

    • Cache stats card — per-bucket (URL / asset) counts, cumulative uploaded-to-Telegram byte size, oldest/newest timestamps, and a hint explaining what the size means. Clear-cache button behind a confirm modal. (2be608b)
    • New TimezoneSelector and LocaleSelector components replace the raw inputs with IANA-aware searchable pickers. Max-entries input exposed; TTL range widened to 0..8760 hours (0 = disabled). (2be608b)

    Mobile nav

    • Full sidebar parity in the More panel — now mirrors the desktop sidebar tree (groups + subnodes) so every destination is reachable from mobile. Previously the panel carried a hand-picked flat list that drifted behind newly-added routes. (2be608b)
    • Safe-area handling — nav height uses env(safe-area-inset-bottom); panel bottom + z-index fixed so page content can no longer visually overlay the bottom bar. (2be608b)

    Bug Fixes

    • update_settings TypeErrorany(await ... for ...) was an async generator (not an iterator) and raised at runtime; replaced with an explicit loop so settings updates actually commit. (2be608b)

    Accessibility

    • Password-manager association on the password-change form — hidden username field + autocomplete hints on all three password inputs so browsers stop warning and password managers fill correctly. (2be608b)
    • Telegram webhook secret wrapped in a no-op form with autocomplete=off to silence DOM/a11y warnings. (2be608b)

    All Commits
    • 2be608b — feat(cache): thumbhash-validated asset cache + settings UX overhaul (alexei.dolgolyov)

    Changelog

    93df538 chore: release v0.2.4
    2be608b feat(cache): thumbhash-validated asset cache + settings UX overhaul

    Downloads
  • v0.2.3 5028f15f4f

    Notify Bridge 0.2.3
    Release / release (push) Successful in 1m17s
    Stable

    alexei.dolgolyov released this 2026-04-22 03:30:45 +03:00 | 100 commits to master since this release

    v0.2.3 (2026-04-22)

    Bot-command scope hardening: commands now see only what their chat is wired to
    receive notifications about, closing a leak where a bot serving multiple chats
    exposed the whole provider catalog to every chat. Plus a handful of Immich
    command fixes (missing public_url enrichment, silently-swallowed search errors,
    always-on link previews).

    Features

    • Per-chat album scope derived from notification routing — for a (provider, bot, chat_id) triple, the allowed album set is now computed by walking TargetReceiver → NotificationTarget → NotificationTrackerTarget → NotificationTracker and unioning the collection IDs. /albums, /random, /search, /find, /latest, /memory, /summary, /favorites, /place, /person, /status, /events all intersect their results with the resolved scope. Chats with no notification routing for a tracker return nothing rather than leaking the provider's catalog. (3b76a09)
    • Scope modal relabeled — the per-listener allowed_album_ids UI is now explicitly an override for this bot (escape hatch when you want a divergent scope for a whole bot); the default is derive from notification routing, which matches what operators have already configured elsewhere. (3b76a09)
    • Drop tracker counts from /statustrackers_active / trackers_total were per-provider aggregates that would leak info about trackers a chat has no visibility into. Immich default /status templates (en, ru) now show only Albums + Last event; the template-editor variable catalog no longer suggests the removed vars for the Immich /status slot. Note: custom templates that reference {{ trackers_active }} / {{ trackers_total }} need to be updated. (5a232f1)

    Bug Fixes

    • /albums honors per-chat scope — previously ignored CommandTrackerListener.allowed_album_ids and listed every album tracked by the provider, so scoped chats saw neighbours' albums. Now applies the same intersect filter the /_cmd_immich media commands use. (4ff3876)
    • Disable Telegram link previews on command text replies — listings (/albums, /events, /people, …) embed multiple links and were rendering a preview for the first URL regardless of the operator's Disable link previews toggle. send_reply now always passes disable_web_page_preview=True. (4ff3876)
    • Restore public_url enrichment on /search, /find, /person, /place_enrich_assets's return value was being discarded, dropping the public URL populated on each asset. Now assigned properly. (3b76a09)
    • Surface Immich search errors instead of silently returning []search_smart / search_metadata consolidated into a _search_items helper that logs non-200 responses and transport errors, and accepts the alternate {"assets": [...]} flat-list shape from older Immich versions. "Always no results" bugs are now diagnosable. (3b76a09)
    • Redact Immich search error bodies before they land in server logs — credentials echoed by authenticating proxies no longer leak into logs. (3b76a09)

    All Commits
    • 5a232f1 — feat(commands): drop tracker counts from /status (alexei.dolgolyov)
    • 4ff3876 — fix(commands): /albums honors per-chat scope, disable link previews (alexei.dolgolyov)
    • 3b76a09 — feat(commands): per-chat album scope derived from notification routing (alexei.dolgolyov)

    Changelog

    5028f15 chore: release v0.2.3
    5a232f1 feat(commands): drop tracker counts from /status
    3b76a09 feat(commands): per-chat album scope derived from notification routing
    4ff3876 fix(commands): /albums honors per-chat scope, disable link previews

    Downloads
  • v0.2.2 83215473c7

    Notify Bridge 0.2.2
    Release / release (push) Successful in 1m0s
    Stable

    alexei.dolgolyov released this 2026-04-22 02:51:10 +03:00 | 104 commits to master since this release

    v0.2.2 (2026-04-22)

    Patch release — homelab usability fixes on top of v0.2.1. The SSRF hardening
    introduced in v0.2.1 blocks outbound requests to RFC1918 / link-local hosts,
    which breaks tracking of Immich / Gitea / etc. running on the same LAN.
    This release makes the workaround discoverable and enables it by default
    in the shipped docker-compose.yml.

    Bug Fixes

    • Default NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 in docker-compose.yml — the shipped compose is intended for homelab use. The flag is now hardcoded in the environment: block (not a ${...} substitution) so it works correctly with Portainer's per-stack env panel, which only does compose-file substitution and not runtime container env. Operators running on a public-facing host can drop the line. (4e23d2b)

    Documentation

    • Surface NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS hint in SSRF rejection errors — the UnsafeURLError raised by ImmichClient now tells operators how to allow LAN targets, instead of leaving them to dig through source. (58cba88)

    All Commits
    • 4e23d2b — chore(compose): hardcode NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 in compose (alexei.dolgolyov)
    • f7d51b2 — Revert "chore(compose): default NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 for homelab" (alexei.dolgolyov)
    • 3bb0585 — chore(compose): default NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 for homelab (alexei.dolgolyov)
    • 58cba88 — docs(immich-ssrf): surface NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS hint in error (alexei.dolgolyov)

    Changelog

    8321547 chore: release v0.2.2
    4e23d2b chore(compose): hardcode NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 in compose
    f7d51b2 Revert "chore(compose): default NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 for homelab"
    3bb0585 chore(compose): default NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 for homelab
    58cba88 docs(immich-ssrf): surface NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS hint in error

    Downloads
  • v0.2.1 645331d320

    Notify Bridge 0.2.1
    Release / release (push) Successful in 1m27s
    Stable

    alexei.dolgolyov released this 2026-04-22 02:35:38 +03:00 | 109 commits to master since this release

    v0.2.1 (2026-04-22)

    Security-focused release on top of v0.2.0. Hardens the restore/backup flow,
    CSRF/SSRF surfaces, JWT revocation on role change, and template-context
    leakage; adds a new per-tracking-config quiet hours feature with
    app-level IANA timezone support; plus a handful of performance fixes.

    Features

    • Per-tracking-config quiet hours with app-level IANA timezone — new Timezone app setting (defaults to UTC) and a Quiet Hours section on the Immich tracking-config form. HH:MM windows (including overnight, e.g. 22:00–07:00) are interpreted in the configured timezone and suppress all notifications for that tracker. (6c3dd67)

    Security

    • Signed & verified pending-restore bundles — SHA256 stored in AppSetting and checked on startup apply; files outside data_dir are refused and permissions tightened to 0600. (56993d2)
    • Same-origin check on POST /api/backup/apply-restart — Bearer-in-localStorage was CSRF-reachable from any XSS'd admin tab; require matching Origin/Referer. (56993d2)
    • JWT token_version bumps on demotion — role/username change and admin password reset now bump token_version so already-issued tokens lose admin. Last-admin TOCTOU guarded by COUNT + post-commit recheck that rolls back on race. (56993d2)
    • SSRF guard extended to ImmichClient.__init__ and the external_domain setter — admin-mutable URLs were bypassing the check that webhook / Slack / Discord paths already used. Dev scripts/restart-backend.sh now sets NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1 so homelab Immich instances still work. (56993d2)
    • Redact & cap Immich error bodies (~120 chars) before they flow into ActionExecution.error / EventLog.details (both UI-visible). (56993d2)
    • Deny-list sensitive keys (api_key, token, secret, password, authorization, cookie, …) in template-context merges so a rogue template cannot exfiltrate provider creds via {{ api_key }}. (56993d2)
    • Cap user-controlled Immich search paramsquery ≤ 256, person_ids ≤ 50, size ≤ 100 — so a Telegram listener cannot DoS upstream. (56993d2)
    • Stream upload reads with a running byte counter + Content-Length precheck instead of buffering the full body and then rejecting. (56993d2)
    • Log Telegram parse_mode fallbacks instead of swallowing silently — template escape bugs now surface in server logs. (56993d2)
    • Rollback partial imports on pending-restore failure (error recorded on a fresh session). (56993d2)

    Performance

    • Fix N+1 in _refresh_telegram_chat_titles — single IN query instead of session.get per chat. (56993d2)
    • Parallelize album & shared-link fetches in test_dispatch via asyncio.gather, and per-receiver Telegram test sends in the notifier with a semaphore of 5. (56993d2)
    • Early-exit collect_scheduled_assets(limit=0) so the periodic-summary test path skips the full per-album filter/sample (was O(album_assets)). (56993d2)
    • Explicit CREATE INDEX IF NOT EXISTS for event_log (user_id / action_id / provider_id) so the first boot after upgrade isn't left unindexed for the dashboard query. (56993d2)
    • Add AbortController timeout (120s) to fetchAuth so uploads/downloads don't hang indefinitely. (56993d2)

    All Commits
    • 6c3dd67 — feat(tracking): per-config quiet hours with app-level IANA timezone (alexei.dolgolyov)
    • 56993d2 — fix(security,perf): harden restore, CSRF, token_version + perf pass (alexei.dolgolyov)

    Changelog

    645331d chore: release v0.2.1
    6c3dd67 feat(tracking): per-config quiet hours with app-level IANA timezone
    56993d2 fix(security,perf): harden restore, CSRF, token_version + perf pass

    Downloads
  • v0.2.0 fe92b206b7

    Notify Bridge 0.2.0
    Release / release (push) Successful in 1m20s
    Stable

    alexei.dolgolyov released this 2026-04-22 01:35:24 +03:00 | 112 commits to master since this release

    v0.2.0 (2026-04-22)

    First feature release since the initial v0.1.0 cut: a broad polish pass across
    the backend, frontend, and schema, plus two fixes landed on top.

    Features

    Immich commands & tracking

    • Per-chat album scope for Immich commands (/search, /latest, /memory, etc.) with a new Edit album scope modal on command-tracker listeners (inherit or explicit multiselect) (a7a2b4e)
    • /search and /find accept a trailing page number; Immich client search_smart / search_metadata take a page param (a7a2b4e)
    • Auto-organize rules now set the target album's thumbnail to the first added image (falls back to any asset type) when the album has none (a7a2b4e)

    Dashboard & status

    • Action events (action_success / action_partial / action_failed) are emitted on every non-dry-run and surfaced on the dashboard with icons, filters, and colors (a7a2b4e)
    • Clear-events button + confirm modal on the dashboard (DELETE /api/status/events, scoped to the current user) (a7a2b4e)
    • Event rows render live tracker/provider/action names via FK join, with snapshot fallback when an entity has been deleted (a7a2b4e)

    Backup & restore

    • Full backup restore flow: prepare-restore writes a pending marker, a restart banner offers Apply-now / Apply-later, and the lifespan hook applies on next startup and archives under data/applied_restores/ (a7a2b4e)
    • Manual Create backup button on the Backup page (POST /api/backup/files, same format as scheduled) (a7a2b4e)
    • apply-restart sends SIGTERM so the lifespan shutdown runs; NOTIFY_BRIDGE_SUPERVISED env var gates the button (a7a2b4e)

    Users & deletion protection

    • PATCH /api/users/{id} for username and role changes with a last-admin guard, plus an Edit user modal on the Users page (a7a2b4e)
    • Deletion protection now returns structured {message, entity, blocked_by}; ApiError carries .blockedBy and the new BlockedByModal is wired into 8 deletion flows (a7a2b4e)

    Telegram

    • Per-receiver locale for Telegram test messages (resolves TelegramChat.language_override per chat instead of applying the first receiver's locale to everyone) (a7a2b4e)
    • Telegram poller detects the "webhook is active" 409 and auto-calls deleteWebhook for bots whose DB update_mode is polling (throttled per bot) (a7a2b4e)
    • New TelegramClient.get_chat and set_album_thumbnail helpers (CLAUDE.md rule 6 — all Bot API calls go through TelegramClient) (a7a2b4e)

    Form UX

    • Auto-select first available tracking / template / command / config + bot on create forms (trackers, command-trackers, targets, template/command configs) (a7a2b4e)
    • Global provider selector is visible even when there is only one provider (a7a2b4e)
    • Telegram target disable_url_preview defaults to true (a7a2b4e)

    Bug Fixes

    • Telegram target list: load chats/listeners before expanding so the slide animation computes the right height (cf4976d)
    • Test dispatch falls back to tracker defaults for tracking/template config (matching load_link_data), distinguishes "no template config linked" vs. "slot missing in linked config", and the frontend testTrackerTarget now treats {success:false,error:...} in a 2xx body as a failure instead of flashing a success snack (80c034d)
    • Immich person-asset lookup switched from the removed GET /api/people/{id}/assets to POST /api/search/metadata with personIds — fixes /person and auto-organize rules silently returning zero candidates on Immich 1.106+ (a7a2b4e)
    • add_assets_to_album now surfaces the Immich error body on non-2xx responses (a7a2b4e)
    • Immich tracker "Checking links" parallelised (concurrency cap 6) (a7a2b4e)

    Development / Internal

    Schema / Migrations

    • event_log: add user_id, action_id, action_name (+ defensive migration and backfill of user_id from notification_tracker) (a7a2b4e)
    • command_tracker_listener: add allowed_album_ids (a7a2b4e)

    Refactors & Internals

    • Bounded concurrency (semaphores) in NotificationDispatcher._preload_asset_data and _refresh_telegram_chat_titles; chat-title sweep extended to 24h since save_chat_from_webhook covers active chats opportunistically (a7a2b4e)
    • Periodic-summary test path reuses the shared collect_scheduled_assets primitive (limit=0) so test and production go through one path (a7a2b4e)
    • New fetchAuth helper for multipart/binary calls (reuses api()'s refresh + ApiError mapping) (a7a2b4e)
    • parseDate helper for consistent UTC date rendering (a7a2b4e)

    Seeds

    • Rename "Default Commands" → "Default Immich Commands"; track_assets_removed now defaults to False (a7a2b4e)

    All Commits
    Hash Message Author
    cf4976d fix(telegram): load chats/listeners before expanding to fix slide animation height alexei.dolgolyov
    80c034d fix(test-dispatch): fall back to tracker defaults, surface soft errors alexei.dolgolyov
    a7a2b4e feat: large polish pass — UX fixes, per-chat scope, restore/backup, action events alexei.dolgolyov

    Changelog

    fe92b20 chore: release v0.2.0
    cf4976d fix(telegram): load chats/listeners before expanding to fix slide animation height
    80c034d fix(test-dispatch): fall back to tracker defaults, surface soft errors
    a7a2b4e feat: large polish pass — UX fixes, per-chat scope, restore/backup, action events

    Downloads