после миграции группы в супергруппу необходимо обновить её id
This commit is contained in:
+69
-19
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from collections.abc import Awaitable, Callable
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from max_parser import parse_message
|
from max_parser import parse_message
|
||||||
@@ -6,7 +7,7 @@ from models import ParsedMessage
|
|||||||
from pymax import MaxClient
|
from pymax import MaxClient
|
||||||
from pymax.types import PhotoAttach, VideoAttach
|
from pymax.types import PhotoAttach, VideoAttach
|
||||||
from storage import BridgeStorage
|
from storage import BridgeStorage
|
||||||
from telegram_api import TelegramClient
|
from telegram_api import TelegramApiError, TelegramClient
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -80,12 +81,17 @@ class MaxToTelegramBridge:
|
|||||||
total_media = len(parsed.image_urls) + len(parsed.video_urls)
|
total_media = len(parsed.image_urls) + len(parsed.video_urls)
|
||||||
if total_media > 1:
|
if total_media > 1:
|
||||||
# Отправляем одним альбомом в Telegram (единое сообщение).
|
# Отправляем одним альбомом в Telegram (единое сообщение).
|
||||||
sent_messages = await self._telegram.send_media_group(
|
target_chat_id, sent_messages = await self._send_with_migration_retry(
|
||||||
chat_id=target_chat_id,
|
target_chat_id=target_chat_id,
|
||||||
image_urls=parsed.image_urls,
|
max_chat_title_norm=normalized,
|
||||||
video_urls=parsed.video_urls,
|
max_chat_title=parsed.chat_name,
|
||||||
caption=text,
|
send_action=lambda chat_id: self._telegram.send_media_group(
|
||||||
reply_to_message_id=reply_telegram_mid,
|
chat_id=chat_id,
|
||||||
|
image_urls=parsed.image_urls,
|
||||||
|
video_urls=parsed.video_urls,
|
||||||
|
caption=text,
|
||||||
|
reply_to_message_id=reply_telegram_mid,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
for sent in sent_messages:
|
for sent in sent_messages:
|
||||||
mid = sent.get("message_id")
|
mid = sent.get("message_id")
|
||||||
@@ -109,8 +115,13 @@ class MaxToTelegramBridge:
|
|||||||
|
|
||||||
sent_any = False
|
sent_any = False
|
||||||
if parsed.text.strip() and total_media == 0:
|
if parsed.text.strip() and total_media == 0:
|
||||||
sent = await self._telegram.send_text(
|
target_chat_id, sent = await self._send_with_migration_retry(
|
||||||
target_chat_id, text, reply_to_message_id=reply_telegram_mid
|
target_chat_id=target_chat_id,
|
||||||
|
max_chat_title_norm=normalized,
|
||||||
|
max_chat_title=parsed.chat_name,
|
||||||
|
send_action=lambda chat_id: self._telegram.send_text(
|
||||||
|
chat_id, text, reply_to_message_id=reply_telegram_mid
|
||||||
|
),
|
||||||
)
|
)
|
||||||
mid = sent.get("result", {}).get("message_id") if isinstance(sent.get("result"), dict) else None
|
mid = sent.get("result", {}).get("message_id") if isinstance(sent.get("result"), dict) else None
|
||||||
if mid is not None:
|
if mid is not None:
|
||||||
@@ -124,11 +135,16 @@ class MaxToTelegramBridge:
|
|||||||
|
|
||||||
for index, image_url in enumerate(parsed.image_urls):
|
for index, image_url in enumerate(parsed.image_urls):
|
||||||
caption = text if not sent_any and index == 0 else None
|
caption = text if not sent_any and index == 0 else None
|
||||||
sent = await self._telegram.send_photo(
|
target_chat_id, sent = await self._send_with_migration_retry(
|
||||||
target_chat_id,
|
target_chat_id=target_chat_id,
|
||||||
image_url,
|
max_chat_title_norm=normalized,
|
||||||
caption=caption,
|
max_chat_title=parsed.chat_name,
|
||||||
reply_to_message_id=reply_telegram_mid if not sent_any and index == 0 else None,
|
send_action=lambda chat_id: self._telegram.send_photo(
|
||||||
|
chat_id,
|
||||||
|
image_url,
|
||||||
|
caption=caption,
|
||||||
|
reply_to_message_id=reply_telegram_mid if not sent_any and index == 0 else None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
mid = sent.get("result", {}).get("message_id") if isinstance(sent.get("result"), dict) else None
|
mid = sent.get("result", {}).get("message_id") if isinstance(sent.get("result"), dict) else None
|
||||||
if mid is not None:
|
if mid is not None:
|
||||||
@@ -142,11 +158,16 @@ class MaxToTelegramBridge:
|
|||||||
|
|
||||||
for index, video_url in enumerate(parsed.video_urls):
|
for index, video_url in enumerate(parsed.video_urls):
|
||||||
caption = text if not sent_any and index == 0 else None
|
caption = text if not sent_any and index == 0 else None
|
||||||
sent = await self._telegram.send_video(
|
target_chat_id, sent = await self._send_with_migration_retry(
|
||||||
target_chat_id,
|
target_chat_id=target_chat_id,
|
||||||
video_url,
|
max_chat_title_norm=normalized,
|
||||||
caption=caption,
|
max_chat_title=parsed.chat_name,
|
||||||
reply_to_message_id=reply_telegram_mid if not sent_any and index == 0 else None,
|
send_action=lambda chat_id: self._telegram.send_video(
|
||||||
|
chat_id,
|
||||||
|
video_url,
|
||||||
|
caption=caption,
|
||||||
|
reply_to_message_id=reply_telegram_mid if not sent_any and index == 0 else None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
mid = sent.get("result", {}).get("message_id") if isinstance(sent.get("result"), dict) else None
|
mid = sent.get("result", {}).get("message_id") if isinstance(sent.get("result"), dict) else None
|
||||||
if mid is not None:
|
if mid is not None:
|
||||||
@@ -167,6 +188,35 @@ class MaxToTelegramBridge:
|
|||||||
len(parsed.video_urls),
|
len(parsed.video_urls),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _send_with_migration_retry(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
target_chat_id: str,
|
||||||
|
max_chat_title_norm: str,
|
||||||
|
max_chat_title: str,
|
||||||
|
send_action: Callable[[str], Awaitable[Any]],
|
||||||
|
) -> tuple[str, Any]:
|
||||||
|
try:
|
||||||
|
sent = await send_action(target_chat_id)
|
||||||
|
return target_chat_id, sent
|
||||||
|
except TelegramApiError as exc:
|
||||||
|
migrated_chat_id = exc.migrate_to_chat_id
|
||||||
|
if not migrated_chat_id or migrated_chat_id == str(target_chat_id):
|
||||||
|
raise
|
||||||
|
logger.warning(
|
||||||
|
"Telegram chat %s upgraded to %s for MAX chat '%s'; update route and retry",
|
||||||
|
target_chat_id,
|
||||||
|
migrated_chat_id,
|
||||||
|
max_chat_title,
|
||||||
|
)
|
||||||
|
self._storage.set_chat_route(
|
||||||
|
max_chat_title_norm=max_chat_title_norm,
|
||||||
|
telegram_chat_id=migrated_chat_id,
|
||||||
|
telegram_chat_title=max_chat_title,
|
||||||
|
)
|
||||||
|
sent = await send_action(migrated_chat_id)
|
||||||
|
return migrated_chat_id, sent
|
||||||
|
|
||||||
def _resolve_telegram_reply_to(
|
def _resolve_telegram_reply_to(
|
||||||
self, *, telegram_chat_id: str, max_chat_id: str, parsed: ParsedMessage
|
self, *, telegram_chat_id: str, max_chat_id: str, parsed: ParsedMessage
|
||||||
) -> int | None:
|
) -> int | None:
|
||||||
|
|||||||
+52
-3
@@ -1,11 +1,34 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
class TelegramApiError(RuntimeError):
|
class TelegramApiError(RuntimeError):
|
||||||
pass
|
def __init__(
|
||||||
|
self,
|
||||||
|
message: str,
|
||||||
|
*,
|
||||||
|
method: str | None = None,
|
||||||
|
status_code: int | None = None,
|
||||||
|
error_code: int | None = None,
|
||||||
|
description: str | None = None,
|
||||||
|
parameters: dict[str, Any] | None = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(message)
|
||||||
|
self.method = method
|
||||||
|
self.status_code = status_code
|
||||||
|
self.error_code = error_code
|
||||||
|
self.description = description
|
||||||
|
self.parameters = parameters or {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def migrate_to_chat_id(self) -> str | None:
|
||||||
|
value = self.parameters.get("migrate_to_chat_id")
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
class TelegramClient:
|
class TelegramClient:
|
||||||
@@ -209,10 +232,36 @@ class TelegramClient:
|
|||||||
|
|
||||||
response = await asyncio.to_thread(_do_request)
|
response = await asyncio.to_thread(_do_request)
|
||||||
if response.status_code >= 400:
|
if response.status_code >= 400:
|
||||||
|
error_code: int | None = None
|
||||||
|
description: str | None = None
|
||||||
|
parameters: dict[str, Any] = {}
|
||||||
|
try:
|
||||||
|
payload_data = response.json()
|
||||||
|
if isinstance(payload_data, dict):
|
||||||
|
if isinstance(payload_data.get("error_code"), int):
|
||||||
|
error_code = payload_data.get("error_code")
|
||||||
|
if isinstance(payload_data.get("description"), str):
|
||||||
|
description = payload_data.get("description")
|
||||||
|
if isinstance(payload_data.get("parameters"), dict):
|
||||||
|
parameters = payload_data.get("parameters", {})
|
||||||
|
except (json.JSONDecodeError, ValueError):
|
||||||
|
payload_data = None
|
||||||
|
|
||||||
raise TelegramApiError(
|
raise TelegramApiError(
|
||||||
f"Telegram HTTP error on {method}: {response.status_code} {response.text}"
|
f"Telegram HTTP error on {method}: {response.status_code} {response.text}",
|
||||||
|
method=method,
|
||||||
|
status_code=response.status_code,
|
||||||
|
error_code=error_code,
|
||||||
|
description=description,
|
||||||
|
parameters=parameters,
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
if not data.get("ok"):
|
if not data.get("ok"):
|
||||||
raise TelegramApiError(f"Telegram API error on {method}: {data}")
|
raise TelegramApiError(
|
||||||
|
f"Telegram API error on {method}: {data}",
|
||||||
|
method=method,
|
||||||
|
error_code=data.get("error_code") if isinstance(data.get("error_code"), int) else None,
|
||||||
|
description=data.get("description") if isinstance(data.get("description"), str) else None,
|
||||||
|
parameters=data.get("parameters") if isinstance(data.get("parameters"), dict) else None,
|
||||||
|
)
|
||||||
return data
|
return data
|
||||||
|
|||||||
Reference in New Issue
Block a user