zai2api / cookie_manager.py
bluewinliang's picture
Update cookie_manager.py
af689c4 verified
"""
Cookie pool manager for Z.AI tokens with round-robin rotation
"""
import asyncio
import logging
from typing import List, Optional
from asyncio import Lock
import httpx
from config import settings
logger = logging.getLogger(__name__)
class CookieManager:
def __init__(self, cookies: List[str]):
self.cookies = cookies or []
self.current_index = 0
self.lock = Lock()
self.failed_cookies = set()
if self.cookies:
logger.info(f"Initialized CookieManager with {len(cookies)} cookies")
else:
logger.warning("CookieManager initialized with no cookies")
async def get_next_cookie(self) -> Optional[str]:
"""Get the next available cookie using round-robin"""
if not self.cookies:
return None
async with self.lock:
attempts = 0
while attempts < len(self.cookies):
cookie = self.cookies[self.current_index]
self.current_index = (self.current_index + 1) % len(self.cookies)
# Skip failed cookies
if cookie not in self.failed_cookies:
return cookie
attempts += 1
# All cookies failed, reset failed set and try again
if self.failed_cookies:
logger.warning(f"All {len(self.cookies)} cookies failed, resetting failed set and retrying")
self.failed_cookies.clear()
return self.cookies[0]
return None
async def mark_cookie_failed(self, cookie: str):
"""Mark a cookie as failed"""
async with self.lock:
self.failed_cookies.add(cookie)
logger.warning(f"Marked cookie as failed: {cookie[:20]}...")
async def mark_cookie_success(self, cookie: str):
"""Mark a cookie as working (remove from failed set)"""
async with self.lock:
if cookie in self.failed_cookies:
self.failed_cookies.discard(cookie)
logger.info(f"Cookie recovered: {cookie[:20]}...")
async def health_check(self, cookie: str) -> bool:
"""Check if a cookie is still valid"""
try:
async with httpx.AsyncClient() as client:
# Use the same payload format as actual requests
import uuid
test_payload = {
"stream": True,
"model": "GLM-4-6-API-V1",
"messages": [{"role": "user", "content": "hi"}],
"background_tasks": {
"title_generation": False,
"tags_generation": False
},
"chat_id": str(uuid.uuid4()),
"features": {
"image_generation": False,
"code_interpreter": False,
"web_search": False,
"auto_web_search": False
},
"id": str(uuid.uuid4()),
"mcp_servers": [],
"model_item": {
"id": "GLM-4-6-API-V1",
"name": "GLM-4.6",
"owned_by": "openai"
},
"params": {},
"tool_servers": [],
"variables": {
"{{USER_NAME}}": "User",
"{{USER_LOCATION}}": "Unknown",
"{{CURRENT_DATETIME}}": "2025-08-04 16:46:56"
}
}
response = await client.post(
"https://chat.z.ai/api/chat/completions",
headers={
"Authorization": f"Bearer {cookie}",
"Content-Type": "application/json",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
"Accept": "application/json, text/event-stream",
"Accept-Language": "zh-CN",
"sec-ch-ua": '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"x-fe-version": "prod-fe-1.0.53",
"Origin": "https://chat.z.ai",
"Referer": "https://chat.z.ai/c/069723d5-060b-404f-992c-4705f1554c4c"
},
json=test_payload,
timeout=10.0
)
# Consider 200 as success
is_healthy = response.status_code == 200
if not is_healthy:
logger.debug(f"Health check failed for cookie {cookie[:20]}...: HTTP {response.status_code}")
else:
logger.debug(f"Health check passed for cookie {cookie[:20]}...")
return is_healthy
except Exception as e:
logger.debug(f"Health check failed for cookie {cookie[:20]}...: {e}")
return False
async def periodic_health_check(self):
"""Periodically check all cookies health"""
while True:
try:
# Only check if we have cookies and some are marked as failed
if self.cookies and self.failed_cookies:
logger.info(f"Running health check for {len(self.failed_cookies)} failed cookies")
for cookie in list(self.failed_cookies): # Create a copy to avoid modification during iteration
if await self.health_check(cookie):
await self.mark_cookie_success(cookie)
logger.info(f"Cookie recovered: {cookie[:20]}...")
else:
logger.debug(f"Cookie still failed: {cookie[:20]}...")
# Wait 10 minutes before next check (reduced frequency)
await asyncio.sleep(600)
except Exception as e:
logger.error(f"Error in periodic health check: {e}")
await asyncio.sleep(300) # Wait 5 minutes on error
# Global cookie manager instance
cookie_manager = CookieManager(settings.COOKIES if settings else [])