Files
notify-bridge/packages/server/src/notify_bridge_server/services/sample_context.py
T
alexei.dolgolyov a7a2b4efa4 feat: large polish pass — UX fixes, per-chat scope, restore/backup, action events
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.
2026-04-22 01:13:11 +03:00

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",
}