++
This commit is contained in:
+148
-24
@@ -5,7 +5,7 @@ from typing import Any
|
||||
from max_parser import parse_message
|
||||
from models import ParsedMessage
|
||||
from pymax import MaxClient
|
||||
from pymax.types import PhotoAttach, VideoAttach
|
||||
from pymax.types import AudioAttach, FileAttach, Message, PhotoAttach, StickerAttach, VideoAttach
|
||||
from storage import BridgeStorage
|
||||
from telegram_api import TelegramApiError, TelegramClient
|
||||
|
||||
@@ -340,29 +340,24 @@ class MaxToTelegramBridge:
|
||||
except Exception:
|
||||
logger.debug("Cannot resolve chat title", exc_info=True)
|
||||
|
||||
attaches = getattr(max_message, "attaches", None) or []
|
||||
for attach in attaches:
|
||||
if isinstance(attach, PhotoAttach):
|
||||
parsed.image_urls.extend(self._extract_photo_urls(attach))
|
||||
elif isinstance(attach, VideoAttach):
|
||||
try:
|
||||
video = await self._max_client.get_video_by_id(
|
||||
chat_id=max_message.chat_id,
|
||||
message_id=max_message.id,
|
||||
video_id=attach.video_id,
|
||||
)
|
||||
video_url = getattr(video, "url", None)
|
||||
if video_url:
|
||||
parsed.video_urls.append(str(video_url))
|
||||
except Exception:
|
||||
logger.exception("Cannot resolve video URL from Max")
|
||||
else:
|
||||
urls = self._extract_any_urls(attach)
|
||||
if urls:
|
||||
parsed.file_urls.extend(urls)
|
||||
else:
|
||||
if not self._is_forward_attach_like(attach):
|
||||
parsed.unknown_attachments.append(type(attach).__name__)
|
||||
await self._collect_message_attachments(
|
||||
message=max_message,
|
||||
parsed=parsed,
|
||||
source_tag="root",
|
||||
)
|
||||
|
||||
link = getattr(max_message, "link", None)
|
||||
linked_message = getattr(link, "message", None)
|
||||
if linked_message is not None:
|
||||
if not (parsed.text or "").strip():
|
||||
linked_text = str(getattr(linked_message, "text", "") or "").strip()
|
||||
if linked_text:
|
||||
parsed.text = linked_text
|
||||
await self._collect_message_attachments(
|
||||
message=linked_message,
|
||||
parsed=parsed,
|
||||
source_tag="forward",
|
||||
)
|
||||
|
||||
# Убираем дубли URL, если парсер и enrich нашли одинаковые вложения.
|
||||
parsed.image_urls = list(dict.fromkeys(parsed.image_urls))
|
||||
@@ -371,6 +366,127 @@ class MaxToTelegramBridge:
|
||||
parsed.unknown_attachments = list(dict.fromkeys(parsed.unknown_attachments))
|
||||
return parsed
|
||||
|
||||
async def _collect_message_attachments(
|
||||
self,
|
||||
*,
|
||||
message: Any,
|
||||
parsed: ParsedMessage,
|
||||
source_tag: str,
|
||||
) -> None:
|
||||
attaches = getattr(message, "attaches", None) or []
|
||||
message_chat_id = getattr(message, "chat_id", None)
|
||||
message_id = getattr(message, "id", None)
|
||||
if message_chat_id is None:
|
||||
message_chat_id = parsed.chat_id
|
||||
|
||||
for attach in attaches:
|
||||
if isinstance(attach, PhotoAttach):
|
||||
parsed.image_urls.extend(self._extract_photo_urls(attach))
|
||||
continue
|
||||
|
||||
if isinstance(attach, VideoAttach):
|
||||
try:
|
||||
video = await self._max_client.get_video_by_id(
|
||||
chat_id=message_chat_id,
|
||||
message_id=message_id,
|
||||
video_id=attach.video_id,
|
||||
)
|
||||
video_url = getattr(video, "url", None)
|
||||
if video_url:
|
||||
parsed.video_urls.append(str(video_url))
|
||||
except Exception:
|
||||
logger.exception("Cannot resolve video URL from Max (%s)", source_tag)
|
||||
continue
|
||||
|
||||
if isinstance(attach, FileAttach):
|
||||
resolved = await self._resolve_file_attach_url(
|
||||
message_chat_id=message_chat_id,
|
||||
message_id=message_id,
|
||||
attach=attach,
|
||||
)
|
||||
if resolved:
|
||||
parsed.file_urls.append(resolved)
|
||||
else:
|
||||
fallback = str(getattr(attach, "name", "") or "").strip()
|
||||
if fallback:
|
||||
parsed.text = self._append_missing_file_note(parsed.text, fallback)
|
||||
else:
|
||||
parsed.unknown_attachments.append(type(attach).__name__)
|
||||
continue
|
||||
|
||||
if isinstance(attach, AudioAttach):
|
||||
audio_url = str(getattr(attach, "url", "") or "").strip()
|
||||
if audio_url:
|
||||
parsed.file_urls.append(audio_url)
|
||||
else:
|
||||
parsed.unknown_attachments.append(type(attach).__name__)
|
||||
continue
|
||||
|
||||
if isinstance(attach, StickerAttach):
|
||||
sticker_url = str(getattr(attach, "url", "") or "").strip()
|
||||
if sticker_url:
|
||||
parsed.image_urls.append(sticker_url)
|
||||
else:
|
||||
parsed.unknown_attachments.append(type(attach).__name__)
|
||||
continue
|
||||
|
||||
# Fallback на случай сырого Attach/нестандартного типа:
|
||||
if await self._resolve_generic_file_attach(
|
||||
message_chat_id=message_chat_id,
|
||||
message_id=message_id,
|
||||
attach=attach,
|
||||
parsed=parsed,
|
||||
):
|
||||
continue
|
||||
|
||||
urls = self._extract_any_urls(attach)
|
||||
if urls:
|
||||
parsed.file_urls.extend(urls)
|
||||
continue
|
||||
|
||||
parsed.unknown_attachments.append(type(attach).__name__)
|
||||
|
||||
async def _resolve_file_attach_url(self, *, message_chat_id: Any, message_id: Any, attach: FileAttach) -> str | None:
|
||||
file_id = getattr(attach, "file_id", None)
|
||||
if file_id is None or message_id is None:
|
||||
return None
|
||||
try:
|
||||
file_info = await self._max_client.get_file_by_id(
|
||||
chat_id=message_chat_id,
|
||||
message_id=message_id,
|
||||
file_id=file_id,
|
||||
)
|
||||
url = getattr(file_info, "url", None)
|
||||
return str(url) if url else None
|
||||
except Exception:
|
||||
logger.exception("Cannot resolve file URL from Max (file_id=%s)", file_id)
|
||||
return None
|
||||
|
||||
async def _resolve_generic_file_attach(
|
||||
self,
|
||||
*,
|
||||
message_chat_id: Any,
|
||||
message_id: Any,
|
||||
attach: Any,
|
||||
parsed: ParsedMessage,
|
||||
) -> bool:
|
||||
file_id = getattr(attach, "file_id", None)
|
||||
if file_id is None or message_id is None:
|
||||
return False
|
||||
try:
|
||||
file_info = await self._max_client.get_file_by_id(
|
||||
chat_id=message_chat_id,
|
||||
message_id=message_id,
|
||||
file_id=file_id,
|
||||
)
|
||||
url = getattr(file_info, "url", None)
|
||||
if url:
|
||||
parsed.file_urls.append(str(url))
|
||||
return True
|
||||
except Exception:
|
||||
logger.debug("Cannot resolve generic file attach from Max", exc_info=True)
|
||||
return False
|
||||
|
||||
def _is_self_message(self, max_message: Any) -> bool:
|
||||
sender = getattr(max_message, "sender", None)
|
||||
me = getattr(self._max_client, "me", None)
|
||||
@@ -459,6 +575,14 @@ class MaxToTelegramBridge:
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _append_missing_file_note(current_text: str, file_name: str) -> str:
|
||||
text = (current_text or "").strip()
|
||||
note = f"[MAX forwarded file without direct URL] {file_name}"
|
||||
if not text:
|
||||
return note
|
||||
return f"{text}\n{note}"
|
||||
|
||||
@staticmethod
|
||||
def _append_unknown_attachment_notice(*, parsed: ParsedMessage, text: str) -> str:
|
||||
if not parsed.unknown_attachments:
|
||||
|
||||
@@ -94,49 +94,6 @@ def _collect_urls(node: Any) -> list[str]:
|
||||
return list(dict.fromkeys(urls))
|
||||
|
||||
|
||||
def _extract_forwarded_text(message: Any) -> str:
|
||||
candidates: list[str] = []
|
||||
seen_ids: set[int] = set()
|
||||
forward_keys = {"forward", "forwarded", "forwards", "link", "message", "messages", "payload", "quote", "origin"}
|
||||
text_keys = {"text", "message", "body", "caption"}
|
||||
|
||||
def walk(value: Any, inside_forward: bool) -> None:
|
||||
if value is None:
|
||||
return
|
||||
obj_id = id(value)
|
||||
if obj_id in seen_ids:
|
||||
return
|
||||
seen_ids.add(obj_id)
|
||||
|
||||
if isinstance(value, str):
|
||||
if inside_forward:
|
||||
text = value.strip()
|
||||
if text and not (text.startswith("http://") or text.startswith("https://")):
|
||||
candidates.append(text)
|
||||
return
|
||||
if isinstance(value, (list, tuple, set)):
|
||||
for item in value:
|
||||
walk(item, inside_forward)
|
||||
return
|
||||
if isinstance(value, dict):
|
||||
current_is_forward = inside_forward or any(key in value for key in forward_keys)
|
||||
for key, nested in value.items():
|
||||
if key in text_keys and current_is_forward and isinstance(nested, str):
|
||||
text = nested.strip()
|
||||
if text and not (text.startswith("http://") or text.startswith("https://")):
|
||||
candidates.append(text)
|
||||
walk(nested, current_is_forward)
|
||||
return
|
||||
if hasattr(value, "__dict__"):
|
||||
walk(vars(value), inside_forward)
|
||||
|
||||
walk(message, False)
|
||||
uniq = list(dict.fromkeys(candidates))
|
||||
if not uniq:
|
||||
return ""
|
||||
return "\n\n".join(uniq[:5])
|
||||
|
||||
|
||||
def _extract_media_urls(message: Any) -> tuple[list[str], list[str], list[str], list[str]]:
|
||||
image_urls: list[str] = []
|
||||
video_urls: list[str] = []
|
||||
@@ -239,11 +196,6 @@ def parse_message(message: Any) -> ParsedMessage:
|
||||
message_id = _stringify(_get_attr(message, ["id", "message_id", "mid"])) or "unknown-id"
|
||||
chat_id = _stringify(_get_attr(message, ["chat_id", "dialog_id", "peer_id"])) or "unknown-chat"
|
||||
text = _stringify(_get_attr(message, ["text", "message", "body"]))
|
||||
forwarded_text = _extract_forwarded_text(message)
|
||||
if text and forwarded_text and forwarded_text != text:
|
||||
text = f"{text}\n\n[forwarded]\n{forwarded_text}"
|
||||
elif not text and forwarded_text:
|
||||
text = forwarded_text
|
||||
|
||||
image_urls, video_urls, file_urls, unknown_attachments = _extract_media_urls(message)
|
||||
reply_mid, reply_preview = _extract_max_reply(message)
|
||||
|
||||
Reference in New Issue
Block a user