From e438f096932aee2419331c7456cbfd22ea0c7135 Mon Sep 17 00:00:00 2001 From: Yuriy Yuriev Date: Fri, 29 May 2026 22:06:34 +0700 Subject: [PATCH] fix delete old state --- handlers/commands.py | 2 ++ main.py | 29 ++++++++++++++++++++++++----- managers/storage.py | 22 +++++++++++++++------- services/browser_service.py | 13 +++++++++++-- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/handlers/commands.py b/handlers/commands.py index 64e9691..852feba 100644 --- a/handlers/commands.py +++ b/handlers/commands.py @@ -2248,6 +2248,8 @@ class BotInterface: params.successful_visits += 1 await self.balance_storage.deduct(params.user_id, 1) logger.info(f"✅ Visit {params.successful_visits}/{max_v}: {params.url[:40]}") + if self.storage: + await self.storage.save_task(task_id, params) else: logger.warning(f"❌ Visit failed ({params.total_visits}): {result.error or 'unknown'}") except asyncio.CancelledError: diff --git a/main.py b/main.py index 84eae07..2a3a675 100644 --- a/main.py +++ b/main.py @@ -42,7 +42,8 @@ class BotApplication: def __init__(self): self.bot: Bot = None self.dispatcher: Dispatcher = None - + self._initialized = False # True только после успешного _restore_tasks + self.background_tasks = BackgroundTaskManager() self.browser_service = BrowserService() self.browser_pool = BrowserPool(settings.BROWSER_POOL_SIZE, self.browser_service) @@ -101,8 +102,24 @@ class BotApplication: # Восстанавливаем задачи await self._restore_tasks() + self._initialized = True + + # Периодическое сохранение на случай аварийного завершения + asyncio.create_task(self._autosave_loop(), name="autosave") + logger.info("Bot initialized!") + async def _autosave_loop(self): + """Сохраняет состояние задач каждые 60 секунд.""" + while True: + await asyncio.sleep(60) + try: + tasks = dict(self.interface.task_manager._tasks) + await self.storage.save_tasks(tasks) + logger.debug(f"Autosaved {len(tasks)} tasks") + except Exception as e: + logger.warning(f"Autosave failed: {e}") + async def _restore_tasks(self): """Восстанавливает задачи после перезапуска.""" saved_tasks = await self.storage.load_tasks() @@ -173,10 +190,12 @@ class BotApplication: async def stop(self): """Остановка с сохранением.""" logger.info("Shutting down...") - - # Сохраняем задачи - tasks = self.interface.task_manager._tasks - await self.storage.save_tasks(tasks) + + # Сохраняем только если инициализация прошла успешно — + # иначе task_manager пустой и мы затрём уже сохранённые задачи + if self._initialized: + tasks = self.interface.task_manager._tasks + await self.storage.save_tasks(tasks) await self.background_tasks.cancel_all() await self.browser_pool.stop() diff --git a/managers/storage.py b/managers/storage.py index 1eb47e2..9fbf9b7 100644 --- a/managers/storage.py +++ b/managers/storage.py @@ -135,15 +135,23 @@ class TaskStorage: return {} async def save_task(self, task_id: str, params: TaskParams) -> None: - tasks = await self.load_tasks() - tasks[task_id] = params - await self.save_tasks(tasks) + async with self._lock: + try: + data = await asyncio.to_thread(self._load_sync) + data[task_id] = self._params_to_dict(params) + await asyncio.to_thread(self._save_sync, data) + except Exception as e: + logger.error(f"save_task error: {e}") async def delete_task(self, task_id: str) -> None: - tasks = await self.load_tasks() - if task_id in tasks: - del tasks[task_id] - await self.save_tasks(tasks) + async with self._lock: + try: + data = await asyncio.to_thread(self._load_sync) + if task_id in data: + del data[task_id] + await asyncio.to_thread(self._save_sync, data) + except Exception as e: + logger.error(f"delete_task error: {e}") class ChatStorage: diff --git a/services/browser_service.py b/services/browser_service.py index 18c0114..85c4d06 100644 --- a/services/browser_service.py +++ b/services/browser_service.py @@ -165,6 +165,9 @@ class BrowserService: if not settings.STICKY_PROXY_HOST: return VisitResult(url=url, success=False, error="No proxy configured") + # Максимальное время одной попытки: запуск браузера + загрузка страницы + чтение + буфер + attempt_timeout = reading_time + 150 + for attempt in range(3): try: proxy_config, proxy_url, proxy_info = _make_sticky_proxy_config() @@ -185,8 +188,11 @@ class BrowserService: if proxy_locale: camoufox_kwargs["locale"] = proxy_locale - async with AsyncCamoufox(**camoufox_kwargs) as browser: - result = await self._browse_page(browser, url, proxy_info, reading_time) + async def _run_browser(): + async with AsyncCamoufox(**camoufox_kwargs) as browser: + return await self._browse_page(browser, url, proxy_info, reading_time) + + result = await asyncio.wait_for(_run_browser(), timeout=attempt_timeout) is_proxy_dead = result.error and any( k in result.error for k in ("502", "Bad Gateway", "NS_ERROR_PROXY") @@ -204,6 +210,9 @@ class BrowserService: self._proxy_fail_streak = 0 return result + except asyncio.TimeoutError: + logger.warning(f"Visit attempt {attempt + 1} timed out after {attempt_timeout}s, retrying") + except (ValueError, InvalidIP, InvalidProxy) as e: logger.warning(f"Visit attempt {attempt + 1} proxy error, retrying: {e}")