fix(telegram): unify send routine across notifications and commands

- Route cache_key values that look like asset UUIDs through asset_cache
  in TelegramClient._get_cache_and_key. Single-asset sends previously
  stored file_ids in url_cache while the media-group path stored them
  in asset_cache, so repeat sends never hit.
- Extract build_asset_media_urls so the notification dispatcher
  (asset_to_media) and the bot command handlers (common._format_assets)
  share one rule for /video/playback vs thumbnail URLs.
- Add services/telegram_send.py as the single factory for constructing
  a TelegramClient. It always wires the shared aiohttp session and both
  file caches, so commands now reuse file_ids populated by notification
  dispatches (and vice versa) instead of re-uploading the same bytes.
- send_reply / send_media_group in commands/handler.py now delegate to
  the factory rather than constructing their own uncached clients.
This commit is contained in:
2026-04-22 16:45:31 +03:00
parent 325eabd751
commit 6de9a1289e
5 changed files with 193 additions and 52 deletions
@@ -7,7 +7,10 @@ import logging
from typing import Any
from notify_bridge_core.notifications.telegram.media import build_telegram_asset_entry
from notify_bridge_core.providers.immich.asset_utils import get_public_url
from notify_bridge_core.providers.immich.asset_utils import (
build_asset_media_urls,
get_public_url,
)
from ..handler import _render_cmd_template
@@ -127,21 +130,19 @@ def _format_assets(
})
if response_mode == "media":
# Reuse the same entry-building helper as the notification dispatcher
# so videos keep their "video" type and point at /video/playback —
# typing them as "photo" made Telegram render the still poster
# thumbnail in media groups instead of the real clip.
# Reuse the same URL rule (build_asset_media_urls) and entry builder
# (build_telegram_asset_entry) as the notification dispatcher so both
# paths agree on video → /video/playback and photo → thumbnail. When
# these diverged, Telegram rendered a still JPEG for each video in
# the media group instead of the real clip.
media_items: list[dict[str, Any]] = []
for asset in assets:
asset_id = asset.get("id", "")
is_video = (asset.get("type") or "").upper() == "VIDEO"
if is_video:
url = f"{client.url}/api/assets/{asset_id}/video/playback"
else:
url = f"{client.url}/api/assets/{asset_id}/thumbnail?size=preview"
asset_type = (asset.get("type") or "").upper()
preview_url, _ = build_asset_media_urls(client.url, asset_id, asset_type)
entry = build_telegram_asset_entry(
url=url,
media_type="video" if is_video else "image",
url=preview_url,
media_type="video" if asset_type == "VIDEO" else "image",
api_key=client.api_key,
internal_url=client.url,
cache_key=asset_id,