Spaces:
Paused
Paused
Upload 6 files
Browse files- Dockerfile +26 -0
- README.md +3 -0
- app.py +85 -197
- requirements.txt +0 -1
Dockerfile
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9-slim
|
| 2 |
+
|
| 3 |
+
# 设置工作目录
|
| 4 |
+
WORKDIR /app
|
| 5 |
+
|
| 6 |
+
# 复制依赖文件
|
| 7 |
+
COPY requirements.txt .
|
| 8 |
+
|
| 9 |
+
# 安装 gunicorn 和其他依赖
|
| 10 |
+
RUN pip install --upgrade pip && \
|
| 11 |
+
pip install --no-cache-dir -r requirements.txt gunicorn
|
| 12 |
+
|
| 13 |
+
# 复制应用程序文件
|
| 14 |
+
COPY app.py .
|
| 15 |
+
COPY register_bot.py .
|
| 16 |
+
|
| 17 |
+
# 设置环境变量
|
| 18 |
+
ENV FLASK_APP=app.py
|
| 19 |
+
ENV FLASK_ENV=production
|
| 20 |
+
ENV PYTHONUNBUFFERED=1
|
| 21 |
+
|
| 22 |
+
# 暴露端口
|
| 23 |
+
EXPOSE 3000
|
| 24 |
+
|
| 25 |
+
# 使用 gunicorn 作为生产级 WSGI 服务器
|
| 26 |
+
CMD ["gunicorn", "--bind", "0.0.0.0:3000", "--workers", "4", "app:app"]
|
README.md
CHANGED
|
@@ -9,3 +9,6 @@ license: apache-2.0
|
|
| 9 |
app_port: 3000
|
| 10 |
---
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
app_port: 3000
|
| 10 |
---
|
| 11 |
|
| 12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 13 |
+
|
| 14 |
+
[email protected]|password123
|
app.py
CHANGED
|
@@ -23,7 +23,6 @@ from datetime import datetime, timedelta
|
|
| 23 |
|
| 24 |
# 新增导入
|
| 25 |
import register_bot
|
| 26 |
-
from proxy_pool import ProxyPool
|
| 27 |
|
| 28 |
# Constants
|
| 29 |
CHAT_COMPLETION_CHUNK = 'chat.completion.chunk'
|
|
@@ -129,28 +128,16 @@ class AuthManager:
|
|
| 129 |
self._session: requests.Session = create_custom_session()
|
| 130 |
self._logger: logging.Logger = logging.getLogger(__name__)
|
| 131 |
self.model_status = {model: True for model in MODEL_INFO.keys()}
|
|
|
|
| 132 |
self._last_auth_attempt = 0
|
| 133 |
self._auth_attempts = 0
|
| 134 |
self._auth_window_start = time.time()
|
| 135 |
self._backoff_delay = AUTH_RETRY_DELAY
|
| 136 |
-
self._failed_attempts = 0 # 添加失败尝试计数
|
| 137 |
-
self._last_failure_time = 0 # 添加最后失败时间
|
| 138 |
-
self._is_valid = True # 添加账号有效性标志
|
| 139 |
|
| 140 |
def _should_attempt_auth(self) -> bool:
|
| 141 |
"""检查是否应该尝试认证请求"""
|
| 142 |
current_time = time.time()
|
| 143 |
|
| 144 |
-
# 如果账号已被标记为无效,直接返回False
|
| 145 |
-
if not self._is_valid:
|
| 146 |
-
return False
|
| 147 |
-
|
| 148 |
-
# 如果连续失败次数过多,标记账号为无效
|
| 149 |
-
if self._failed_attempts >= 5:
|
| 150 |
-
self._is_valid = False
|
| 151 |
-
self._logger.warning(f"Account {self._email} marked as invalid due to too many failures")
|
| 152 |
-
return False
|
| 153 |
-
|
| 154 |
# 检查是否在退避期内
|
| 155 |
if current_time - self._last_auth_attempt < self._backoff_delay:
|
| 156 |
return False
|
|
@@ -161,7 +148,6 @@ class AuthManager:
|
|
| 161 |
self._auth_window_start = current_time
|
| 162 |
self._auth_attempts = 0
|
| 163 |
self._backoff_delay = AUTH_RETRY_DELAY
|
| 164 |
-
self._failed_attempts = 0 # 重置失败计数
|
| 165 |
|
| 166 |
# 检查请求数量
|
| 167 |
if self._auth_attempts >= AUTH_MAX_REQUESTS:
|
|
@@ -169,39 +155,10 @@ class AuthManager:
|
|
| 169 |
|
| 170 |
return True
|
| 171 |
|
| 172 |
-
def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
|
| 173 |
-
"""改进的请求方法,支持代理"""
|
| 174 |
-
global proxy_pool
|
| 175 |
-
max_retries = 3
|
| 176 |
-
|
| 177 |
-
for attempt in range(max_retries):
|
| 178 |
-
try:
|
| 179 |
-
# 获取代理
|
| 180 |
-
proxy = proxy_pool.get_proxy() if proxy_pool else None
|
| 181 |
-
|
| 182 |
-
if proxy:
|
| 183 |
-
kwargs['proxies'] = proxy
|
| 184 |
-
kwargs['timeout'] = 10 # 使用代理时设置较短的超时
|
| 185 |
-
|
| 186 |
-
response = self._session.request(method, url, **kwargs)
|
| 187 |
-
response.raise_for_status()
|
| 188 |
-
return response
|
| 189 |
-
|
| 190 |
-
except requests.RequestException as e:
|
| 191 |
-
if proxy:
|
| 192 |
-
proxy_pool.remove_proxy(proxy)
|
| 193 |
-
|
| 194 |
-
if attempt == max_retries - 1:
|
| 195 |
-
self._logger.error(f"请求错误 ({method} {url}): {e}")
|
| 196 |
-
raise
|
| 197 |
-
|
| 198 |
-
time.sleep(1) # 短暂等待后重试
|
| 199 |
-
|
| 200 |
-
raise requests.RequestException("Max retries exceeded")
|
| 201 |
-
|
| 202 |
def login(self) -> bool:
|
| 203 |
-
"""
|
| 204 |
if not self._should_attempt_auth():
|
|
|
|
| 205 |
return False
|
| 206 |
|
| 207 |
try:
|
|
@@ -220,55 +177,29 @@ class AuthManager:
|
|
| 220 |
|
| 221 |
if response.status_code == 429:
|
| 222 |
self._backoff_delay *= AUTH_BACKOFF_FACTOR
|
| 223 |
-
self._failed_attempts += 1
|
| 224 |
-
self._last_failure_time = time.time()
|
| 225 |
logger.warning(f"Rate limit hit, increasing backoff to {self._backoff_delay}s")
|
| 226 |
return False
|
| 227 |
|
| 228 |
-
|
| 229 |
-
self._failed_attempts += 1
|
| 230 |
-
self._last_failure_time = time.time()
|
| 231 |
-
if self._failed_attempts >= 5:
|
| 232 |
-
self._is_valid = False
|
| 233 |
-
logger.error(f"Account {self._email} marked as invalid due to authentication failures")
|
| 234 |
-
return False
|
| 235 |
-
|
| 236 |
self._user_info = response.json()
|
| 237 |
self._refresh_token = self._user_info.get('refresh_token', '')
|
| 238 |
self._access_token = self._user_info.get('access_token', '')
|
| 239 |
self._token_expiry = time.time() + self._user_info.get('expires_in', 3600)
|
| 240 |
|
| 241 |
-
#
|
| 242 |
-
self._failed_attempts = 0
|
| 243 |
self._backoff_delay = AUTH_RETRY_DELAY
|
| 244 |
self._log_values()
|
| 245 |
return True
|
| 246 |
|
| 247 |
except requests.RequestException as e:
|
| 248 |
logger.error(f"\033[91m登录请求错误: {e}\033[0m")
|
| 249 |
-
self._failed_attempts += 1
|
| 250 |
-
self._last_failure_time = time.time()
|
| 251 |
self._backoff_delay *= AUTH_BACKOFF_FACTOR
|
| 252 |
return False
|
| 253 |
|
| 254 |
-
def is_valid(self) -> bool:
|
| 255 |
-
"""检查账号是否有效"""
|
| 256 |
-
return self._is_valid
|
| 257 |
-
|
| 258 |
-
def reset_status(self) -> None:
|
| 259 |
-
"""重置账号状态"""
|
| 260 |
-
self._is_valid = True
|
| 261 |
-
self._failed_attempts = 0
|
| 262 |
-
self._backoff_delay = AUTH_RETRY_DELAY
|
| 263 |
-
self._auth_attempts = 0
|
| 264 |
-
self._auth_window_start = time.time()
|
| 265 |
-
|
| 266 |
def refresh_user_token(self) -> bool:
|
| 267 |
-
"""改进的令牌刷新方法,使用代理"""
|
| 268 |
url = f"{_API_BASE_URL}/auth/v1/token?grant_type=refresh_token"
|
| 269 |
headers = self._get_headers(with_content_type=True)
|
| 270 |
data = {"refresh_token": self._refresh_token}
|
| 271 |
-
|
| 272 |
try:
|
| 273 |
response = self._make_request('POST', url, headers=headers, json=data)
|
| 274 |
self._user_info = response.json()
|
|
@@ -353,6 +284,16 @@ class AuthManager:
|
|
| 353 |
headers['Authorization'] = f'Bearer {self._access_token}'
|
| 354 |
return headers
|
| 355 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
def is_model_available(self, model):
|
| 357 |
return self.model_status.get(model, True)
|
| 358 |
|
|
@@ -382,9 +323,7 @@ class MultiAuthManager:
|
|
| 382 |
start_index = self.current_index
|
| 383 |
for _ in range(len(self.auth_managers)):
|
| 384 |
auth_manager = self.auth_managers[self.current_index]
|
| 385 |
-
if
|
| 386 |
-
auth_manager.is_model_available(model) and
|
| 387 |
-
auth_manager._should_attempt_auth()):
|
| 388 |
return auth_manager
|
| 389 |
self.current_index = (self.current_index + 1) % len(self.auth_managers)
|
| 390 |
if self.current_index == start_index:
|
|
@@ -398,13 +337,12 @@ class MultiAuthManager:
|
|
| 398 |
return auth_manager
|
| 399 |
return None
|
| 400 |
|
| 401 |
-
def
|
| 402 |
-
"""重置所有账号状态"""
|
| 403 |
for auth_manager in self.auth_managers:
|
| 404 |
-
auth_manager.
|
| 405 |
|
| 406 |
def require_auth(func: Callable) -> Callable:
|
| 407 |
-
"""
|
| 408 |
@wraps(func)
|
| 409 |
def wrapper(self, *args, **kwargs):
|
| 410 |
if not self.ensure_valid_token():
|
|
@@ -840,16 +778,11 @@ def build_payload(request_data, model_id):
|
|
| 840 |
|
| 841 |
return payload
|
| 842 |
|
| 843 |
-
# 在全局变量部分添加
|
| 844 |
-
proxy_pool = ProxyPool()
|
| 845 |
-
|
| 846 |
-
# 修改 make_request 函数
|
| 847 |
def make_request(payload, auth_manager, model_id):
|
| 848 |
"""发送请求并处理可能的认证刷新和模型特定错误。"""
|
| 849 |
global multi_auth_manager
|
| 850 |
max_retries = 3
|
| 851 |
retry_delay = 1
|
| 852 |
-
proxy_retries = 2 # 每个账号最多尝试2次代理
|
| 853 |
|
| 854 |
logger.info(f"尝试发送请求,模型:{model_id}")
|
| 855 |
|
|
@@ -874,6 +807,7 @@ def make_request(payload, auth_manager, model_id):
|
|
| 874 |
if not auth_manager:
|
| 875 |
break
|
| 876 |
|
|
|
|
| 877 |
if auth_manager._email in tried_accounts:
|
| 878 |
continue
|
| 879 |
|
|
@@ -881,137 +815,91 @@ def make_request(payload, auth_manager, model_id):
|
|
| 881 |
logger.info(f"尝试使用账号 {auth_manager._email}")
|
| 882 |
|
| 883 |
for attempt in range(max_retries):
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 894 |
continue
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
return response
|
| 909 |
-
|
| 910 |
-
# 如果请求失败且使用了代理,移除该代理
|
| 911 |
-
if proxy:
|
| 912 |
-
proxy_pool.remove_proxy(proxy)
|
| 913 |
-
if proxy_attempt < proxy_retries - 1:
|
| 914 |
-
# 如果还有代理重试机会,继续下一次尝试
|
| 915 |
-
continue
|
| 916 |
-
|
| 917 |
-
headers_cache.clear()
|
| 918 |
-
|
| 919 |
-
if response.status_code == 401:
|
| 920 |
-
logger.info(f"Token expired for account {auth_manager._email}, attempting refresh")
|
| 921 |
-
if auth_manager.ensure_valid_token():
|
| 922 |
-
break # 跳出代理重试循环,使用新token重试
|
| 923 |
-
|
| 924 |
-
if response.status_code == 403:
|
| 925 |
-
logger.warning(f"Model {model_id} usage limit reached for account {auth_manager._email}")
|
| 926 |
-
auth_manager.set_model_unavailable(model_id)
|
| 927 |
-
break # 跳出代理重试循环,尝试下一个账号
|
| 928 |
-
|
| 929 |
-
logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
|
| 930 |
-
break # 跳出代理重试循环
|
| 931 |
-
|
| 932 |
-
except Exception as e:
|
| 933 |
-
logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
|
| 934 |
-
if proxy:
|
| 935 |
-
proxy_pool.remove_proxy(proxy)
|
| 936 |
-
if proxy_attempt < proxy_retries - 1:
|
| 937 |
-
continue
|
| 938 |
-
break # 跳出代理重试循环
|
| 939 |
-
|
| 940 |
-
if attempt < max_retries - 1:
|
| 941 |
-
time.sleep(retry_delay)
|
| 942 |
|
| 943 |
-
#
|
| 944 |
if len(tried_accounts) == len(multi_auth_manager.auth_managers):
|
| 945 |
logger.info("所有现有账号都已尝试,开始注册新账号")
|
| 946 |
successful_accounts = register_bot.register_and_verify(5)
|
| 947 |
if successful_accounts:
|
| 948 |
credentials = [(account['email'], account['password']) for account in successful_accounts]
|
| 949 |
multi_auth_manager = MultiAuthManager(credentials)
|
|
|
|
| 950 |
return make_request(payload, None, model_id)
|
| 951 |
|
| 952 |
raise Exception("所有账号均不可用,且注册新账号失败")
|
| 953 |
|
| 954 |
def health_check():
|
| 955 |
-
"""
|
| 956 |
-
last_check_time = {}
|
| 957 |
-
check_interval = 60 # 每个账号的检查间隔(秒)
|
| 958 |
-
max_concurrent_checks = 3 # 最大并发检查数量
|
| 959 |
|
| 960 |
-
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
|
| 964 |
-
|
| 965 |
-
|
|
|
|
| 966 |
|
| 967 |
-
|
| 968 |
-
|
|
|
|
| 969 |
|
| 970 |
-
|
| 971 |
-
|
| 972 |
-
|
| 973 |
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
|
|
|
|
|
|
| 977 |
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
try:
|
| 986 |
-
result = future.result(timeout=30)
|
| 987 |
-
if result:
|
| 988 |
-
logger.info(f"Health check passed for {email}")
|
| 989 |
-
else:
|
| 990 |
-
logger.warning(f"Health check failed for {email}")
|
| 991 |
-
except Exception as e:
|
| 992 |
-
logger.error(f"Health check error for {email}: {e}")
|
| 993 |
-
|
| 994 |
-
# 每天重置所有账号的模型使用状态
|
| 995 |
-
current_time_local = time.localtime()
|
| 996 |
-
if current_time_local.tm_hour == 0 and current_time_local.tm_min == 0:
|
| 997 |
-
multi_auth_manager.reset_all_accounts()
|
| 998 |
-
logger.info("Reset model status for all accounts")
|
| 999 |
|
| 1000 |
-
|
| 1001 |
-
|
| 1002 |
|
| 1003 |
-
|
| 1004 |
-
|
| 1005 |
-
def perform_health_check(auth_manager):
|
| 1006 |
-
"""执行单个账号的健康检查"""
|
| 1007 |
-
try:
|
| 1008 |
-
if auth_manager.ensure_valid_token():
|
| 1009 |
-
return True
|
| 1010 |
-
auth_manager.clear_auth()
|
| 1011 |
-
return False
|
| 1012 |
-
except Exception as e:
|
| 1013 |
-
logger.error(f"Health check error for {auth_manager._email}: {e}")
|
| 1014 |
-
return False
|
| 1015 |
|
| 1016 |
# 为了兼容 Flask CLI 和 Gunicorn,修改启动逻辑
|
| 1017 |
if __name__ != "__main__":
|
|
|
|
| 23 |
|
| 24 |
# 新增导入
|
| 25 |
import register_bot
|
|
|
|
| 26 |
|
| 27 |
# Constants
|
| 28 |
CHAT_COMPLETION_CHUNK = 'chat.completion.chunk'
|
|
|
|
| 128 |
self._session: requests.Session = create_custom_session()
|
| 129 |
self._logger: logging.Logger = logging.getLogger(__name__)
|
| 130 |
self.model_status = {model: True for model in MODEL_INFO.keys()}
|
| 131 |
+
# 添加新的属性来跟踪认证请求
|
| 132 |
self._last_auth_attempt = 0
|
| 133 |
self._auth_attempts = 0
|
| 134 |
self._auth_window_start = time.time()
|
| 135 |
self._backoff_delay = AUTH_RETRY_DELAY
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
def _should_attempt_auth(self) -> bool:
|
| 138 |
"""检查是否应该尝试认证请求"""
|
| 139 |
current_time = time.time()
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
# 检查是否在退避期内
|
| 142 |
if current_time - self._last_auth_attempt < self._backoff_delay:
|
| 143 |
return False
|
|
|
|
| 148 |
self._auth_window_start = current_time
|
| 149 |
self._auth_attempts = 0
|
| 150 |
self._backoff_delay = AUTH_RETRY_DELAY
|
|
|
|
| 151 |
|
| 152 |
# 检查请求数量
|
| 153 |
if self._auth_attempts >= AUTH_MAX_REQUESTS:
|
|
|
|
| 155 |
|
| 156 |
return True
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
def login(self) -> bool:
|
| 159 |
+
"""改进的登录方法,包含速率限制和退避机制"""
|
| 160 |
if not self._should_attempt_auth():
|
| 161 |
+
logger.warning(f"Rate limit reached for {self._email}, waiting {self._backoff_delay}s")
|
| 162 |
return False
|
| 163 |
|
| 164 |
try:
|
|
|
|
| 177 |
|
| 178 |
if response.status_code == 429:
|
| 179 |
self._backoff_delay *= AUTH_BACKOFF_FACTOR
|
|
|
|
|
|
|
| 180 |
logger.warning(f"Rate limit hit, increasing backoff to {self._backoff_delay}s")
|
| 181 |
return False
|
| 182 |
|
| 183 |
+
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
self._user_info = response.json()
|
| 185 |
self._refresh_token = self._user_info.get('refresh_token', '')
|
| 186 |
self._access_token = self._user_info.get('access_token', '')
|
| 187 |
self._token_expiry = time.time() + self._user_info.get('expires_in', 3600)
|
| 188 |
|
| 189 |
+
# 重置退避延迟
|
|
|
|
| 190 |
self._backoff_delay = AUTH_RETRY_DELAY
|
| 191 |
self._log_values()
|
| 192 |
return True
|
| 193 |
|
| 194 |
except requests.RequestException as e:
|
| 195 |
logger.error(f"\033[91m登录请求错误: {e}\033[0m")
|
|
|
|
|
|
|
| 196 |
self._backoff_delay *= AUTH_BACKOFF_FACTOR
|
| 197 |
return False
|
| 198 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
def refresh_user_token(self) -> bool:
|
|
|
|
| 200 |
url = f"{_API_BASE_URL}/auth/v1/token?grant_type=refresh_token"
|
| 201 |
headers = self._get_headers(with_content_type=True)
|
| 202 |
data = {"refresh_token": self._refresh_token}
|
|
|
|
| 203 |
try:
|
| 204 |
response = self._make_request('POST', url, headers=headers, json=data)
|
| 205 |
self._user_info = response.json()
|
|
|
|
| 284 |
headers['Authorization'] = f'Bearer {self._access_token}'
|
| 285 |
return headers
|
| 286 |
|
| 287 |
+
def _make_request(self, method: str, url: str, **kwargs) -> requests.Response:
|
| 288 |
+
"""发送HTTP请求并处理异常。"""
|
| 289 |
+
try:
|
| 290 |
+
response = self._session.request(method, url, **kwargs)
|
| 291 |
+
response.raise_for_status()
|
| 292 |
+
return response
|
| 293 |
+
except requests.RequestException as e:
|
| 294 |
+
self._logger.error(f"请求错误 ({method} {url}): {e}")
|
| 295 |
+
raise
|
| 296 |
+
|
| 297 |
def is_model_available(self, model):
|
| 298 |
return self.model_status.get(model, True)
|
| 299 |
|
|
|
|
| 323 |
start_index = self.current_index
|
| 324 |
for _ in range(len(self.auth_managers)):
|
| 325 |
auth_manager = self.auth_managers[self.current_index]
|
| 326 |
+
if auth_manager.is_model_available(model) and auth_manager._should_attempt_auth():
|
|
|
|
|
|
|
| 327 |
return auth_manager
|
| 328 |
self.current_index = (self.current_index + 1) % len(self.auth_managers)
|
| 329 |
if self.current_index == start_index:
|
|
|
|
| 337 |
return auth_manager
|
| 338 |
return None
|
| 339 |
|
| 340 |
+
def reset_all_model_status(self):
|
|
|
|
| 341 |
for auth_manager in self.auth_managers:
|
| 342 |
+
auth_manager.reset_model_status()
|
| 343 |
|
| 344 |
def require_auth(func: Callable) -> Callable:
|
| 345 |
+
"""装饰器,确保在调用API之前有有效的token。"""
|
| 346 |
@wraps(func)
|
| 347 |
def wrapper(self, *args, **kwargs):
|
| 348 |
if not self.ensure_valid_token():
|
|
|
|
| 778 |
|
| 779 |
return payload
|
| 780 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 781 |
def make_request(payload, auth_manager, model_id):
|
| 782 |
"""发送请求并处理可能的认证刷新和模型特定错误。"""
|
| 783 |
global multi_auth_manager
|
| 784 |
max_retries = 3
|
| 785 |
retry_delay = 1
|
|
|
|
| 786 |
|
| 787 |
logger.info(f"尝试发送请求,模型:{model_id}")
|
| 788 |
|
|
|
|
| 807 |
if not auth_manager:
|
| 808 |
break
|
| 809 |
|
| 810 |
+
# 如果这个账号已经尝试过,继续下一个
|
| 811 |
if auth_manager._email in tried_accounts:
|
| 812 |
continue
|
| 813 |
|
|
|
|
| 815 |
logger.info(f"尝试使用账号 {auth_manager._email}")
|
| 816 |
|
| 817 |
for attempt in range(max_retries):
|
| 818 |
+
try:
|
| 819 |
+
url = get_notdiamond_url()
|
| 820 |
+
headers = get_notdiamond_headers(auth_manager)
|
| 821 |
+
response = executor.submit(
|
| 822 |
+
requests.post,
|
| 823 |
+
url,
|
| 824 |
+
headers=headers,
|
| 825 |
+
json=payload,
|
| 826 |
+
stream=True
|
| 827 |
+
).result()
|
| 828 |
+
|
| 829 |
+
if response.status_code == 200 and response.headers.get('Content-Type') == 'text/event-stream':
|
| 830 |
+
logger.info(f"请求成功,使用账号 {auth_manager._email}")
|
| 831 |
+
return response
|
| 832 |
+
|
| 833 |
+
headers_cache.clear()
|
| 834 |
+
|
| 835 |
+
if response.status_code == 401: # Unauthorized
|
| 836 |
+
logger.info(f"Token expired for account {auth_manager._email}, attempting refresh")
|
| 837 |
+
if auth_manager.ensure_valid_token():
|
| 838 |
continue
|
| 839 |
+
|
| 840 |
+
if response.status_code == 403: # Forbidden, 模型使用限制
|
| 841 |
+
logger.warning(f"Model {model_id} usage limit reached for account {auth_manager._email}")
|
| 842 |
+
auth_manager.set_model_unavailable(model_id)
|
| 843 |
+
break # 跳出重试循环,尝试下一个账号
|
| 844 |
+
|
| 845 |
+
logger.error(f"Request failed with status {response.status_code} for account {auth_manager._email}")
|
| 846 |
+
|
| 847 |
+
except Exception as e:
|
| 848 |
+
logger.error(f"Request attempt {attempt + 1} failed for account {auth_manager._email}: {e}")
|
| 849 |
+
if attempt < max_retries - 1:
|
| 850 |
+
time.sleep(retry_delay)
|
| 851 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 852 |
|
| 853 |
+
# 所有账号都尝试过且失败后,才进行注册
|
| 854 |
if len(tried_accounts) == len(multi_auth_manager.auth_managers):
|
| 855 |
logger.info("所有现有账号都已尝试,开始注册新账号")
|
| 856 |
successful_accounts = register_bot.register_and_verify(5)
|
| 857 |
if successful_accounts:
|
| 858 |
credentials = [(account['email'], account['password']) for account in successful_accounts]
|
| 859 |
multi_auth_manager = MultiAuthManager(credentials)
|
| 860 |
+
# 使用新注册的账号重试请求
|
| 861 |
return make_request(payload, None, model_id)
|
| 862 |
|
| 863 |
raise Exception("所有账号均不可用,且注册新账号失败")
|
| 864 |
|
| 865 |
def health_check():
|
| 866 |
+
"""改进的健康检查函数"""
|
| 867 |
+
last_check_time = {} # 用于跟踪每个账号的最后检查时间
|
|
|
|
|
|
|
| 868 |
|
| 869 |
+
while True:
|
| 870 |
+
try:
|
| 871 |
+
if multi_auth_manager:
|
| 872 |
+
current_time = time.time()
|
| 873 |
+
|
| 874 |
+
for auth_manager in multi_auth_manager.auth_managers:
|
| 875 |
+
email = auth_manager._email
|
| 876 |
|
| 877 |
+
# 检查是否需要进行健康检查
|
| 878 |
+
if email not in last_check_time or \
|
| 879 |
+
current_time - last_check_time[email] >= AUTH_CHECK_INTERVAL:
|
| 880 |
|
| 881 |
+
if not auth_manager._should_attempt_auth():
|
| 882 |
+
logger.info(f"Skipping health check for {email} due to rate limiting")
|
| 883 |
+
continue
|
| 884 |
|
| 885 |
+
if not auth_manager.ensure_valid_token():
|
| 886 |
+
logger.warning(f"Auth token validation failed during health check for {email}")
|
| 887 |
+
auth_manager.clear_auth()
|
| 888 |
+
else:
|
| 889 |
+
logger.info(f"Health check passed for {email}")
|
| 890 |
|
| 891 |
+
last_check_time[email] = current_time
|
| 892 |
+
|
| 893 |
+
# 每天重置所有账号的模型使用状态
|
| 894 |
+
current_time_local = time.localtime()
|
| 895 |
+
if current_time_local.tm_hour == 0 and current_time_local.tm_min == 0:
|
| 896 |
+
multi_auth_manager.reset_all_model_status()
|
| 897 |
+
logger.info("Reset model status for all accounts")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 898 |
|
| 899 |
+
except Exception as e:
|
| 900 |
+
logger.error(f"Health check error: {e}")
|
| 901 |
|
| 902 |
+
sleep(60) # 主循环每分钟运行一次
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 903 |
|
| 904 |
# 为了兼容 Flask CLI 和 Gunicorn,修改启动逻辑
|
| 905 |
if __name__ != "__main__":
|
requirements.txt
CHANGED
|
@@ -8,4 +8,3 @@ urllib3==1.26.9
|
|
| 8 |
beautifulsoup4==4.11.1
|
| 9 |
Pillow==9.2.0
|
| 10 |
lxml==4.9.1
|
| 11 |
-
requests[socks]
|
|
|
|
| 8 |
beautifulsoup4==4.11.1
|
| 9 |
Pillow==9.2.0
|
| 10 |
lxml==4.9.1
|
|
|