239 lines
6.0 KiB
Python
239 lines
6.0 KiB
Python
from typing import Any
|
|
|
|
import structlog
|
|
from pymax.types.domain.message import Message as MaxMessage
|
|
|
|
from app.models.domain import MaxIncomingMessage
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
def get_forward_link(message: MaxMessage) -> dict[str, Any] | None:
|
|
link = getattr(message, "link", None)
|
|
if not isinstance(link, dict):
|
|
return None
|
|
if str(link.get("type", "")).upper() != "FORWARD":
|
|
return None
|
|
return link
|
|
|
|
|
|
def get_reply_target(message: MaxMessage) -> int | None:
|
|
link = getattr(message, "link", None)
|
|
if not isinstance(link, dict):
|
|
return None
|
|
if str(link.get("type", "")).upper() != "REPLY":
|
|
return None
|
|
target = link.get("messageId") or link.get("message_id")
|
|
if target is None:
|
|
return None
|
|
try:
|
|
return int(target)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
|
|
|
|
def extract_forwarded_content(
|
|
message: MaxMessage,
|
|
) -> tuple[str, list[dict[str, Any]]] | None:
|
|
link = get_forward_link(message)
|
|
if link is None:
|
|
return None
|
|
|
|
nested = link.get("message")
|
|
if not isinstance(nested, dict):
|
|
logger.warning(
|
|
"max_forward_missing_nested_message",
|
|
chat_id=message.chat_id,
|
|
message_id=message.id,
|
|
)
|
|
return None
|
|
|
|
text = nested.get("text") or ""
|
|
attaches = nested.get("attaches") or []
|
|
if not isinstance(attaches, list):
|
|
attaches = []
|
|
|
|
logger.info(
|
|
"max_forward_extracted",
|
|
chat_id=message.chat_id,
|
|
message_id=message.id,
|
|
nested_message_id=nested.get("id"),
|
|
nested_sender=nested.get("sender"),
|
|
text_len=len(text),
|
|
attach_count=len(attaches),
|
|
)
|
|
return text, attaches
|
|
|
|
|
|
def format_forwarded_text(
|
|
original_text: str,
|
|
original_sender_name: str | None = None,
|
|
) -> str:
|
|
header = "↪️ Переслано"
|
|
if original_sender_name:
|
|
header += f" от {original_sender_name}"
|
|
if original_text:
|
|
return f"{header}:\n{original_text}"
|
|
return header
|
|
|
|
|
|
def _attach_type(attach: dict[str, Any]) -> str:
|
|
return str(attach.get("_type") or attach.get("type") or "").upper()
|
|
|
|
|
|
async def resolve_raw_attaches(
|
|
client,
|
|
chat_id: int,
|
|
message_id: int,
|
|
attaches: list[dict[str, Any]],
|
|
) -> list[dict]:
|
|
items: list[dict] = []
|
|
for attach in attaches:
|
|
if not isinstance(attach, dict):
|
|
continue
|
|
|
|
attach_type = _attach_type(attach)
|
|
if attach_type == "PHOTO":
|
|
url = attach.get("baseUrl") or attach.get("base_url")
|
|
if url:
|
|
items.append(
|
|
{
|
|
"kind": "photo",
|
|
"url": url,
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message_id,
|
|
}
|
|
)
|
|
continue
|
|
|
|
if attach_type == "VIDEO":
|
|
video_id = attach.get("videoId") or attach.get("video_id")
|
|
if video_id is not None:
|
|
items.append(
|
|
{
|
|
"kind": "video",
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message_id,
|
|
"max_video_id": video_id,
|
|
}
|
|
)
|
|
continue
|
|
|
|
if attach_type == "FILE":
|
|
file_id = attach.get("fileId") or attach.get("file_id")
|
|
if file_id is not None:
|
|
items.append(
|
|
{
|
|
"kind": "document",
|
|
"file_name": attach.get("name"),
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message_id,
|
|
"max_file_id": file_id,
|
|
}
|
|
)
|
|
continue
|
|
|
|
url = attach.get("baseUrl") or attach.get("base_url") or attach.get("url")
|
|
if url:
|
|
items.append(
|
|
{
|
|
"kind": "document",
|
|
"url": url,
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message_id,
|
|
}
|
|
)
|
|
|
|
logger.debug(
|
|
"max_raw_attaches_resolved",
|
|
chat_id=chat_id,
|
|
message_id=message_id,
|
|
input_count=len(attaches),
|
|
resolved_count=len(items),
|
|
)
|
|
return items
|
|
|
|
|
|
async def resolve_media(client, message: MaxMessage) -> list[dict]:
|
|
from pymax.types.domain.attachments.file import FileAttachment
|
|
from pymax.types.domain.attachments.photo import PhotoAttachment
|
|
from pymax.types.domain.attachments.video import VideoAttachment
|
|
|
|
items: list[dict] = []
|
|
chat_id = message.chat_id
|
|
if chat_id is None:
|
|
return items
|
|
|
|
for attach in message.attaches:
|
|
if isinstance(attach, dict):
|
|
items.extend(
|
|
await resolve_raw_attaches(client, chat_id, message.id, [attach])
|
|
)
|
|
continue
|
|
if isinstance(attach, PhotoAttachment):
|
|
items.append(
|
|
{
|
|
"kind": "photo",
|
|
"url": attach.base_url,
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message.id,
|
|
}
|
|
)
|
|
elif isinstance(attach, VideoAttachment):
|
|
items.append(
|
|
{
|
|
"kind": "video",
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message.id,
|
|
"max_video_id": attach.video_id,
|
|
}
|
|
)
|
|
elif isinstance(attach, FileAttachment):
|
|
items.append(
|
|
{
|
|
"kind": "document",
|
|
"file_name": attach.name,
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message.id,
|
|
"max_file_id": attach.file_id,
|
|
}
|
|
)
|
|
else:
|
|
url = getattr(attach, "base_url", None) or getattr(attach, "url", None)
|
|
if url:
|
|
items.append(
|
|
{
|
|
"kind": "document",
|
|
"url": url,
|
|
"max_chat_id": chat_id,
|
|
"max_message_id": message.id,
|
|
}
|
|
)
|
|
return items
|
|
|
|
|
|
def build_chat_title(chat, ls_prefix: str) -> tuple[str, bool]:
|
|
is_dm = bool(getattr(chat, "is_dialog", False) or chat.type == "DIALOG")
|
|
if is_dm:
|
|
title = chat.title or "Контакт"
|
|
if not title.startswith(ls_prefix.strip()):
|
|
title = f"{ls_prefix}{title}"
|
|
return title, True
|
|
return chat.title or f"Чат {chat.id}", False
|
|
|
|
|
|
def resolve_sender_name(user) -> str:
|
|
if user is None:
|
|
return "Неизвестный"
|
|
if user.names:
|
|
name = user.names[0]
|
|
parts = [name.first_name, name.last_name]
|
|
return " ".join(p for p in parts if p) or f"User {user.id}"
|
|
return f"User {user.id}"
|
|
|
|
|
|
def format_max_text(message: MaxIncomingMessage) -> str:
|
|
if message.is_dm:
|
|
return message.text
|
|
sender = message.sender_name or "Неизвестный"
|
|
return f"{sender}:\n{message.text}"
|