From 60d67b613acbc05b27d72e7cb869f093d1257119 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 2 Aug 2026 00:44:16 +0500 Subject: [PATCH] refactor(logging): add detailed debug logs --- main.py | 9 +++- modules/checker.py | 85 +++++++++++++++++++++++----------- modules/tdata.py | 48 ++++++++++++------- modules/thread.py | 113 +++++++++++++++++++++++++++++---------------- 4 files changed, 172 insertions(+), 83 deletions(-) diff --git a/main.py b/main.py index 6b2eaae..b3bff8c 100644 --- a/main.py +++ b/main.py @@ -7,14 +7,17 @@ logger = logging.getLogger(__name__) async def main(): + logger.debug("Инициализация Worker") async with Worker() as worker: + logger.debug("Worker успешно инициализирован") await worker.check_all() + logger.debug("Worker завершил работу") if __name__ == "__main__": logging.basicConfig( - level=logging.INFO, - format="%(asctime)s | %(levelname)-7s | %(message)s", + level=logging.DEBUG, + format="%(asctime)s | %(levelname)-7s | %(name)-20s | %(message)s", datefmt="%H:%M:%S", handlers=[logging.StreamHandler()], ) @@ -23,7 +26,9 @@ if __name__ == "__main__": logging.getLogger("opentele2").setLevel(logging.WARNING) try: + logger.info("Запуск приложения TDATA-Checker") asyncio.run(main()) + logger.info("Приложение завершено") except KeyboardInterrupt: logger.info("Прервано пользователем") diff --git a/modules/checker.py b/modules/checker.py index 7612a30..f0537ce 100644 --- a/modules/checker.py +++ b/modules/checker.py @@ -1,6 +1,7 @@ import logging from dataclasses import dataclass +from pathlib import Path from phonenumbers import parse, geocoder from typing import List, Optional @@ -32,112 +33,144 @@ class Result: class Checker: - def __init__(self, path: str): + def __init__(self, path: Path, temp_path: Path): self.path = path + self.temp_path = temp_path self.client = None - logger.info(f"Инициализация проверки для пути: {path}") + logger.debug(f"Создание экземпляра Checker для пути: {path}") try: - self.desktop = TDATA(path=self.path) + self.desktop = TDATA(path=self.path, temp_path=self.temp_path) + logger.debug("TDATA успешно инициализирована") except Exception as e: logger.error(f"Ошибка инициализации TDATA: {e}") self.desktop = None async def __aenter__(self): - self.__ensure_client() + logger.debug("Вход в контекстный менеджер Checker") + await self.__ensure_client() return self async def __aexit__(self, exc_type, exc_val, exc_tb): + logger.debug("Выход из контекстного менеджера Checker") if self.client: - logger.debug("Отключение клиента Telethon") try: await self.client.disconnect() - + logger.debug("Telethon клиент отключен") except Exception as e: - logger.error(f"Ошибка отключения клиента Telethon: {e}") + logger.warning(f"Ошибка при отключении клиента: {e}") async def __ensure_client(self): if self.client is None: if self.desktop is None: + logger.error("Попытка инициализации клиента с неинициализированной TDATA") raise Exception("TDATA не инициализирована") - logger.debug("Инициализация клиента Telethon из TDATA") + logger.debug("Конвертация TDATA в Telethon клиент") self.client = await self.desktop.convert() + logger.debug("Telethon клиент успешно создан") async def check_stars(self, me: User) -> int: + logger.debug("Запрос баланса звезд") try: stars_status = await self.client(GetStarsStatusRequest(peer=me)) - return stars_status.balance.amount + stars = stars_status.balance.amount + logger.debug(f"Баланс звезд: {stars}") + return stars except Exception as e: logger.warning(f"Не удалось получить баланс звезд: {e}") return 0 async def check_country(self, me: User) -> str: + logger.debug(f"Определение страны для номера +{me.phone}") try: phone_parse = parse(str(f"+{me.phone}")) - return geocoder.description_for_number(phone_parse, "en") + country = geocoder.description_for_number(phone_parse, "ru") + logger.debug(f"Страна определена: {country}") + return country except Exception as e: logger.warning(f"Не удалось определить страну: {e}") return "N/A" async def check_contacts(self) -> List[User]: + logger.debug("Запрос списка контактов") try: - logger.debug("Получение контактов") result = await self.client(GetContactsRequest(hash=0)) - return result.users + contacts = result.users + logger.debug(f"Получено контактов: {len(contacts)}") + return contacts except Exception as e: logger.warning(f"Не удалось получить контакты: {e}") + return [] async def check_chats(self) -> List[Chat]: + logger.debug("Запрос всех диалогов") try: - logger.debug("Получение всех диалогов") chats = [] async for dialog in self.client.iter_dialogs(): chats.append(dialog.entity) + logger.debug(f"Получено диалогов: {len(chats)}") return chats except Exception as e: logger.warning(f"Не удалось получить чаты: {e}") + return [] async def check_channels(self, chats: List[Chat]) -> List[Channel]: + logger.debug(f"Фильтрация каналов из {len(chats)} чатов") try: channels = [chat for chat in chats if isinstance(chat, Channel)] + logger.debug(f"Найдено каналов: {len(channels)}") return channels except Exception as e: logger.warning(f"Не удалось отфильтровать каналы: {e}") + return [] async def check_admin_channels(self, channels: List[Channel]) -> List[Channel]: + logger.debug("Проверка прав администратора в каналах") try: - logger.debug("Проверка прав администратора") admin_channels = [] + checked = 0 for channel in channels: - permissions = await self.client.get_permissions(channel, "me") - if permissions.is_admin or permissions.is_creator: - admin_channels.append(channel) + checked += 1 + try: + permissions = await self.client.get_permissions(channel, "me") + if permissions.is_admin or permissions.is_creator: + admin_channels.append(channel) + logger.debug(f"Найден админ канал: {channel.title}") + except Exception as e: + logger.debug(f"Не удалось проверить права для канала: {e}") + + logger.debug(f"Проверено каналов: {checked}, найдено админских: {len(admin_channels)}") + return admin_channels except Exception as e: logger.warning(f"Не удалось проверить админские права: {e}") + return [] async def check_all(self) -> Result: + logger.debug("Начало полной проверки аккаунта") try: me = await self.client.get_me() + if not me: + logger.error("Не удалось получить информацию о пользователе") + raise SessionInvalid() - # Собираем данные с обработкой ошибок - stars = self.check_stars(me=me) - country = self.check_country(me=me) + logger.debug(f"Получен пользователь: {me.first_name} (+{me.phone})") - contacts = self.check_contacts() - chats = self.check_chats() - channels = [] if chats == [] else self.check_channels(chats=chats) - admin_channels = ( - [] if channels == [] else self.check_admin_channels(channels=channels) - ) + stars = await self.check_stars(me=me) + country = await self.check_country(me=me) + contacts = await self.check_contacts() + chats = await self.check_chats() + channels = await self.check_channels(chats=chats) if chats else [] + admin_channels = await self.check_admin_channels(channels=channels) if channels else [] + logger.debug("Полная проверка завершена успешно") return Result( me=me, stars=stars, diff --git a/modules/tdata.py b/modules/tdata.py index f162757..c902098 100644 --- a/modules/tdata.py +++ b/modules/tdata.py @@ -17,6 +17,7 @@ logger = logging.getLogger(__name__) class TDATA: def __init__(self, path: Path, temp_path: Path): + logger.debug(f"Инициализация TDATA с путем: {path}") try: self.path = path self.temp_path = temp_path @@ -27,21 +28,26 @@ class TDATA: self.desktop = None self.telethon = None - self.api = API.TelegramDesktop + + logger.debug("Загрузка TDesktop") self.desktop = self.__get_telegram_desktop() - logger.debug(f"TDATA инициализирована с путем: {self.path}") + if self.desktop: + logger.debug("TDesktop успешно загружен") except Exception as e: logger.error(f"Не удалось инициализировать TDATA: {e}") raise def __get_telegram_desktop(self) -> TDesktop | None: + logger.debug("Попытка загрузки TDesktop") try: if self.desktop: return self.desktop - return TDesktop(self.path.absolute(), api=self.api) + desktop = TDesktop(self.path.absolute(), api=self.api) + logger.debug("TDesktop создан успешно") + return desktop except TFileNotFound as e: logger.warning(f"Файлы TDATA не найдены или неполные: {e}") @@ -52,27 +58,35 @@ class TDATA: raise def __gen_session_path(self) -> str: - logger.debug("Генерация пути сессии") + logger.debug("Генерация уникального пути для сессии") tempPath = f"temp/{uuid4()}.session" while Path(tempPath).exists(): - logger.debug(f"Путь сессии '{tempPath}' существует, генерация нового") + logger.debug(f"Путь сессии '{tempPath}' уже существует, генерация нового") tempPath = f"temp/{uuid4()}.session" + logger.debug(f"Сгенерирован путь сессии: {tempPath}") return tempPath async def validate(self) -> bool: logger.debug("Валидация TDATA") if self.desktop is None: - logger.warning("TDesktop не инициализирован, TDATA невалидна") + logger.warning("TDesktop не инициализирован") return False - return self.desktop.isLoaded() + is_loaded = self.desktop.isLoaded() + if is_loaded: + logger.debug("TDATA содержит загруженные аккаунты") + else: + logger.warning("TDATA не содержит загруженных аккаунтов") - async def valid(self, client: TelegramClient, disconnect: bool = True) -> bool: + return is_loaded + + async def is_valid(self, client: TelegramClient, disconnect: bool = True) -> bool: + logger.debug("Проверка авторизации Telethon клиента") try: - logger.debug("Проверка авторизации клиента Telethon") await client.connect() + logger.debug("Подключение к Telegram установлено") authorized = await client.is_user_authorized() @@ -84,8 +98,8 @@ class TDATA: self.telethon = client return True - except: - logger.exception("Не удалось валидировать клиент") + except Exception as e: + logger.error(f"Не удалось валидировать клиент: {e}") return False finally: @@ -94,26 +108,28 @@ class TDATA: await client.disconnect() async def convert(self) -> TelegramClient: - logger.debug("Конвертация TDATA в сессию Telethon") + logger.debug("Начало конвертации TDATA в Telethon сессию") if not await self.validate(): + logger.error("TDATA невалидна, конвертация невозможна") raise TDesktopNotLoaded("TDATA невалидна или не авторизована") tempPath = self.__gen_session_path() try: - logger.debug(f"Создание сессии Telethon по пути '{tempPath}'") + logger.debug(f"Создание Telethon сессии по пути '{tempPath}'") client = await self.desktop.ToTelethon( session=tempPath, flag=UseCurrentSession, api=self.api ) - valid_status = await self.valid(client, disconnect=False) + logger.debug("Валидация созданной сессии") + valid_status = await self.is_valid(client, disconnect=False) if not valid_status: logger.error("Созданная сессия невалидна") raise TDesktopUnauthorized("TDATA невалидна или не авторизована") - logger.debug(f"Успешно конвертировано в сессию: {tempPath}") + logger.debug(f"Конвертация успешна: {tempPath}") return self.telethon except Exception as e: - logger.exception(f"Не удалось конвертировать TDATA: {e}") + logger.error(f"Не удалось конвертировать TDATA: {e}") raise diff --git a/modules/thread.py b/modules/thread.py index 4398d1e..c696bbe 100644 --- a/modules/thread.py +++ b/modules/thread.py @@ -5,7 +5,7 @@ from shutil import move from pathlib import Path from countryflag import getflag -from checker import Result, Checker, SessionInvalid +from modules.checker import Result, Checker, SessionInvalid logger = logging.getLogger(__name__) @@ -17,6 +17,7 @@ class Worker: output_path: str = "data", temp_path: str = "temp", ): + logger.debug(f"Инициализация Worker с путями: input={input_path}, output={output_path}, temp={temp_path}") self.input_path = Path(input_path).absolute() self.output_path = Path(output_path).absolute() self.temp_path = Path(temp_path).absolute() @@ -25,52 +26,65 @@ class Worker: self.invalid_path = self.output_path.joinpath("invalid") self.all_path = self.valid_path.joinpath("all.txt") + logger.debug(f"Пути настроены: valid={self.valid_path}, invalid={self.invalid_path}") async def __aenter__(self): + logger.debug("Вход в контекстный менеджер Worker") + logger.debug("Создание необходимых директорий") + if not self.input_path.exists(): self.input_path.mkdir() + logger.debug(f"Создана директория: {self.input_path}") + if not self.output_path.exists(): self.output_path.mkdir() + logger.debug(f"Создана директория: {self.output_path}") if not self.temp_path.exists(): self.temp_path.mkdir() + logger.debug(f"Создана директория: {self.temp_path}") else: + logger.debug("Очистка существующей временной директории") self.__cleanup() if not self.valid_path.exists(): self.valid_path.mkdir() + logger.debug(f"Создана директория: {self.valid_path}") + if not self.invalid_path.exists(): self.invalid_path.mkdir() + logger.debug(f"Создана директория: {self.invalid_path}") + logger.debug("Все директории готовы") return self async def __aexit__(self, exc_type, exc_val, exc_tb): + logger.debug("Выход из контекстного менеджера Worker") self.__cleanup() - if self.client: - logger.debug("Отключение клиента Telethon") - try: - await self.client.disconnect() - except: - pass def __cleanup(self): + logger.debug("Начало очистки временных файлов") if self.temp_path.exists(): deleted_count = 0 for file in os.listdir(path=self.temp_path): file_path = self.temp_path.joinpath(str(file)) try: - if file_path.isfile() and ".gitinclude" not in file_path: + if file_path.is_file() and ".gitinclude" not in str(file_path): file_path.unlink(missing_ok=True) - elif file_path.isdir(): + deleted_count += 1 + elif file_path.is_dir(): file_path.rmdir() - deleted_count += 1 + deleted_count += 1 except Exception as e: - logger.warning(f"Не удалось удалить {file_path}: {e}") + logger.debug(f"Не удалось удалить {file_path}: {e}") if deleted_count > 0: - logger.debug(f"Удалено {deleted_count} временных файлов") + logger.debug(f"Удалено временных файлов: {deleted_count}") + else: + logger.debug("Нет временных файлов для удаления") def __list_tdata(self): + logger.debug(f"Поиск TDATA папок в {self.input_path}") tdatas = [] if self.input_path.exists(): @@ -78,10 +92,13 @@ class Worker: joined = self.input_path.joinpath(content) if joined.is_dir(): tdatas.append(joined.absolute()) + logger.debug(f"Найдена TDATA папка: {joined}") + logger.debug(f"Всего найдено TDATA папок: {len(tdatas)}") return tdatas def __template(self, result: Result): + logger.debug(f"Создание шаблона для пользователя {result.me.id}") return ( f"👤 [{getflag(countries=result.country)} {result.country}] {result.me.phone} — {result.me.id}\n" f"⭐️ {result.stars}\n" @@ -91,62 +108,80 @@ class Worker: ) def __append(self, content: str, path: Path): - with open(file=path, mode="+a", encoding="utf-8") as file: - file.write(content) + logger.debug(f"Добавление данных в файл: {path}") + with open(file=path, mode="a", encoding="utf-8") as file: + file.write(content + "\n\n") def __write(self, content: str, path: Path): - with open(file=path, mode="+w", encoding="utf-8") as file: + logger.debug(f"Запись данных в файл: {path}") + with open(file=path, mode="w", encoding="utf-8") as file: file.write(content) def __save_valid(self, user_id: int, result: Result, tdata: Path): - id_path = self.output_path.joinpath(str(user_id)) + logger.debug(f"Сохранение валидной TDATA для пользователя {user_id}") + id_path = self.valid_path.joinpath(str(user_id)) if not id_path.absolute().exists(): id_path.mkdir() + logger.debug(f"Создана директория для пользователя: {id_path}") content = self.__template(result=result) id_txt_path = id_path.joinpath(f"{user_id}.txt") - # Append to all.txt + logger.debug("Добавление в общий файл all.txt") self.__append(content=content, path=self.all_path) - # Write to id.txt + logger.debug("Запись в индивидуальный файл") self.__write(content=content, path=id_txt_path) - tdata_old_path = self.input_path.joinpath(tdata).absolute() + tdata_old_path = tdata + tdata_relative = tdata.relative_to(self.input_path) + tdata_new_path = id_path.joinpath(tdata_relative.name) - tdata_relative_id_path = id_path.joinpath(tdata) - tdata_new_path = self.valid_path.joinpath(tdata_relative_id_path).absolute() - - # Move tdata - move(src=tdata_old_path, dst=tdata_new_path) + logger.debug(f"Перемещение TDATA: {tdata_old_path} -> {tdata_new_path}") + move(src=str(tdata_old_path), dst=str(tdata_new_path)) + logger.info(f"Валидная TDATA сохранена для пользователя {user_id}") def __save_invalid(self, tdata: Path): - tdata_old_path = self.input_path.joinpath(tdata).absolute() - tdata_new_path = self.invalid_path.joinpath(tdata).absolute() + logger.debug(f"Сохранение невалидной TDATA: {tdata}") + tdata_old_path = tdata + tdata_relative = tdata.relative_to(self.input_path) + tdata_new_path = self.invalid_path.joinpath(tdata_relative.name) - # Move tdata - move(src=tdata_old_path, dst=tdata_new_path) + logger.debug(f"Перемещение TDATA: {tdata_old_path} -> {tdata_new_path}") + move(src=str(tdata_old_path), dst=str(tdata_new_path)) + logger.info(f"Невалидная TDATA перемещена: {tdata_relative.name}") - async def check_one(self, tdata: str): - tdata_absolute = Path(tdata).absolute() - tdata_relative = Path(tdata).relative_to(self.input_path) + async def check_one(self, tdata: Path): + tdata_absolute = tdata.absolute() + logger.info(f"Проверка TDATA: {tdata.name}") - logger.info(f"Начало проверки TDATA: {tdata_absolute}") try: - async with Checker(path=tdata_absolute) as checker: + async with Checker(path=tdata_absolute, temp_path=self.temp_path) as checker: result = await checker.check_all() - return - self.__save_valid(user_id=result.me.id, tdata=tdata_relative) + logger.info(f"Проверка завершена: {result.me.first_name} (+{result.me.phone})") + self.__save_valid(user_id=result.me.id, result=result, tdata=tdata_absolute) except SessionInvalid: - self.__save_invalid(tdata=tdata_relative) + logger.warning(f"TDATA невалидна: {tdata.name}") + self.__save_invalid(tdata=tdata_absolute) - except: - pass + except Exception as e: + logger.error(f"Ошибка при проверке TDATA {tdata.name}: {e}") + self.__save_invalid(tdata=tdata_absolute) async def check_all(self): + logger.info("Начало массовой проверки TDATA") tdatas = self.__list_tdata() - for tdata in tdatas: + if not tdatas: + logger.warning("TDATA папки не найдены") + return + + logger.info(f"Найдено TDATA для проверки: {len(tdatas)}") + + for index, tdata in enumerate(tdatas, 1): + logger.info(f"[{index}/{len(tdatas)}] Проверка: {tdata.name}") await self.check_one(tdata=tdata) + + logger.info("Массовая проверка завершена")