refactor(logging): add detailed debug logs
This commit is contained in:
@@ -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("Прервано пользователем")
|
||||
|
||||
+59
-26
@@ -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,
|
||||
|
||||
+32
-16
@@ -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
|
||||
|
||||
+74
-39
@@ -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("Массовая проверка завершена")
|
||||
|
||||
Reference in New Issue
Block a user