From c0fca009b861c6c8a99e2263c8c85649922a1741 Mon Sep 17 00:00:00 2001 From: overspend1 Date: Sun, 21 Dec 2025 17:40:11 +0100 Subject: [PATCH] Allow saving/opening QR login image --- core/app.py | 2 ++ core/client.py | 46 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/core/app.py b/core/app.py index 0c85a2b..509bc35 100644 --- a/core/app.py +++ b/core/app.py @@ -116,6 +116,8 @@ class OverUB: await self.client.connect( login_mode=str(bot_cfg.get("login_mode", "phone")), phone=str(bot_cfg.get("phone", "")), + qr_path=bot_cfg.get("login_qr_path"), + qr_open=bool(bot_cfg.get("login_qr_open", False)), ) await self.client.attach_handlers(self) await self._load_builtin_modules() diff --git a/core/client.py b/core/client.py index 971624a..25df515 100644 --- a/core/client.py +++ b/core/client.py @@ -1,6 +1,9 @@ from __future__ import annotations import asyncio +import subprocess +import sys +from pathlib import Path from typing import Any, Optional from core.logger import get_logger @@ -16,7 +19,13 @@ class ClientWrapper: self.session_name = session_name self.client: Any = None - async def connect(self, login_mode: str = "phone", phone: Optional[str] = None) -> None: + async def connect( + self, + login_mode: str = "phone", + phone: Optional[str] = None, + qr_path: Optional[str] = None, + qr_open: bool = False, + ) -> None: try: from telethon import TelegramClient from telethon.errors import SessionPasswordNeededError @@ -35,7 +44,7 @@ class ClientWrapper: logger.warning("QR login not supported by this Telethon version, falling back to phone login") await self.client.start(phone=phone) return - self._print_qr(qr.url) + self._print_qr(qr.url, qr_path=qr_path, qr_open=qr_open) try: await qr.wait() except SessionPasswordNeededError: @@ -119,10 +128,13 @@ class ClientWrapper: if self.client: await self.client.disconnected - def _print_qr(self, url: str) -> None: + def _print_qr(self, url: str, qr_path: Optional[str] = None, qr_open: bool = False) -> None: logger.info("QR login URL: %s", url) print("Telegram app -> Settings -> Devices -> Scan QR") print(f"If you cannot scan, try opening this URL on your phone: {url}") + saved_path = None + if qr_path: + saved_path = self._save_qr_image(url, qr_path) try: import qrcode @@ -131,7 +143,33 @@ class ClientWrapper: qr.make(fit=True) qr.print_ascii(invert=True) except Exception: - return + pass + if qr_open and saved_path: + self._open_file(saved_path) + + def _save_qr_image(self, url: str, qr_path: str) -> Optional[Path]: + try: + import qrcode + except Exception: + logger.warning("qrcode package not installed; cannot save QR image") + return None + path = Path(qr_path) + path.parent.mkdir(parents=True, exist_ok=True) + img = qrcode.make(url) + img.save(path) + logger.info("QR image saved to %s", path) + return path + + def _open_file(self, path: Path) -> None: + try: + if sys.platform.startswith("darwin"): + subprocess.run(["open", str(path)], check=False) + elif sys.platform.startswith("win"): + subprocess.run(["cmd", "/c", "start", "", str(path)], check=False) + else: + subprocess.run(["xdg-open", str(path)], check=False) + except Exception: + logger.warning("Failed to open QR image automatically") async def _prompt(self, prompt: str) -> str: loop = asyncio.get_event_loop()