a7a2b4efa4
Backend
- Per-chat album scope for Immich commands (search/latest/memory/...): new
allowed_album_ids on CommandTrackerListener, threaded listener/page kwargs
through ProviderCommandHandler.handle; PATCH listener-scope endpoint.
- /search and /find accept a trailing page number; Immich client search_smart
/ search_metadata take a page param.
- Immich person-asset lookup switched from removed GET /api/people/{id}/assets
to POST /api/search/metadata with personIds (fixes /person command and
auto_organize rules silently returning zero candidates on Immich 1.106+).
- Auto_organize rule now sets the target album's thumbnail to the first added
image when missing (falls back to any asset type); failures do not fail the
rule. add_assets_to_album surfaces the Immich error body on non-2xx.
- EventLog.user_id / action_id / action_name columns with defensive migration
+ backfill. Status query filters by user_id directly; Immich/webhook paths
emit user_id explicitly. action_runner writes an action_success/partial/
failed event on each non-dry-run.
- Dashboard DELETE /api/status/events (scoped to user_id) + rendering live
tracker/provider/action names via FK join with snapshot fallback.
- PATCH /api/users/{id} for username/role change with last-admin guard.
- Deletion protection returns structured {message, entity, blocked_by}
(ApiError carries .blockedBy; frontend opens BlockedByModal).
- Backup prepare-restore → AppSetting markers + atomic write of
pending_restore.json; lifespan hook applies on next startup and archives
under data/applied_restores/. apply-restart sends SIGTERM so the lifespan
shutdown runs; NOTIFY_BRIDGE_SUPERVISED env override gates the button.
Manual POST /api/backup/files (same format as scheduled).
- New periodic-summary test path reuses shared collect_scheduled_assets
(limit=0) so test and future production code go through one primitive.
- Per-receiver locale for Telegram test messages (resolves
TelegramChat.language_override per chat instead of applying the first
receiver's locale to everyone).
- 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.
- Telegram poller detects the \"webhook is active\" 409 and auto-calls
deleteWebhook for bots whose DB update_mode is polling (throttled per bot).
- TelegramClient.get_chat added (CLAUDE.md rule 6); set_album_thumbnail added.
- Seeds: rename \"Default Commands\" → \"Default Immich Commands\";
track_assets_removed default False.
Frontend
- Global provider selector visible when there is only one provider.
- Clear-events button + i18n + ConfirmModal on the dashboard; new icons/
labels/filters/colors for action_success / action_partial / action_failed.
- Auto-select first available tracking/template/command/config + bot on
create forms (trackers, command-trackers, targets, template/command
configs).
- Telegram target disable_url_preview defaults to true.
- BlockedByModal wired into 8 deletion flows; fetchAuth helper for
multipart/binary calls (reuses api()'s refresh + ApiError mapping).
- Immich tracker 'Checking links' parallelised (concurrency cap 6).
- Backup page: pending-restore banner + Apply-now / Apply-later modal,
restarting overlay polling /api/health, manual 'Create backup' button.
- Command-trackers listener row gets an 'Edit album scope' modal with
inherit/explicit multiselect.
- Users page: Edit user modal (username + role).
- parseDate helper for consistent UTC date rendering.
Migrations / schema
- event_log: + user_id, action_id, action_name (+ backfill user_id from
notification_tracker).
- command_tracker_listener: + allowed_album_ids.
199 lines
7.7 KiB
Python
199 lines
7.7 KiB
Python
"""Sample template context for previews and test notifications.
|
|
|
|
IMPORTANT: Keep sample assets and context in sync with:
|
|
- ``notify_bridge_core.templates.context.build_template_context`` (runtime variables)
|
|
- ``notify_bridge_server.api.template_configs`` (variable docs)
|
|
- ``notify_bridge_core.templates.validator`` (runtime_vars whitelist)
|
|
|
|
When adding new template variables, update all four locations.
|
|
"""
|
|
|
|
# Sample asset matching what build_asset_detail() actually returns
|
|
_SAMPLE_ASSET = {
|
|
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
"filename": "IMG_001.jpg",
|
|
"type": "IMAGE",
|
|
"created_at": "2026-03-19T10:30:00",
|
|
"owner": "Alice",
|
|
"owner_id": "user-uuid-1",
|
|
"description": "Family picnic",
|
|
"people": ["Alice", "Bob"],
|
|
"is_favorite": True,
|
|
"rating": 5,
|
|
"latitude": 48.8566,
|
|
"longitude": 2.3522,
|
|
"city": "Paris",
|
|
"state": "Ile-de-France",
|
|
"country": "France",
|
|
"url": "https://immich.example.com/photos/abc123",
|
|
"public_url": "https://immich.example.com/share/abc123/photos/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
"download_url": "https://immich.example.com/api/assets/abc123/original",
|
|
"photo_url": "https://immich.example.com/api/assets/abc123/thumbnail",
|
|
"file_size": 3_500_000, # 3.5 MB — original asset bytes
|
|
"playback_size": None, # photos are sent as-is, no transcoded variant
|
|
"oversized": False,
|
|
}
|
|
|
|
_SAMPLE_VIDEO_ASSET = {
|
|
**_SAMPLE_ASSET,
|
|
"id": "d4e5f6a7-b8c9-0123-defg-456789abcdef",
|
|
"filename": "VID_002.mp4",
|
|
"type": "VIDEO",
|
|
"is_favorite": False,
|
|
"rating": None,
|
|
"photo_url": None,
|
|
"public_url": "https://immich.example.com/share/abc123/photos/d4e5f6a7-b8c9-0123-defg-456789abcdef",
|
|
"playback_url": "https://immich.example.com/api/assets/def456/video",
|
|
"file_size": 180_000_000, # 180 MB — original HEVC
|
|
"playback_size": 62_000_000, # 62 MB transcoded — exceeds Telegram's 50 MB limit
|
|
"oversized": True,
|
|
}
|
|
|
|
_SAMPLE_COLLECTION = {
|
|
"name": "Family Photos",
|
|
"url": "https://immich.example.com/share/abc123",
|
|
"public_url": "https://immich.example.com/share/abc123",
|
|
"asset_count": 42,
|
|
"shared": True,
|
|
}
|
|
|
|
# Full context covering ALL possible template variables
|
|
_SAMPLE_CONTEXT = {
|
|
# Core event fields (always present)
|
|
"collection_id": "b2eeeaa4-bba0-477a-a06f-5cb9e21818e8",
|
|
"collection_name": "Family Photos",
|
|
"collection_url": "https://immich.example.com/share/abc123",
|
|
"event_type": "assets_added",
|
|
"timestamp": "2026-03-19T10:30:00+00:00",
|
|
"service_name": "Immich",
|
|
"service_type": "immich",
|
|
# Immich aliases (always present alongside collection_*)
|
|
"album_name": "Family Photos",
|
|
"album_id": "b2eeeaa4-bba0-477a-a06f-5cb9e21818e8",
|
|
"old_album_name": "Old Album",
|
|
"new_album_name": "New Album",
|
|
"change_type": "assets_added",
|
|
"added_count": 3,
|
|
"removed_count": 1,
|
|
"added_assets": [_SAMPLE_ASSET, _SAMPLE_VIDEO_ASSET],
|
|
"removed_assets": ["asset-id-1", "asset-id-2"],
|
|
"people": ["Alice", "Bob"],
|
|
"shared": True,
|
|
"target_type": "telegram",
|
|
"has_videos": True,
|
|
"has_photos": True,
|
|
"has_oversized_videos": True,
|
|
"max_video_size": 50 * 1024 * 1024, # 50 MB in bytes
|
|
"max_video_size_mb": 50,
|
|
# Rename fields (always present, empty for non-rename events)
|
|
"old_name": "Old Album",
|
|
"new_name": "New Album",
|
|
"old_shared": False,
|
|
"new_shared": True,
|
|
# Public share URLs (may be empty if no shared link exists)
|
|
"public_url": "https://immich.example.com/share/abc123",
|
|
"protected_url": "",
|
|
"album_url": "https://immich.example.com/albums/b2eeeaa4",
|
|
# Common date/location (set when all assets share the same value)
|
|
"common_date": "19.03.2026",
|
|
"common_location": "Paris, France",
|
|
# Date format strings (from template config)
|
|
"date_format": "%d.%m.%Y, %H:%M UTC",
|
|
"date_only_format": "%d.%m.%Y",
|
|
# Scheduled/periodic variables (for those templates)
|
|
"collections": [_SAMPLE_COLLECTION, {**_SAMPLE_COLLECTION, "name": "Vacation 2025", "asset_count": 120}],
|
|
"albums": [_SAMPLE_COLLECTION, {**_SAMPLE_COLLECTION, "name": "Vacation 2025", "asset_count": 120}],
|
|
"assets": [_SAMPLE_ASSET, {**_SAMPLE_ASSET, "id": "x1y2z3", "filename": "IMG_002.jpg", "city": "London", "country": "UK", "public_url": "https://immich.example.com/share/abc123/photos/x1y2z3"}],
|
|
"date": "2026-03-19",
|
|
"photo_count": 30,
|
|
"video_count": 5,
|
|
"owner": "Alice",
|
|
# Gitea variables (for gitea provider templates)
|
|
"sender": "alexei",
|
|
"sender_name": "Alexei",
|
|
"sender_avatar": "",
|
|
"repo_name": "my-project",
|
|
"repo_full_name": "alexei/my-project",
|
|
"repo_url": "https://gitea.example.com/alexei/my-project",
|
|
"repo_description": "Example project",
|
|
"branch": "main",
|
|
"commits": [{"id": "abc1234567", "short_id": "abc1234", "message": "fix: example commit", "url": "", "author": "Alexei"}],
|
|
"commit_count": 1,
|
|
"compare_url": "https://gitea.example.com/alexei/my-project/compare/abc...def",
|
|
"issue_number": 42,
|
|
"issue_title": "Example issue",
|
|
"issue_url": "https://gitea.example.com/alexei/my-project/issues/42",
|
|
"issue_state": "open",
|
|
"issue_body": "",
|
|
"issue_labels": ["bug"],
|
|
"pr_number": 17,
|
|
"pr_title": "Add feature",
|
|
"pr_url": "https://gitea.example.com/alexei/my-project/pulls/17",
|
|
"pr_state": "open",
|
|
"pr_body": "",
|
|
"pr_merged": False,
|
|
"pr_base": "main",
|
|
"pr_head": "feature/example",
|
|
"pr_labels": [],
|
|
"comment_body": "Looks good!",
|
|
"comment_url": "",
|
|
"comment_author": "alexei",
|
|
"release_tag": "v1.0.0",
|
|
"release_name": "Version 1.0.0",
|
|
"release_url": "https://gitea.example.com/alexei/my-project/releases/tag/v1.0.0",
|
|
"release_body": "Initial release",
|
|
"release_draft": False,
|
|
"release_prerelease": False,
|
|
# Planka variables (for planka provider templates)
|
|
"board_name": "My Project",
|
|
"board_id": "123456",
|
|
"board_url": "https://planka.example.com/boards/123456",
|
|
"card_name": "Fix login bug",
|
|
"card_id": "789012",
|
|
"card_url": "https://planka.example.com/cards/789012",
|
|
"card_description": "Users cannot log in with SSO",
|
|
"card_due_date": "2026-04-01T00:00:00.000Z",
|
|
"list_name": "In Progress",
|
|
"list_id": "list-1",
|
|
"old_list_name": "To Do",
|
|
"new_list_name": "In Progress",
|
|
"old_list_id": "list-0",
|
|
"new_list_id": "list-1",
|
|
"comment_text": "Looks good, ready for review!",
|
|
"comment_id": "comment-1",
|
|
"task_name": "Write unit tests",
|
|
"task_id": "task-1",
|
|
"task_completed": True,
|
|
"attachment_name": "screenshot.png",
|
|
"attachment_id": "att-1",
|
|
"label_name": "bug",
|
|
"label_color": "berry-red",
|
|
# NUT (UPS) variables (for nut provider templates)
|
|
"ups_name": "kiper",
|
|
"ups_model": "Smart-UPS 1500",
|
|
"ups_manufacturer": "APC",
|
|
"battery_charge": 95,
|
|
"battery_runtime": "1:23:45",
|
|
"battery_runtime_seconds": 5025,
|
|
"ups_load": 42,
|
|
"ups_status": "OL",
|
|
"input_voltage": "230.0",
|
|
"output_voltage": "230.0",
|
|
"event_description": "UPS switched to battery power",
|
|
"previous_status": "OL",
|
|
# Scheduler variables (for scheduler provider templates)
|
|
"schedule_name": "Daily Reminder",
|
|
"fire_count": 42,
|
|
"current_date": "22.03.2026",
|
|
"current_time": "09:00",
|
|
"current_datetime": "22.03.2026, 09:00 UTC",
|
|
"weekday": "Monday",
|
|
"custom_vars": {"team": "Engineering", "message": "Time for standup!"},
|
|
"team": "Engineering",
|
|
"message": "Time for standup!",
|
|
# Generic Webhook variables (for webhook provider templates)
|
|
"raw_payload": {"action": "opened", "issue": {"title": "Bug report", "number": 1}, "sender": {"login": "user1"}},
|
|
"event_type_raw": "webhook_received",
|
|
"source_ip": "192.168.1.100",
|
|
}
|