diff --git a/src/vk_parser_app/workers/tg_poster.py b/src/vk_parser_app/workers/tg_poster.py index e02e34f..aa7690b 100644 --- a/src/vk_parser_app/workers/tg_poster.py +++ b/src/vk_parser_app/workers/tg_poster.py @@ -12,7 +12,7 @@ from zoneinfo import ZoneInfo from aiogram import Bot from aiogram.exceptions import TelegramRetryAfter -from aiogram.types import FSInputFile, InputMediaPhoto, InputMediaVideo +from aiogram.types import FSInputFile, InputMediaPhoto, InputMediaVideo, LinkPreviewOptions from loguru import logger from ..config import settings @@ -356,6 +356,56 @@ class TelegramPoster: async def send_post(self, post: dict[str, Any]) -> list[int]: text = self.build_text(post) media_rows = await self.load_media(int(post["id"])) + + # If no media, just send text + if not media_rows: + return await self.send_text(text) + + # Telegram Rich Message (Single message, block layout, up to 4096 chars) + # We use LinkPreviewOptions with the first media URL to show it above the text. + first_url = None + for row in media_rows: + url = str(row.get("original_url") or "").strip() + if url.startswith("http"): + first_url = url + break + + if first_url: + # Send as a Rich Message with Link Preview + message = await self.tg_retry( + lambda: self.bot.send_message( + text=text, + link_preview_options=LinkPreviewOptions( + url=first_url, + show_above_text=True + ), + **self.chat_kwargs() + ) + ) + message_ids = [int(message.message_id)] + + # If it's an album (multiple media), we can send the rest as a media group without caption + if len(media_rows) > 1: + media_items = [] + for item in media_rows[1:]: + media = self.media_value(item) + if not media: continue + media_type = str(item.get("media_type") or "") + if media_type == "photo": + media_items.append({"type": "photo", "media": media}) + elif media_type == "video": + media_items.append({"type": "video", "media": media}) + + rest = media_items + for start in range(0, len(rest), self.media_group_max_items): + chunk = rest[start : start + self.media_group_max_items] + message_ids.extend(await self.send_media_items(chunk, caption=None)) + if self.send_delay_sec: + await asyncio.sleep(self.send_delay_sec) + + return message_ids + + # Fallback if no valid HTTP URL was found for Rich Message media_items = [] for item in media_rows: media = self.media_value(item) @@ -366,12 +416,13 @@ class TelegramPoster: media_items.append({"type": "photo", "media": media}) elif media_type == "video": media_items.append({"type": "video", "media": media}) - message_ids: list[int] = [] + if not media_items: return await self.send_text(text) first_caption = text[: self.caption_limit] first = media_items[: self.media_group_max_items] + message_ids = [] message_ids.extend(await self.send_media_items(first, first_caption)) if self.send_delay_sec: await asyncio.sleep(self.send_delay_sec)