diff --git a/.gitignore b/.gitignore index f6c526d..f0ae3a4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ __pycache__/ *.db *.sqlite *.sqlite3 +*.session +*.session-journal .venv/ env/ diff --git a/README.md b/README.md index 33e0fd5..c68f90b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ OverUB is a modular Telegram userbot built around a plugin-first architecture. 1. Install dependencies: `pip install -r requirements.txt` 2. Edit `config/config.yml` 3. Run: `python -m __main__` +4. Optional: set `bot.login_mode` to `qr` for QR login ## CLI - `python -m __main__ create-plugin ` diff --git a/core/app.py b/core/app.py index c79005f..0c85a2b 100644 --- a/core/app.py +++ b/core/app.py @@ -112,7 +112,11 @@ class OverUB: await self.events.emit("on_startup") await self.database.connect() await self.migrations.apply(self) - await self.client.connect() + bot_cfg = self.config.get().get("bot", {}) + await self.client.connect( + login_mode=str(bot_cfg.get("login_mode", "phone")), + phone=str(bot_cfg.get("phone", "")), + ) await self.client.attach_handlers(self) await self._load_builtin_modules() await self._load_plugins() diff --git a/core/client.py b/core/client.py index c92990e..1f330bc 100644 --- a/core/client.py +++ b/core/client.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any +import asyncio +from typing import Any, Optional from core.logger import get_logger @@ -15,14 +16,33 @@ class ClientWrapper: self.session_name = session_name self.client: Any = None - async def connect(self) -> None: + async def connect(self, login_mode: str = "phone", phone: Optional[str] = None) -> None: try: from telethon import TelegramClient + from telethon.errors import SessionPasswordNeededError except ImportError as exc: raise RuntimeError("Telethon not installed") from exc self.client = TelegramClient(self.session_name, self.api_id, self.api_hash) - await self.client.start() + if login_mode == "qr": + await self.client.connect() + if await self.client.is_user_authorized(): + logger.info("Telethon client already authorized") + return + try: + qr = await self.client.qr_login() + except AttributeError: + 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) + try: + await qr.wait() + except SessionPasswordNeededError: + password = await self._prompt("Two-step verification enabled. Enter password: ") + await self.client.sign_in(password=password) + else: + await self.client.start(phone=phone) logger.info("Telethon client connected") async def attach_handlers(self, app: Any) -> None: @@ -98,3 +118,10 @@ class ClientWrapper: async def wait_until_disconnected(self) -> None: if self.client: await self.client.disconnected + + def _print_qr(self, url: str) -> None: + logger.info("Scan this QR login URL with Telegram: %s", url) + + async def _prompt(self, prompt: str) -> str: + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, input, prompt)