diff --git a/thon/__init__.py b/thon/__init__.py index 769a9e6..f2c8453 100644 --- a/thon/__init__.py +++ b/thon/__init__.py @@ -26,8 +26,21 @@ class Thon: async with ProcessThon(session, self.__options) as thon: if isinstance(thon, str): if "Banned" in thon and self.__move_banned_folder: - await move(session.item) - await move(session.json_file) + print( + f"{session.item.name} | Аккаунт забанен, перемещение файлов..." + ) + # Перемещаем файлы сессии с обработкой ошибок + success_session = await move(session.item, "banned") + success_json = await move(session.json_file, "banned") + + if success_session and success_json: + print( + f"{session.item.name} | Файлы успешно перемещены / Files moved successfully" + ) + else: + print( + f"{session.item.name} | Не удалось полностью переместить файлы / Failed to move all files" + ) if self.__callback_error is not None: self.__callback_error() continue diff --git a/thon/process.py b/thon/process.py index f38c3fe..6a33a58 100644 --- a/thon/process.py +++ b/thon/process.py @@ -59,7 +59,7 @@ class ProcessThon(Data): def __get_client(self) -> TelegramClient: __session = str(self.__item) if self.__item else self.string_session proxy = self.proxy - print(proxy) + self._logger.info(f"{self.session_file} | {proxy}") client = TelegramClient( session=__session, api_id=self.app_id, @@ -169,14 +169,22 @@ class ProcessThon(Data): return "" async def disconnect(self): + """Полное отключение клиента с очисткой ресурсов""" + # Сначала переводим в оффлайн (если есть) with contextlib.suppress(Exception): await self.client(UpdateStatusRequest(offline=True)) + + # Затем отключаем соединение (если есть) with contextlib.suppress(Exception): - await self.client.disconnect() # type: ignore # ty:ignore[unused-ignore-comment] + await self.client.disconnect() + + # Явно удаляем client из памяти + with contextlib.suppress(Exception): + del self.__client async def __aenter__(self) -> Self | str: r = await self.check() - print(r) + self._logger.info(f"{self.session_file} | {r}") if r != "OK": await self.disconnect() return r diff --git a/thon/utils.py b/thon/utils.py index f89c110..b4e7dfc 100644 --- a/thon/utils.py +++ b/thon/utils.py @@ -27,14 +27,59 @@ def extract_verification_code(text: str, regexp: str) -> str | None: return code if code.isdigit() else None -async def move(path: Path, move_folder_name: str = "banned"): +async def move( + path: Path, move_folder_name: str = "banned", max_retries: int = 5 +) -> bool: + """ + Перемещает файл с retry-логикой, если файл занят. + Возвращает True если успешно, False если не удалось. + """ _folder = Path(f"sessions/{move_folder_name}") __folder = Path(f"сессии/{move_folder_name}") if __folder.exists(): _folder = __folder _folder.mkdir(parents=True, exist_ok=True) + loop = asyncio.get_running_loop() - try: - await loop.run_in_executor(None, os.rename, path, _folder / path.name) - except OSError as e: - print(f"{path.name} | Error moving file / Не удалось переместить файл: {e}") + + for attempt in range(max_retries): + try: + # Проверяем, что файл существует и доступен для чтения + if not path.exists(): + print(f"{path.name} | Файл не найден / File not found") + return False + + if not path.is_file(): + print(f"{path.name} | Это не файл / Not a file") + return False + + # Попытка переместить файл + await loop.run_in_executor(None, os.rename, path, _folder / path.name) + return True + + except OSError: + if attempt < max_retries - 1: + # Ждем перед следующей попыткой (с экспоненциальной задержкой) + wait_time = 0.5 * (2**attempt) # 0.5s, 1s, 2s, 4s... + print( + f"{path.name} | Попытка {attempt + 1}/{max_retries}: файл занят, ждем {wait_time}s..." + ) + await asyncio.sleep(wait_time) + else: + # Последняя попытка неудачна - попробуем копию + print( + f"{path.name} | Ошибка перемещения / Failed to move (final attempt), trying copy..." + ) + try: + await loop.run_in_executor( + None, os.replace, path, _folder / path.name + ) + print(f"{path.name} | Файл перемещен через копию / Moved via copy") + return True + except OSError as e: + print( + f"{path.name} | Не удалось ни переместить, ни скопировать / Failed to move or copy: {e}" + ) + return False + + return False