fix delete old state

This commit is contained in:
Yuriy Yuriev
2026-05-29 22:06:34 +07:00
parent b02b7518cf
commit e438f09693
4 changed files with 52 additions and 14 deletions
+2
View File
@@ -2248,6 +2248,8 @@ class BotInterface:
params.successful_visits += 1 params.successful_visits += 1
await self.balance_storage.deduct(params.user_id, 1) await self.balance_storage.deduct(params.user_id, 1)
logger.info(f"✅ Visit {params.successful_visits}/{max_v}: {params.url[:40]}") logger.info(f"✅ Visit {params.successful_visits}/{max_v}: {params.url[:40]}")
if self.storage:
await self.storage.save_task(task_id, params)
else: else:
logger.warning(f"❌ Visit failed ({params.total_visits}): {result.error or 'unknown'}") logger.warning(f"❌ Visit failed ({params.total_visits}): {result.error or 'unknown'}")
except asyncio.CancelledError: except asyncio.CancelledError:
+24 -5
View File
@@ -42,7 +42,8 @@ class BotApplication:
def __init__(self): def __init__(self):
self.bot: Bot = None self.bot: Bot = None
self.dispatcher: Dispatcher = None self.dispatcher: Dispatcher = None
self._initialized = False # True только после успешного _restore_tasks
self.background_tasks = BackgroundTaskManager() self.background_tasks = BackgroundTaskManager()
self.browser_service = BrowserService() self.browser_service = BrowserService()
self.browser_pool = BrowserPool(settings.BROWSER_POOL_SIZE, self.browser_service) self.browser_pool = BrowserPool(settings.BROWSER_POOL_SIZE, self.browser_service)
@@ -101,8 +102,24 @@ class BotApplication:
# Восстанавливаем задачи # Восстанавливаем задачи
await self._restore_tasks() await self._restore_tasks()
self._initialized = True
# Периодическое сохранение на случай аварийного завершения
asyncio.create_task(self._autosave_loop(), name="autosave")
logger.info("Bot initialized!") 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): async def _restore_tasks(self):
"""Восстанавливает задачи после перезапуска.""" """Восстанавливает задачи после перезапуска."""
saved_tasks = await self.storage.load_tasks() saved_tasks = await self.storage.load_tasks()
@@ -173,10 +190,12 @@ class BotApplication:
async def stop(self): async def stop(self):
"""Остановка с сохранением.""" """Остановка с сохранением."""
logger.info("Shutting down...") logger.info("Shutting down...")
# Сохраняем задачи # Сохраняем только если инициализация прошла успешно —
tasks = self.interface.task_manager._tasks # иначе task_manager пустой и мы затрём уже сохранённые задачи
await self.storage.save_tasks(tasks) if self._initialized:
tasks = self.interface.task_manager._tasks
await self.storage.save_tasks(tasks)
await self.background_tasks.cancel_all() await self.background_tasks.cancel_all()
await self.browser_pool.stop() await self.browser_pool.stop()
+15 -7
View File
@@ -135,15 +135,23 @@ class TaskStorage:
return {} return {}
async def save_task(self, task_id: str, params: TaskParams) -> None: async def save_task(self, task_id: str, params: TaskParams) -> None:
tasks = await self.load_tasks() async with self._lock:
tasks[task_id] = params try:
await self.save_tasks(tasks) 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: async def delete_task(self, task_id: str) -> None:
tasks = await self.load_tasks() async with self._lock:
if task_id in tasks: try:
del tasks[task_id] data = await asyncio.to_thread(self._load_sync)
await self.save_tasks(tasks) 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: class ChatStorage:
+11 -2
View File
@@ -165,6 +165,9 @@ class BrowserService:
if not settings.STICKY_PROXY_HOST: if not settings.STICKY_PROXY_HOST:
return VisitResult(url=url, success=False, error="No proxy configured") return VisitResult(url=url, success=False, error="No proxy configured")
# Максимальное время одной попытки: запуск браузера + загрузка страницы + чтение + буфер
attempt_timeout = reading_time + 150
for attempt in range(3): for attempt in range(3):
try: try:
proxy_config, proxy_url, proxy_info = _make_sticky_proxy_config() proxy_config, proxy_url, proxy_info = _make_sticky_proxy_config()
@@ -185,8 +188,11 @@ class BrowserService:
if proxy_locale: if proxy_locale:
camoufox_kwargs["locale"] = proxy_locale camoufox_kwargs["locale"] = proxy_locale
async with AsyncCamoufox(**camoufox_kwargs) as browser: async def _run_browser():
result = await self._browse_page(browser, url, proxy_info, reading_time) 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( is_proxy_dead = result.error and any(
k in result.error for k in ("502", "Bad Gateway", "NS_ERROR_PROXY") k in result.error for k in ("502", "Bad Gateway", "NS_ERROR_PROXY")
@@ -204,6 +210,9 @@ class BrowserService:
self._proxy_fail_streak = 0 self._proxy_fail_streak = 0
return result 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: except (ValueError, InvalidIP, InvalidProxy) as e:
logger.warning(f"Visit attempt {attempt + 1} proxy error, retrying: {e}") logger.warning(f"Visit attempt {attempt + 1} proxy error, retrying: {e}")