Files
max2telegram/src/main.py
T

101 lines
3.6 KiB
Python

import asyncio
import logging
from pymax import MaxClient, Message
from dotenv import load_dotenv
from bridge import MaxToTelegramBridge
from config import load_settings
from health import HealthState
from health_web import start_health_server
from reverse_bridge import TelegramToMaxBridge
from storage import BridgeStorage
from telegram_api import TelegramClient
def _setup_logging() -> None:
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
def build_client() -> tuple[MaxClient, MaxToTelegramBridge]:
settings = load_settings()
max_client = MaxClient(
phone=settings.max_phone,
work_dir=settings.max_work_dir,
)
telegram_client = TelegramClient(
bot_token=settings.telegram_bot_token,
fallback_user_id=settings.telegram_fallback_user_id,
)
storage = BridgeStorage(settings.sqlite_path)
bridge = MaxToTelegramBridge(max_client=max_client, telegram=telegram_client, storage=storage)
return max_client, bridge
def main() -> None:
load_dotenv()
_setup_logging()
settings = load_settings()
max_client = MaxClient(phone=settings.max_phone, work_dir=settings.max_work_dir)
telegram_client = TelegramClient(
bot_token=settings.telegram_bot_token,
fallback_user_id=settings.telegram_fallback_user_id,
)
storage = BridgeStorage(settings.sqlite_path)
bridge = MaxToTelegramBridge(max_client=max_client, telegram=telegram_client, storage=storage)
health = HealthState(unhealthy_after_sec=15 * 60)
start_health_server(host="0.0.0.0", port=5000, health=health)
reverse_bridge = TelegramToMaxBridge(max_client=max_client, telegram=telegram_client, storage=storage, health=health)
logger = logging.getLogger("max2telegram")
reverse_bridge_task: asyncio.Task[None] | None = None
max_probe_task: asyncio.Task[None] | None = None
@max_client.on_start
async def on_start() -> None:
nonlocal reverse_bridge_task, max_probe_task
logger.info("Max client started as %s", max_client.me.id)
health.mark_max_ok()
if reverse_bridge_task is None or reverse_bridge_task.done():
reverse_bridge_task = asyncio.create_task(reverse_bridge.start(), name="reverse-bridge")
if max_probe_task is None or max_probe_task.done():
max_probe_task = asyncio.create_task(
_max_probe_loop(max_client=max_client, health=health),
name="max-probe-loop",
)
@max_client.on_message()
async def on_message(message: Message) -> None:
health.mark_max_event()
try:
await bridge.forward_message(message)
except Exception:
logger.exception("Failed to forward Max message")
asyncio.run(max_client.start())
async def _max_probe_loop(*, max_client: MaxClient, health: HealthState) -> None:
# Best-effort контроль соединения: периодически дергаем API.
# Если PyMax разорвет соединение/сломается сессия, это обычно проявится как исключение.
await asyncio.sleep(2)
while True:
try:
me = getattr(max_client, "me", None)
my_id = getattr(me, "id", None)
if my_id is not None:
await max_client.get_user(user_id=my_id)
health.mark_max_ok()
except Exception:
health.mark_max_error()
logger = logging.getLogger("max2telegram")
logger.exception("Max probe failed")
await asyncio.sleep(60)
if __name__ == "__main__":
main()