reverse bridge
This commit is contained in:
+14
-8
@@ -35,15 +35,21 @@ class MaxToTelegramBridge:
|
|||||||
logger.debug("Skip empty message %s/%s", parsed.chat_id, parsed.message_id)
|
logger.debug("Skip empty message %s/%s", parsed.chat_id, parsed.message_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
target_chat_id, matched_by_title = await self._telegram.resolve_target_chat_id(parsed.chat_name)
|
normalized = parsed.chat_name.strip().casefold()
|
||||||
if matched_by_title:
|
routed = self._storage.get_chat_route(max_chat_title_norm=normalized)
|
||||||
logger.info("Route Max chat '%s' to Telegram chat %s", parsed.chat_name, target_chat_id)
|
if routed:
|
||||||
|
target_chat_id = routed
|
||||||
|
logger.info("Route Max chat '%s' to Telegram chat %s (bound)", parsed.chat_name, target_chat_id)
|
||||||
else:
|
else:
|
||||||
logger.info(
|
target_chat_id, matched_by_title = await self._telegram.resolve_target_chat_id(parsed.chat_name)
|
||||||
"Telegram chat '%s' not found, route to fallback user %s",
|
if matched_by_title:
|
||||||
parsed.chat_name,
|
logger.info("Route Max chat '%s' to Telegram chat %s", parsed.chat_name, target_chat_id)
|
||||||
target_chat_id,
|
else:
|
||||||
)
|
logger.info(
|
||||||
|
"Telegram chat '%s' not found, route to fallback user %s",
|
||||||
|
parsed.chat_name,
|
||||||
|
target_chat_id,
|
||||||
|
)
|
||||||
|
|
||||||
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:
|
||||||
|
|||||||
@@ -99,6 +99,13 @@ class TelegramToMaxBridge:
|
|||||||
if not isinstance(chat, dict):
|
if not isinstance(chat, dict):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Команда привязки чата Telegram к названию чата в MAX (для Max->Telegram маршрутизации).
|
||||||
|
# Работает даже при privacy mode, т.к. команды приходят боту.
|
||||||
|
text = str(message.get("text") or "").strip()
|
||||||
|
if text.startswith("/bind_max"):
|
||||||
|
await self._handle_bind_max_command(message, chat)
|
||||||
|
return
|
||||||
|
|
||||||
chat_title = _telegram_chat_title(chat)
|
chat_title = _telegram_chat_title(chat)
|
||||||
normalized = _normalize_title(chat_title)
|
normalized = _normalize_title(chat_title)
|
||||||
if not normalized:
|
if not normalized:
|
||||||
@@ -132,6 +139,37 @@ class TelegramToMaxBridge:
|
|||||||
media_group_id=None,
|
media_group_id=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _handle_bind_max_command(self, message: dict[str, Any], chat: dict[str, Any]) -> None:
|
||||||
|
raw = str(message.get("text") or "").strip()
|
||||||
|
# формат: /bind_max <точное название чата в MAX>
|
||||||
|
parts = raw.split(maxsplit=1)
|
||||||
|
if len(parts) < 2 or not parts[1].strip():
|
||||||
|
logger.error("bind_max: missing MAX chat title. Use: /bind_max <MAX chat title>")
|
||||||
|
return
|
||||||
|
|
||||||
|
max_title = parts[1].strip()
|
||||||
|
norm = _normalize_title(max_title)
|
||||||
|
|
||||||
|
# проверяем, что чат существует в MAX
|
||||||
|
max_chat_id = self._resolve_max_chat_id_by_title(norm)
|
||||||
|
if max_chat_id is None:
|
||||||
|
logger.error("bind_max: MAX чат '%s' не найден — привязку не сохраняю", max_title)
|
||||||
|
return
|
||||||
|
|
||||||
|
telegram_chat_id = str(chat.get("id"))
|
||||||
|
telegram_title = _telegram_chat_title(chat)
|
||||||
|
self._storage.set_chat_route(
|
||||||
|
max_chat_title_norm=norm,
|
||||||
|
telegram_chat_id=telegram_chat_id,
|
||||||
|
telegram_chat_title=telegram_title,
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"bind_max: bound MAX '%s' -> Telegram '%s' (%s)",
|
||||||
|
max_title,
|
||||||
|
telegram_title,
|
||||||
|
telegram_chat_id,
|
||||||
|
)
|
||||||
|
|
||||||
async def _flush_ready_media_groups(self) -> None:
|
async def _flush_ready_media_groups(self) -> None:
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
ready: list[tuple[tuple[str, str], _MediaGroupBuffer]] = []
|
ready: list[tuple[tuple[str, str], _MediaGroupBuffer]] = []
|
||||||
|
|||||||
@@ -41,6 +41,16 @@ class BridgeStorage:
|
|||||||
conn.execute(
|
conn.execute(
|
||||||
"CREATE INDEX IF NOT EXISTS idx_message_mapping_media_group ON message_mapping (telegram_chat_id, media_group_id)"
|
"CREATE INDEX IF NOT EXISTS idx_message_mapping_media_group ON message_mapping (telegram_chat_id, media_group_id)"
|
||||||
)
|
)
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS chat_routes (
|
||||||
|
max_chat_title_norm TEXT PRIMARY KEY,
|
||||||
|
telegram_chat_id TEXT NOT NULL,
|
||||||
|
telegram_chat_title TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
def was_forwarded(self, message_id: str, chat_id: str) -> bool:
|
def was_forwarded(self, message_id: str, chat_id: str) -> bool:
|
||||||
@@ -94,3 +104,30 @@ class BridgeStorage:
|
|||||||
if not row:
|
if not row:
|
||||||
return None
|
return None
|
||||||
return str(row[0])
|
return str(row[0])
|
||||||
|
|
||||||
|
def set_chat_route(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
max_chat_title_norm: str,
|
||||||
|
telegram_chat_id: str,
|
||||||
|
telegram_chat_title: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
with closing(self._connect()) as conn:
|
||||||
|
conn.execute(
|
||||||
|
"""
|
||||||
|
INSERT OR REPLACE INTO chat_routes (max_chat_title_norm, telegram_chat_id, telegram_chat_title)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""",
|
||||||
|
(max_chat_title_norm, telegram_chat_id, telegram_chat_title),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def get_chat_route(self, *, max_chat_title_norm: str) -> str | None:
|
||||||
|
with closing(self._connect()) as conn:
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT telegram_chat_id FROM chat_routes WHERE max_chat_title_norm = ?",
|
||||||
|
(max_chat_title_norm,),
|
||||||
|
).fetchone()
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
return str(row[0])
|
||||||
|
|||||||
Reference in New Issue
Block a user