Spaces:
Paused
Paused
Upload 3 files
Browse files- Dockerfile +4 -11
- app.py +31 -191
- requirements.txt +0 -1
Dockerfile
CHANGED
|
@@ -6,16 +6,9 @@ WORKDIR /app
|
|
| 6 |
# 复制依赖文件
|
| 7 |
COPY requirements.txt .
|
| 8 |
|
| 9 |
-
#
|
| 10 |
-
RUN
|
| 11 |
-
|
| 12 |
-
gcc \
|
| 13 |
-
python3-dev \
|
| 14 |
-
&& pip install --upgrade pip \
|
| 15 |
-
&& pip install --no-cache-dir -r requirements.txt gunicorn \
|
| 16 |
-
&& apt-get purge -y --auto-remove gcc python3-dev \
|
| 17 |
-
&& apt-get clean \
|
| 18 |
-
&& rm -rf /var/lib/apt/lists/*
|
| 19 |
|
| 20 |
# 复制应用程序文件
|
| 21 |
COPY app.py .
|
|
@@ -30,4 +23,4 @@ ENV PYTHONUNBUFFERED=1
|
|
| 30 |
EXPOSE 3000
|
| 31 |
|
| 32 |
# 使用 gunicorn 作为生产级 WSGI 服务器
|
| 33 |
-
CMD ["gunicorn", "--bind", "0.0.0.0:3000", "--workers", "4", "
|
|
|
|
| 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 .
|
|
|
|
| 23 |
EXPOSE 3000
|
| 24 |
|
| 25 |
# 使用 gunicorn 作为生产级 WSGI 服务器
|
| 26 |
+
CMD ["gunicorn", "--bind", "0.0.0.0:3000", "--workers", "4", "app:app"]
|
app.py
CHANGED
|
@@ -8,7 +8,7 @@ import re
|
|
| 8 |
import socket
|
| 9 |
from concurrent.futures import ThreadPoolExecutor
|
| 10 |
from functools import lru_cache, wraps
|
| 11 |
-
from typing import Dict, Any, Callable, List, Tuple
|
| 12 |
import requests
|
| 13 |
import tiktoken
|
| 14 |
from flask import Flask, Response, jsonify, request, stream_with_context
|
|
@@ -20,9 +20,6 @@ from cachetools import TTLCache
|
|
| 20 |
import threading
|
| 21 |
from time import sleep
|
| 22 |
from datetime import datetime, timedelta
|
| 23 |
-
import aiohttp
|
| 24 |
-
import asyncio
|
| 25 |
-
import atexit
|
| 26 |
|
| 27 |
# 新增导入
|
| 28 |
import register_bot
|
|
@@ -374,8 +371,7 @@ def get_notdiamond_headers(auth_manager):
|
|
| 374 |
'accept-language': 'zh-CN,zh;q=0.9',
|
| 375 |
'content-type': 'application/json',
|
| 376 |
'user-agent': _USER_AGENT,
|
| 377 |
-
'authorization': f'Bearer {auth_manager.get_jwt_value()}'
|
| 378 |
-
'Host': NOTDIAMOND_DOMAIN # 添加 Host 头
|
| 379 |
}
|
| 380 |
headers_cache[cache_key] = headers
|
| 381 |
return headers
|
|
@@ -782,145 +778,8 @@ def build_payload(request_data, model_id):
|
|
| 782 |
|
| 783 |
return payload
|
| 784 |
|
| 785 |
-
# 修改理相关的常量和配置
|
| 786 |
-
PROXY_API_BASE = "https://269900.xyz"
|
| 787 |
-
PROXY_CHECK_INTERVAL = 300 # 5分钟检查一次代理可用性
|
| 788 |
-
PROXY_MAX_LATENCY = 5000 # 最大延迟(毫秒)
|
| 789 |
-
MAX_PROXIES = 10 # 保持的最大代理数量
|
| 790 |
-
PROXY_TIMEOUT = 5 # 代理请求超时时间(秒)
|
| 791 |
-
|
| 792 |
-
class ProxyManager:
|
| 793 |
-
def __init__(self):
|
| 794 |
-
self.proxies: List[Dict[str, Union[str, int, float]]] = []
|
| 795 |
-
self.last_update = 0
|
| 796 |
-
self.lock = threading.Lock()
|
| 797 |
-
self.session = requests.Session()
|
| 798 |
-
|
| 799 |
-
def fetch_proxies(self):
|
| 800 |
-
"""同步获取代理列表"""
|
| 801 |
-
try:
|
| 802 |
-
endpoint = f"{PROXY_API_BASE}/fetch_all?region=cn&count=5"
|
| 803 |
-
response = self.session.get(endpoint, timeout=PROXY_TIMEOUT)
|
| 804 |
-
|
| 805 |
-
if response.status_code == 200:
|
| 806 |
-
# 解析文本响应
|
| 807 |
-
proxy_list = [p.strip() for p in response.text.split(',') if p.strip()]
|
| 808 |
-
|
| 809 |
-
with self.lock:
|
| 810 |
-
self.proxies = []
|
| 811 |
-
for proxy_str in proxy_list:
|
| 812 |
-
proxy_info = self._parse_proxy_string(proxy_str)
|
| 813 |
-
if proxy_info:
|
| 814 |
-
self.proxies.append(proxy_info)
|
| 815 |
-
|
| 816 |
-
self.last_update = time.time()
|
| 817 |
-
logger.info(f"Successfully fetched {len(self.proxies)} proxies")
|
| 818 |
-
else:
|
| 819 |
-
logger.error(f"Failed to fetch proxies, status: {response.status_code}")
|
| 820 |
-
|
| 821 |
-
except requests.exceptions.RequestException as e:
|
| 822 |
-
logger.error(f"Error fetching proxies: {e}")
|
| 823 |
-
except Exception as e:
|
| 824 |
-
logger.error(f"Unexpected error in fetch_proxies: {e}")
|
| 825 |
-
finally:
|
| 826 |
-
if not self.proxies:
|
| 827 |
-
logger.warning("No proxies available, will retry later")
|
| 828 |
-
|
| 829 |
-
def get_proxy(self) -> Optional[str]:
|
| 830 |
-
"""获取一个可用的代理"""
|
| 831 |
-
with self.lock:
|
| 832 |
-
current_time = time.time()
|
| 833 |
-
|
| 834 |
-
if not self.proxies or (current_time - self.last_update) > PROXY_CHECK_INTERVAL:
|
| 835 |
-
self.fetch_proxies()
|
| 836 |
-
|
| 837 |
-
if not self.proxies:
|
| 838 |
-
return None
|
| 839 |
-
|
| 840 |
-
valid_proxies = [p for p in self.proxies if p['fails'] < 3]
|
| 841 |
-
if not valid_proxies:
|
| 842 |
-
self.fetch_proxies()
|
| 843 |
-
valid_proxies = [p for p in self.proxies if p['fails'] < 3]
|
| 844 |
-
if not valid_proxies:
|
| 845 |
-
return None
|
| 846 |
-
|
| 847 |
-
proxy = min(valid_proxies, key=lambda x: (x['fails'], x['last_use']))
|
| 848 |
-
proxy['last_use'] = current_time
|
| 849 |
-
return proxy['url']
|
| 850 |
-
|
| 851 |
-
def mark_proxy_failed(self, proxy_url: str) -> None:
|
| 852 |
-
"""标记代理失败"""
|
| 853 |
-
with self.lock:
|
| 854 |
-
for proxy in self.proxies:
|
| 855 |
-
if proxy['url'] == proxy_url:
|
| 856 |
-
proxy['fails'] += 1
|
| 857 |
-
if proxy['fails'] >= 3:
|
| 858 |
-
self.proxies.remove(proxy)
|
| 859 |
-
logger.info(f"Removed failed proxy: {proxy_url}")
|
| 860 |
-
break
|
| 861 |
-
|
| 862 |
-
def check_proxy(self, proxy_url: str) -> bool:
|
| 863 |
-
"""同步检查代理是否可用"""
|
| 864 |
-
try:
|
| 865 |
-
response = self.session.get(
|
| 866 |
-
'https://www.google.com',
|
| 867 |
-
proxies={'https': proxy_url},
|
| 868 |
-
timeout=PROXY_TIMEOUT
|
| 869 |
-
)
|
| 870 |
-
return response.status_code == 200
|
| 871 |
-
except Exception:
|
| 872 |
-
return False
|
| 873 |
-
|
| 874 |
-
def _parse_proxy_string(self, proxy_str: str) -> Optional[Dict[str, Union[str, int, float]]]:
|
| 875 |
-
"""解析代理字符串"""
|
| 876 |
-
try:
|
| 877 |
-
proxy_str = proxy_str.strip()
|
| 878 |
-
if not proxy_str:
|
| 879 |
-
return None
|
| 880 |
-
|
| 881 |
-
if proxy_str.startswith(('http://', 'https://', 'socks4://', 'socks5://')):
|
| 882 |
-
proxy_type = proxy_str.split('://')[0]
|
| 883 |
-
address = proxy_str.split('://')[1]
|
| 884 |
-
else:
|
| 885 |
-
proxy_type = 'http'
|
| 886 |
-
address = proxy_str
|
| 887 |
-
|
| 888 |
-
if ':' in address:
|
| 889 |
-
ip, port = address.split(':')
|
| 890 |
-
port = int(port)
|
| 891 |
-
else:
|
| 892 |
-
return None
|
| 893 |
-
|
| 894 |
-
return {
|
| 895 |
-
'url': proxy_str if proxy_str.startswith(('http://', 'https://', 'socks4://', 'socks5://'))
|
| 896 |
-
else f"http://{proxy_str}",
|
| 897 |
-
'type': proxy_type,
|
| 898 |
-
'ip': ip,
|
| 899 |
-
'port': port,
|
| 900 |
-
'last_use': 0,
|
| 901 |
-
'fails': 0
|
| 902 |
-
}
|
| 903 |
-
except Exception as e:
|
| 904 |
-
logger.error(f"Error parsing proxy string '{proxy_str}': {e}")
|
| 905 |
-
return None
|
| 906 |
-
|
| 907 |
-
def __del__(self):
|
| 908 |
-
"""清理资源"""
|
| 909 |
-
if hasattr(self, 'session'):
|
| 910 |
-
self.session.close()
|
| 911 |
-
|
| 912 |
-
# 修改代理管理器的创建方式
|
| 913 |
-
proxy_manager = None
|
| 914 |
-
|
| 915 |
-
def get_proxy_manager():
|
| 916 |
-
"""获取或创建代理管理器实例"""
|
| 917 |
-
global proxy_manager
|
| 918 |
-
if proxy_manager is None:
|
| 919 |
-
proxy_manager = ProxyManager()
|
| 920 |
-
return proxy_manager
|
| 921 |
-
|
| 922 |
def make_request(payload, auth_manager, model_id):
|
| 923 |
-
"""
|
| 924 |
global multi_auth_manager
|
| 925 |
max_retries = 3
|
| 926 |
retry_delay = 1
|
|
@@ -940,6 +799,7 @@ def make_request(payload, auth_manager, model_id):
|
|
| 940 |
else:
|
| 941 |
raise Exception("无法注册新账号")
|
| 942 |
|
|
|
|
| 943 |
tried_accounts = set()
|
| 944 |
|
| 945 |
while len(tried_accounts) < len(multi_auth_manager.auth_managers):
|
|
@@ -947,6 +807,7 @@ def make_request(payload, auth_manager, model_id):
|
|
| 947 |
if not auth_manager:
|
| 948 |
break
|
| 949 |
|
|
|
|
| 950 |
if auth_manager._email in tried_accounts:
|
| 951 |
continue
|
| 952 |
|
|
@@ -957,61 +818,46 @@ def make_request(payload, auth_manager, model_id):
|
|
| 957 |
try:
|
| 958 |
url = get_notdiamond_url()
|
| 959 |
headers = get_notdiamond_headers(auth_manager)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 960 |
|
| 961 |
-
|
| 962 |
-
|
| 963 |
-
|
| 964 |
-
session.mount('https://', adapter)
|
| 965 |
-
session.mount('http://', adapter)
|
| 966 |
|
| 967 |
-
|
| 968 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 969 |
|
| 970 |
-
try:
|
| 971 |
-
response = session.post(
|
| 972 |
-
url,
|
| 973 |
-
headers=headers,
|
| 974 |
-
json=payload,
|
| 975 |
-
stream=True,
|
| 976 |
-
timeout=(5, 30), # (连接超时, 读取超时)
|
| 977 |
-
verify=False # 禁用SSL验证
|
| 978 |
-
)
|
| 979 |
-
|
| 980 |
-
if response.status_code == 200:
|
| 981 |
-
return response
|
| 982 |
-
|
| 983 |
-
if response.status_code == 401:
|
| 984 |
-
logger.info(f"Token expired for account {auth_manager._email}, attempting refresh")
|
| 985 |
-
if auth_manager.ensure_valid_token():
|
| 986 |
-
continue
|
| 987 |
-
|
| 988 |
-
if response.status_code == 403:
|
| 989 |
-
logger.warning(f"Model {model_id} usage limit reached for account {auth_manager._email}")
|
| 990 |
-
auth_manager.set_model_unavailable(model_id)
|
| 991 |
-
break
|
| 992 |
-
|
| 993 |
-
logger.error(f"Request failed with status {response.status_code}")
|
| 994 |
-
|
| 995 |
-
except requests.exceptions.RequestException as e:
|
| 996 |
-
logger.error(f"Request attempt {attempt + 1} failed: {e}")
|
| 997 |
-
if attempt < max_retries - 1:
|
| 998 |
-
time.sleep(retry_delay)
|
| 999 |
-
continue
|
| 1000 |
-
|
| 1001 |
except Exception as e:
|
| 1002 |
-
logger.error(f"
|
| 1003 |
if attempt < max_retries - 1:
|
| 1004 |
time.sleep(retry_delay)
|
| 1005 |
continue
|
| 1006 |
-
finally:
|
| 1007 |
-
session.close()
|
| 1008 |
|
|
|
|
| 1009 |
if len(tried_accounts) == len(multi_auth_manager.auth_managers):
|
| 1010 |
logger.info("所有现有账号都已尝试,开始注册新账号")
|
| 1011 |
successful_accounts = register_bot.register_and_verify(5)
|
| 1012 |
if successful_accounts:
|
| 1013 |
credentials = [(account['email'], account['password']) for account in successful_accounts]
|
| 1014 |
multi_auth_manager = MultiAuthManager(credentials)
|
|
|
|
| 1015 |
return make_request(payload, None, model_id)
|
| 1016 |
|
| 1017 |
raise Exception("所有账号均不可用,且注册新账号失败")
|
|
@@ -1067,9 +913,3 @@ if __name__ == "__main__":
|
|
| 1067 |
port = int(os.environ.get("PORT", 3000))
|
| 1068 |
app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
|
| 1069 |
|
| 1070 |
-
@atexit.register
|
| 1071 |
-
def cleanup():
|
| 1072 |
-
"""清理资源"""
|
| 1073 |
-
if proxy_manager:
|
| 1074 |
-
logger.info("Cleaning up resources...")
|
| 1075 |
-
|
|
|
|
| 8 |
import socket
|
| 9 |
from concurrent.futures import ThreadPoolExecutor
|
| 10 |
from functools import lru_cache, wraps
|
| 11 |
+
from typing import Dict, Any, Callable, List, Tuple
|
| 12 |
import requests
|
| 13 |
import tiktoken
|
| 14 |
from flask import Flask, Response, jsonify, request, stream_with_context
|
|
|
|
| 20 |
import threading
|
| 21 |
from time import sleep
|
| 22 |
from datetime import datetime, timedelta
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
# 新增导入
|
| 25 |
import register_bot
|
|
|
|
| 371 |
'accept-language': 'zh-CN,zh;q=0.9',
|
| 372 |
'content-type': 'application/json',
|
| 373 |
'user-agent': _USER_AGENT,
|
| 374 |
+
'authorization': f'Bearer {auth_manager.get_jwt_value()}'
|
|
|
|
| 375 |
}
|
| 376 |
headers_cache[cache_key] = headers
|
| 377 |
return headers
|
|
|
|
| 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
|
|
|
|
| 799 |
else:
|
| 800 |
raise Exception("无法注册新账号")
|
| 801 |
|
| 802 |
+
# 记录已尝试的账号
|
| 803 |
tried_accounts = set()
|
| 804 |
|
| 805 |
while len(tried_accounts) < len(multi_auth_manager.auth_managers):
|
|
|
|
| 807 |
if not auth_manager:
|
| 808 |
break
|
| 809 |
|
| 810 |
+
# 如果这个账号已经尝试过,继续下一个
|
| 811 |
if auth_manager._email in tried_accounts:
|
| 812 |
continue
|
| 813 |
|
|
|
|
| 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("所有账号均不可用,且注册新账号失败")
|
|
|
|
| 913 |
port = int(os.environ.get("PORT", 3000))
|
| 914 |
app.run(debug=False, host='0.0.0.0', port=port, threaded=True)
|
| 915 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
aiohttp==3.8.1
|
|
|
|
| 8 |
beautifulsoup4==4.11.1
|
| 9 |
Pillow==9.2.0
|
| 10 |
lxml==4.9.1
|
|
|