++
This commit is contained in:
+22
-1
@@ -178,6 +178,7 @@ class MaxToTelegramBridge:
|
|||||||
|
|
||||||
for index, file_url in enumerate(parsed.file_urls):
|
for index, file_url in enumerate(parsed.file_urls):
|
||||||
caption = text if not sent_any and index == 0 else None
|
caption = text if not sent_any and index == 0 else None
|
||||||
|
file_name = parsed.file_names_by_url.get(file_url)
|
||||||
target_chat_id, sent = await self._send_with_migration_retry(
|
target_chat_id, sent = await self._send_with_migration_retry(
|
||||||
target_chat_id=target_chat_id,
|
target_chat_id=target_chat_id,
|
||||||
max_chat_title_norm=normalized,
|
max_chat_title_norm=normalized,
|
||||||
@@ -185,6 +186,7 @@ class MaxToTelegramBridge:
|
|||||||
send_action=lambda chat_id: self._telegram.send_document(
|
send_action=lambda chat_id: self._telegram.send_document(
|
||||||
chat_id,
|
chat_id,
|
||||||
file_url,
|
file_url,
|
||||||
|
file_name=file_name,
|
||||||
caption=caption,
|
caption=caption,
|
||||||
reply_to_message_id=reply_telegram_mid if not sent_any and index == 0 else None,
|
reply_to_message_id=reply_telegram_mid if not sent_any and index == 0 else None,
|
||||||
),
|
),
|
||||||
@@ -366,6 +368,13 @@ class MaxToTelegramBridge:
|
|||||||
parsed.image_urls = list(dict.fromkeys(parsed.image_urls))
|
parsed.image_urls = list(dict.fromkeys(parsed.image_urls))
|
||||||
parsed.video_urls = list(dict.fromkeys(parsed.video_urls))
|
parsed.video_urls = list(dict.fromkeys(parsed.video_urls))
|
||||||
parsed.file_urls = list(dict.fromkeys(parsed.file_urls))
|
parsed.file_urls = list(dict.fromkeys(parsed.file_urls))
|
||||||
|
parsed.file_names_by_url = {
|
||||||
|
url: name for url, name in parsed.file_names_by_url.items() if url in parsed.file_urls and name
|
||||||
|
}
|
||||||
|
if parsed.file_urls:
|
||||||
|
parsed.unknown_attachments = [
|
||||||
|
x for x in parsed.unknown_attachments if not self._is_file_unknown_marker(x)
|
||||||
|
]
|
||||||
parsed.unknown_attachments = list(dict.fromkeys(parsed.unknown_attachments))
|
parsed.unknown_attachments = list(dict.fromkeys(parsed.unknown_attachments))
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
@@ -409,6 +418,9 @@ class MaxToTelegramBridge:
|
|||||||
)
|
)
|
||||||
if resolved:
|
if resolved:
|
||||||
parsed.file_urls.append(resolved)
|
parsed.file_urls.append(resolved)
|
||||||
|
file_name = str(getattr(attach, "name", "") or "").strip()
|
||||||
|
if file_name:
|
||||||
|
parsed.file_names_by_url[resolved] = file_name
|
||||||
else:
|
else:
|
||||||
fallback = str(getattr(attach, "name", "") or "").strip()
|
fallback = str(getattr(attach, "name", "") or "").strip()
|
||||||
if fallback:
|
if fallback:
|
||||||
@@ -484,7 +496,11 @@ class MaxToTelegramBridge:
|
|||||||
)
|
)
|
||||||
url = getattr(file_info, "url", None)
|
url = getattr(file_info, "url", None)
|
||||||
if url:
|
if url:
|
||||||
parsed.file_urls.append(str(url))
|
resolved = str(url)
|
||||||
|
parsed.file_urls.append(resolved)
|
||||||
|
file_name = str(getattr(attach, "name", "") or "").strip()
|
||||||
|
if file_name:
|
||||||
|
parsed.file_names_by_url[resolved] = file_name
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.debug("Cannot resolve generic file attach from Max", exc_info=True)
|
logger.debug("Cannot resolve generic file attach from Max", exc_info=True)
|
||||||
@@ -604,3 +620,8 @@ class MaxToTelegramBridge:
|
|||||||
)
|
)
|
||||||
unknown = ", ".join(parsed.unknown_attachments[:5]) if parsed.unknown_attachments else "unknown"
|
unknown = ", ".join(parsed.unknown_attachments[:5]) if parsed.unknown_attachments else "unknown"
|
||||||
return f"{base}\n\n[!] Неизвестный или пустой тип сообщения из MAX (attachments={unknown})."
|
return f"{base}\n\n[!] Неизвестный или пустой тип сообщения из MAX (attachments={unknown})."
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_file_unknown_marker(value: str) -> bool:
|
||||||
|
normalized = str(value or "").strip().casefold()
|
||||||
|
return normalized in {"attachtype.file", "fileattach", "file"}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ class ParsedMessage:
|
|||||||
image_urls: list[str] = field(default_factory=list)
|
image_urls: list[str] = field(default_factory=list)
|
||||||
video_urls: list[str] = field(default_factory=list)
|
video_urls: list[str] = field(default_factory=list)
|
||||||
file_urls: list[str] = field(default_factory=list)
|
file_urls: list[str] = field(default_factory=list)
|
||||||
|
# URL -> исходное имя файла (если удалось определить в MAX).
|
||||||
|
file_names_by_url: dict[str, str] = field(default_factory=dict)
|
||||||
unknown_attachments: list[str] = field(default_factory=list)
|
unknown_attachments: list[str] = field(default_factory=list)
|
||||||
# Ответ в MAX: Message.link указывает на исходное сообщение (тред).
|
# Ответ в MAX: Message.link указывает на исходное сообщение (тред).
|
||||||
reply_to_max_message_id: str | None = None
|
reply_to_max_message_id: str | None = None
|
||||||
|
|||||||
+16
-4
@@ -112,17 +112,20 @@ class TelegramClient:
|
|||||||
self,
|
self,
|
||||||
chat_id: str,
|
chat_id: str,
|
||||||
document_url: str,
|
document_url: str,
|
||||||
|
file_name: str | None = None,
|
||||||
caption: str | None = None,
|
caption: str | None = None,
|
||||||
*,
|
*,
|
||||||
reply_to_message_id: int | None = None,
|
reply_to_message_id: int | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
# Telegram часто не может скачать URL, которые доступны только клиенту MAX.
|
# Telegram часто не может скачать URL, которые доступны только клиенту MAX.
|
||||||
# Поэтому скачиваем сами во временный файл и отправляем как multipart upload.
|
# Поэтому скачиваем сами во временный файл и отправляем как multipart upload.
|
||||||
tmp_path = await self._download_to_temp(document_url)
|
safe_name = self._sanitize_filename(file_name) if file_name else None
|
||||||
|
tmp_path = await self._download_to_temp(document_url, preferred_filename=safe_name)
|
||||||
try:
|
try:
|
||||||
return await self.send_document_file(
|
return await self.send_document_file(
|
||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
file_path=tmp_path,
|
file_path=tmp_path,
|
||||||
|
upload_filename=safe_name,
|
||||||
caption=caption,
|
caption=caption,
|
||||||
reply_to_message_id=reply_to_message_id,
|
reply_to_message_id=reply_to_message_id,
|
||||||
)
|
)
|
||||||
@@ -137,6 +140,7 @@ class TelegramClient:
|
|||||||
*,
|
*,
|
||||||
chat_id: str,
|
chat_id: str,
|
||||||
file_path: str,
|
file_path: str,
|
||||||
|
upload_filename: str | None = None,
|
||||||
caption: str | None = None,
|
caption: str | None = None,
|
||||||
reply_to_message_id: int | None = None,
|
reply_to_message_id: int | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
@@ -146,7 +150,7 @@ class TelegramClient:
|
|||||||
if reply_to_message_id is not None:
|
if reply_to_message_id is not None:
|
||||||
payload["reply_to_message_id"] = str(int(reply_to_message_id))
|
payload["reply_to_message_id"] = str(int(reply_to_message_id))
|
||||||
|
|
||||||
filename = pathlib.Path(file_path).name
|
filename = upload_filename or pathlib.Path(file_path).name
|
||||||
|
|
||||||
def _do_request() -> requests.Response:
|
def _do_request() -> requests.Response:
|
||||||
with open(file_path, "rb") as f:
|
with open(file_path, "rb") as f:
|
||||||
@@ -327,8 +331,8 @@ class TelegramClient:
|
|||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def _download_to_temp(self, url: str) -> str:
|
async def _download_to_temp(self, url: str, preferred_filename: str | None = None) -> str:
|
||||||
filename = self._infer_filename_from_url(url) or "max-file"
|
filename = preferred_filename or self._infer_filename_from_url(url) or "max-file"
|
||||||
tmp_dir = self._ensure_tmp_root()
|
tmp_dir = self._ensure_tmp_root()
|
||||||
fd, path = tempfile.mkstemp(prefix="max2tg_", suffix=f"_{filename}", dir=tmp_dir)
|
fd, path = tempfile.mkstemp(prefix="max2tg_", suffix=f"_{filename}", dir=tmp_dir)
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
@@ -370,3 +374,11 @@ class TelegramClient:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _sanitize_filename(name: str | None) -> str | None:
|
||||||
|
if not name:
|
||||||
|
return None
|
||||||
|
bad = '<>:"/\\|?*'
|
||||||
|
cleaned = "".join("_" if ch in bad else ch for ch in str(name)).strip().strip(".")
|
||||||
|
return cleaned or None
|
||||||
|
|||||||
Reference in New Issue
Block a user