refactor(logging): add detailed debug logs

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