reverse bridge

This commit is contained in:
kislovdm
2026-04-09 12:08:10 +03:00
parent bd0daf5054
commit b5b5bc8b72
3 changed files with 89 additions and 8 deletions
+6
View File
@@ -35,6 +35,12 @@ class MaxToTelegramBridge:
logger.debug("Skip empty message %s/%s", parsed.chat_id, parsed.message_id)
return
normalized = parsed.chat_name.strip().casefold()
routed = self._storage.get_chat_route(max_chat_title_norm=normalized)
if routed:
target_chat_id = routed
logger.info("Route Max chat '%s' to Telegram chat %s (bound)", parsed.chat_name, target_chat_id)
else:
target_chat_id, matched_by_title = await self._telegram.resolve_target_chat_id(parsed.chat_name)
if matched_by_title:
logger.info("Route Max chat '%s' to Telegram chat %s", parsed.chat_name, target_chat_id)
+38
View File
@@ -99,6 +99,13 @@ class TelegramToMaxBridge:
if not isinstance(chat, dict):
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)
normalized = _normalize_title(chat_title)
if not normalized:
@@ -132,6 +139,37 @@ class TelegramToMaxBridge:
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:
now = time.monotonic()
ready: list[tuple[tuple[str, str], _MediaGroupBuffer]] = []
+37
View File
@@ -41,6 +41,16 @@ class BridgeStorage:
conn.execute(
"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()
def was_forwarded(self, message_id: str, chat_id: str) -> bool:
@@ -94,3 +104,30 @@ class BridgeStorage:
if not row:
return None
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])