ProxyCat/modules/modules.py
本间白猫 086d37aa73 2.0.4
2025-04-01 15:03:42 +08:00

664 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio, logging, random, httpx, re, os, time
from configparser import ConfigParser
from packaging import version
from colorama import Fore, Style
class ColoredFormatter(logging.Formatter):
COLORS = {
logging.INFO: Fore.GREEN,
logging.WARNING: Fore.YELLOW,
logging.ERROR: Fore.RED,
logging.CRITICAL: Fore.RED + Style.BRIGHT,
}
def format(self, record):
log_color = self.COLORS.get(record.levelno, Fore.WHITE)
record.msg = f"{log_color}{record.msg}{Style.RESET_ALL}"
return super().format(record)
MESSAGES = {
'cn': {
'getting_new_proxy': '正在获取新的代理IP',
'new_proxy_is': '新的代理IP为: {}',
'proxy_check_start': '开始检测代理地址...',
'proxy_check_disabled': '代理检测已禁用',
'valid_proxies': '有效代理地址: {}',
'no_valid_proxies': '没有有效的代理地址',
'proxy_check_failed': '{}代理 {} 检测失败: {}',
'proxy_switch': '切换代理: {} -> {}',
'proxy_consecutive_fails': '代理 {} 连续失败 {} 次,正在切换新代理',
'proxy_invalid': '代理 {} 无效,立即切换代理',
'proxy_failure': '代理检测失败: {}',
'proxy_failure_threshold': '代理无效,开始切换',
'proxy_check_error': '代理检查时发生错误: {}',
'connection_timeout': '连接超时',
'data_transfer_timeout': '数据传输超时,正在重试...',
'connection_reset': '连接被重置',
'transfer_cancelled': '传输被取消',
'data_transfer_error': '数据传输错误: {}',
'unsupported_protocol': '不支持的协议请求: {}',
'client_error': '客户端处理出错: {}',
'response_write_error': '响应写入错误: {}',
'server_closing': '服务器正在关闭...',
'program_interrupted': '程序被用户中断',
'multiple_proxy_fail': '多次尝试获取有效代理失败,退出程序',
'current_proxy': '当前代理',
'next_switch': '下次切换',
'seconds': '',
'no_proxies_available': '没有可用的代理',
'proxy_file_not_found': '代理文件不存在: {}',
'auth_not_set': '未设置 (无需认证)',
'public_account': '公众号',
'blog': '博客',
'proxy_mode': '代理轮换模式',
'cycle': '循环',
'loadbalance': '负载均衡',
'single_round': '单轮',
'proxy_interval': '代理更换时间',
'default_auth': '默认账号密码',
'local_http': '本地监听地址 (HTTP)',
'local_socks5': '本地监听地址 (SOCKS5)',
'star_project': '开源项目求 Star',
'client_handle_error': '客户端处理错误: {}',
'proxy_invalid_switch': '代理无效,切换代理',
'request_fail_retry': '请求失败,重试剩余次数: {}',
'user_interrupt': '用户中断程序',
'new_version_found': '发现新版本!',
'visit_quark': '夸克网盘: https://pan.quark.cn/s/39b4b5674570',
'visit_github': 'GitHub: https://github.com/honmashironeko/ProxyCat',
'visit_baidu': '百度网盘: https://pan.baidu.com/s/1C9LVC9aiaQeYFSj_2mWH1w?pwd=13r5',
'latest_version': '当前已是最新版本',
'version_info_not_found': '未找到版本信息',
'update_check_error': '检查更新失败: {}',
'unauthorized_ip': '未授权的IP尝试访问: {}',
'client_cancelled': '客户端连接已取消',
'socks5_connection_error': 'SOCKS5连接错误: {}',
'connect_timeout': '连接超时',
'connection_reset': '连接被重置',
'transfer_cancelled': '传输已取消',
'client_request_error': '客户端请求错误: {}',
'unsupported_protocol': '不支持的协议: {}',
'request_retry': '请求失败,重试中 (剩余{}次)',
'response_write_error': '写入响应时出错: {}',
'consecutive_failures': '检测到连续代理失败: {}',
'invalid_proxy': '当前代理无效: {}',
'whitelist_error': '添加白名单失败: {}',
'api_mode_notice': '当前为API模式收到请求将自动获取代理地址',
'server_running': '代理服务器运行在 {}:{}',
'server_start_error': '服务器启动错误: {}',
'server_shutting_down': '正在关闭服务器...',
'client_process_error': '处理客户端请求时出错: {}',
'request_handling_error': '请求处理错误: {}',
'proxy_forward_error': '代理转发错误: {}',
'data_transfer_timeout': '{}数据传输超时',
'data_transfer_error': '{}数据传输错误: {}',
'status_update_error': '状态更新出错',
'display_level_notice': '当前显示级别: {}',
'display_level_desc': '''显示级别说明:
0: 仅显示代理切换和错误信息
1: 显示代理切换、倒计时和错误信息
2: 显示所有详细信息''',
'new_client_connect': '新客户端连接 - IP: {}, 用户: {}',
'no_auth': '无认证',
'connection_error': '连接处理错误: {}',
'cleanup_error': '清理IP错误: {}',
'port_changed': '端口已更改: {} -> {},需要重启服务器生效',
'config_updated': '服务器配置已更新',
'load_proxy_file_error': '加载代理文件失败: {}',
'proxy_check_result': '代理检查完成,有效代理:{}',
'no_proxy': '无代理',
'cycle_mode': '循环模式',
'loadbalance_mode': '负载均衡模式',
'proxy_check_start': '开始检查代理...',
'proxy_check_complete': '代理检查完成',
'proxy_save_success': '代理保存成功',
'proxy_save_failed': '代理保存失败: {}',
'ip_list_save_success': 'IP名单保存成功',
'ip_list_save_failed': 'IP名单保存失败: {}',
'switch_success': '代理切换成功',
'switch_failed': '代理切换失败: {}',
'service_start_success': '服务启动成功',
'service_start_failed': '服务启动失败',
'service_already_running': '服务已在运行',
'service_stop_success': '服务停止成功',
'service_not_running': '服务未在运行',
'service_restart_success': '服务重启成功',
'service_restart_failed': '服务重启失败',
'invalid_action': '无效的操作',
'operation_failed': '操作失败: {}',
'logs_cleared': '日志已清除',
'clear_logs_failed': '清除日志失败: {}',
'unsupported_language': '不支持的语言',
'language_changed': '语言已切换为{}',
'loading': '加载中...',
'get_proxy_failed': '获取新代理失败: {}',
'log_level_all': '全部',
'log_level_info': '信息',
'log_level_warning': '警告',
'log_level_error': '错误',
'log_level_critical': '严重错误',
'confirm_clear_logs': '确定要清除所有日志吗?此操作不可恢复。',
'language_label': '语言',
'chinese': '中文',
'english': 'English',
'manual_switch_btn': '手动切换',
'service_control_title': '服务控制',
'language_switch_success': '',
'language_switch_failed': '',
'refresh_failed': '刷新数据失败: {}',
'auth_username_label': '认证用户名',
'auth_password_label': '认证密码',
'proxy_auth_username_label': '代理认证用户名',
'proxy_auth_password_label': '代理认证密码',
'progress_bar_label': '切换进度',
'proxy_settings_title': '代理设置',
'config_save_success': '配置保存成功',
'config_save_failed': '配置保存失败:{}',
'config_restart_required': '配置已更改,需要重启服务器生效',
'confirm_restart_service': '是否立即重启服务器?',
'service_status': '服务状态',
'running': '运行中',
'stopped': '已停止',
'restarting': '重启中',
'unknown': '未知',
'service_start_failed': '服务启动失败:{}',
'service_stop_failed': '服务停止失败:{}',
'service_restart_failed': '服务重启失败:{}',
'invalid_token': '无效的访问令牌',
'config_file_changed': '检测到配置文件更改,正在重新加载...',
'proxy_file_changed': '代理文件已更改,正在重新加载...',
'test_target_label': '测试目标地址',
'invalid_test_target': '无效的测试目标地址',
'users_save_success': '用户保存成功',
'users_save_failed': '用户保存失败:{}',
'user_management_title': '用户管理',
'username_column': '用户名',
'password_column': '密码',
'actions_column': '操作',
'add_user_btn': '添加用户',
'enter_username': '请输入用户名',
'enter_password': '请输入密码',
'confirm_delete_user': '确定要删除该用户吗?',
'no_logs_found': '未找到匹配的日志',
'clear_search': '清除搜索',
'web_panel_url': '网页控制面板地址: {}',
'web_panel_notice': '请使用浏览器访问上述地址来管理代理服务器',
'api_proxy_settings_title': 'API代理设置',
'all_retries_failed': '所有重试均已失败,最后错误: {}',
'proxy_get_failed': '获取代理失败',
'proxy_get_error': '获取代理错误: {}',
'request_error': '请求错误: {}',
'proxy_switch_error': '代理切换错误: {}',
},
'en': {
'getting_new_proxy': 'Getting new proxy IP',
'new_proxy_is': 'New proxy IP is: {}',
'proxy_check_start': 'Starting proxy check...',
'proxy_check_disabled': 'Proxy check is disabled',
'valid_proxies': 'Valid proxies: {}',
'no_valid_proxies': 'No valid proxies found',
'proxy_check_failed': '{} proxy {} check failed: {}',
'proxy_switch': 'Switch proxy: {} -> {}',
'proxy_consecutive_fails': 'Proxy {} failed {} times consecutively, switching to new proxy',
'proxy_invalid': 'Proxy {} is invalid, switching proxy immediately',
'proxy_failure': 'Proxy check failed: {}',
'proxy_failure_threshold': 'Proxy invalid, switching now',
'proxy_check_error': 'Error occurred during proxy check: {}',
'connection_timeout': 'Connection timeout',
'data_transfer_timeout': 'Data transfer timeout, retrying...',
'connection_reset': 'Connection reset',
'transfer_cancelled': 'Transfer cancelled',
'data_transfer_error': 'Data transfer error: {}',
'unsupported_protocol': 'Unsupported protocol request: {}',
'client_error': 'Client handling error: {}',
'response_write_error': 'Response write error: {}',
'server_closing': 'Server is closing...',
'program_interrupted': 'Program interrupted by user',
'multiple_proxy_fail': 'Multiple attempts to get valid proxy failed, exiting',
'current_proxy': 'Current Proxy',
'next_switch': 'Next Switch',
'seconds': 's',
'no_proxies_available': 'No proxies available',
'proxy_file_not_found': 'Proxy file not found: {}',
'auth_not_set': 'Not set (No authentication required)',
'public_account': 'WeChat Public Number',
'blog': 'Blog',
'proxy_mode': 'Proxy Rotation Mode',
'cycle': 'Cycle',
'loadbalance': 'Load Balance',
'single_round': 'Single Round',
'proxy_interval': 'Proxy Change Interval',
'default_auth': 'Default Username and Password',
'local_http': 'Local Listening Address (HTTP)',
'local_socks5': 'Local Listening Address (SOCKS5)',
'star_project': 'Star the Project',
'client_handle_error': 'Client handling error: {}',
'proxy_invalid_switch': 'Proxy invalid, switching proxy',
'request_fail_retry': 'Request failed, retrying remaining times: {}',
'user_interrupt': 'User interrupted the program',
'new_version_found': 'New version available!',
'visit_quark': 'Quark Drive: https://pan.quark.cn/s/39b4b5674570',
'visit_github': 'GitHub: https://github.com/honmashironeko/ProxyCat',
'visit_baidu': 'Baidu Drive: https://pan.baidu.com/s/1C9LVC9aiaQeYFSj_2mWH1w?pwd=13r5',
'latest_version': 'You are using the latest version',
'version_info_not_found': 'Version information not found',
'update_check_error': 'Failed to check for updates: {}',
'unauthorized_ip': 'Unauthorized IP attempt: {}',
'client_cancelled': 'Client connection cancelled',
'socks5_connection_error': 'SOCKS5 connection error: {}',
'connect_timeout': 'Connection timeout',
'connection_reset': 'Connection reset',
'transfer_cancelled': 'Transfer cancelled',
'data_transfer_error': 'Data transfer error: {}',
'client_request_error': 'Client request handling error: {}',
'unsupported_protocol': 'Unsupported protocol: {}',
'request_retry': 'Request failed, retrying ({} left)',
'request_error': 'Error during request: {}',
'response_write_error': 'Error writing response: {}',
'consecutive_failures': 'Consecutive proxy failures detected for {}',
'invalid_proxy': 'Current proxy is invalid: {}',
'whitelist_error': 'Failed to add whitelist: {}',
'api_mode_notice': 'Currently in API mode, proxy address will be automatically obtained upon request',
'all_retries_failed': 'All retries failed, last error: {}',
'proxy_get_failed': 'Failed to get proxy',
'proxy_get_error': 'Error getting proxy: {}',
'proxy_switch_error': 'Error switching proxy: {}',
'server_running': 'Proxy server running at {}:{}',
'server_start_error': 'Server startup error: {}',
'server_shutting_down': 'Server shutting down...',
'client_process_error': 'Client processing error: {}',
'request_handling_error': 'Request handling error: {}',
'proxy_forward_error': 'Proxy forwarding error: {}',
'data_transfer_timeout': '{}data transfer timeout',
'status_update_error': 'Status update error',
'display_level_notice': 'Current display level: {}',
'display_level_desc': '''Display level description:
0: Only show proxy switch and error messages
1: Show proxy switch, countdown and error messages
2: Show all detailed information''',
'new_client_connect': 'New client connection - IP: {}, User: {}',
'no_auth': 'No authentication',
'connection_error': 'Connection handling error: {}',
'cleanup_error': 'Cleanup IP error: {}',
'port_changed': 'Port changed: {} -> {}, server restart required to take effect',
'config_updated': 'Server configuration updated',
'load_proxy_file_error': 'Failed to load proxy file: {}',
'proxy_check_result': 'Proxy check completed, valid proxies: {}',
'no_proxy': 'No proxy',
'cycle_mode': 'Cycle mode',
'loadbalance_mode': 'Load balance mode',
'proxy_check_start': 'Starting proxy check...',
'proxy_check_complete': 'Proxy check completed',
'proxy_save_success': 'Proxy saved successfully',
'proxy_save_failed': 'Failed to save proxy: {}',
'ip_list_save_success': 'IP list saved successfully',
'ip_list_save_failed': 'Failed to save IP list: {}',
'switch_success': 'Proxy switched successfully',
'switch_failed': 'Failed to switch proxy: {}',
'service_start_success': 'Service started successfully',
'service_start_failed': 'Failed to start service',
'service_already_running': 'Service is already running',
'service_stop_success': 'Service stopped successfully',
'service_not_running': 'Service is not running',
'service_restart_success': 'Service restarted successfully',
'service_restart_failed': 'Failed to restart service',
'invalid_action': 'Invalid action',
'operation_failed': 'Operation failed: {}',
'logs_cleared': 'Logs cleared',
'clear_logs_failed': 'Failed to clear logs: {}',
'unsupported_language': 'Unsupported language',
'language_changed': 'Language changed to {}',
'loading': 'Loading...',
'get_proxy_failed': 'Failed to get new proxy: {}',
'log_level_all': 'All',
'log_level_info': 'Info',
'log_level_warning': 'Warning',
'log_level_error': 'Error',
'log_level_critical': 'Critical',
'confirm_clear_logs': 'Are you sure you want to clear all logs? This action cannot be undone.',
'language_label': 'Language',
'chinese': 'Chinese',
'english': 'English',
'manual_switch_btn': 'Switch Manually',
'service_control_title': 'Service Control',
'language_switch_success': 'Language switched successfully',
'language_switch_failed': 'Failed to switch language',
'refresh_failed': 'Failed to refresh data: {}',
'auth_username_label': 'Authentication Username',
'auth_password_label': 'Authentication Password',
'proxy_auth_username_label': 'Proxy Authentication Username',
'proxy_auth_password_label': 'Proxy Authentication Password',
'progress_bar_label': 'Switch Progress',
'proxy_settings_title': 'Proxy Settings',
'config_save_success': 'Configuration saved successfully',
'config_save_failed': 'Failed to save configuration: {}',
'config_restart_required': 'Configuration changed, server restart required to take effect',
'confirm_restart_service': 'Restart server now?',
'service_status': 'Service Status',
'running': 'Running',
'stopped': 'Stopped',
'restarting': 'Restarting',
'unknown': 'Unknown',
'service_start_failed': 'Failed to start service: {}',
'service_stop_failed': 'Failed to stop service: {}',
'service_restart_failed': 'Failed to restart service: {}',
'invalid_token': 'Invalid access token',
'config_file_changed': 'Config file change detected, reloading...',
'proxy_file_changed': 'Proxy file changed, reloading...',
'test_target_label': 'Test Target Address',
'invalid_test_target': 'Invalid test target address',
'users_save_success': 'Users saved successfully',
'users_save_failed': 'Failed to save users: {}',
'user_management_title': 'User Management',
'username_column': 'Username',
'password_column': 'Password',
'actions_column': 'Actions',
'add_user_btn': 'Add User',
'enter_username': 'Please enter username',
'enter_password': 'Please enter password',
'confirm_delete_user': 'Are you sure you want to delete this user?',
'no_logs_found': 'No matching logs found',
'clear_search': 'Clear Search',
'web_panel_url': 'Web panel URL: {}',
'web_panel_notice': 'Please use a browser to access the above URL to manage the proxy server',
'api_proxy_settings_title': 'API Proxy Settings',
}
}
class MessageManager:
def __init__(self, messages=MESSAGES):
self.messages = messages
self.default_lang = 'cn'
def get(self, key, lang='cn', *args):
try:
return self.messages[lang][key].format(*args) if args else self.messages[lang][key]
except KeyError:
return self.messages[self.default_lang][key] if key in self.messages[self.default_lang] else key
message_manager = MessageManager(MESSAGES)
get_message = message_manager.get
def print_banner(config):
language = config.get('language', 'cn').lower()
has_auth = config.get('username') and config.get('password')
auth_info = f"{config.get('username')}:{config.get('password')}" if has_auth else get_message('auth_not_set', language)
http_addr = f"http://{auth_info}@127.0.0.1:{config.get('port')}" if has_auth else f"http://127.0.0.1:{config.get('port')}"
socks5_addr = f"socks5://{auth_info}@127.0.0.1:{config.get('port')}" if has_auth else f"socks5://127.0.0.1:{config.get('port')}"
banner_info = [
(get_message('public_account', language), '樱花庄的本间白猫'),
(get_message('blog', language), 'https://y.shironekosan.cn'),
(get_message('proxy_mode', language), get_message('cycle', language) if config.get('mode') == 'cycle' else get_message('loadbalance', language) if config.get('mode') == 'loadbalance' else get_message('single_round', language)),
(get_message('proxy_interval', language), f"{config.get('interval')}{get_message('seconds', language)}"),
(get_message('default_auth', language), auth_info),
(get_message('local_http', language), http_addr),
(get_message('local_socks5', language), socks5_addr),
(get_message('star_project', language), 'https://github.com/honmashironeko/ProxyCat'),
]
print(f"{Fore.MAGENTA}{'=' * 55}")
for key, value in banner_info:
print(f"{Fore.YELLOW}{key}: {Fore.GREEN}{value}{Style.RESET_ALL}")
print(f"{Fore.MAGENTA}{'=' * 55}\n")
display_level = config.get('display_level', '1')
if int(display_level) >= 2:
print(f"\n{Fore.CYAN}{get_message('display_level_desc', language)}{Style.RESET_ALL}")
else:
print(f"\n{Fore.CYAN}{get_message('display_level_notice', language).format(display_level)}{Style.RESET_ALL}")
logo1 = r"""
|\ _,,,---,,_ by 本间白猫
ZZZzz /,`.-'`' -. ;-;;,_
|,4- ) )-,_. ,\ ( `'-'
'---''(_/--' `-'\_) ProxyCat
"""
logo2 = r"""
* ,MMM8&&&. *
MMMM88&&&&& .
MMMM88&&&&&&&
* MMM88&&&&&&&&
MMM88&&&&&&&&
'MMM88&&&&&&'
'MMM8&&&' *
/\/|_ __/\\
/ -\ /- ~\ . '
\ =_YT_ = /
/==*(` `\ ~ \ ProxyCat
/ \ / `\ by 本间白猫
| | ) ~ (
/ \ / ~ \\
\ / \~ ~/
_/\_/\_/\__ _/_/\_/\__~__/_/\_/\_/\_/\_/\_
| | | | ) ) | | | (( | | | | | |
| | | |( ( | | | \\ | | | | | |
| | | | )_) | | | |))| | | | | |
| | | | | | | | (/ | | | | | |
| | | | | | | | | | | | | | |
"""
logo3 = r"""
/\_/\ _
/`` \ / )
|n n |__ ( (
=(Y =.'` `\ \ \\
{`"` \ ) )
{ / |/ /
\\ ,( / /
ProxyCat) ) /-'\ ,_.' by 本间白猫
(,(,/ ((,,/
"""
logo4 = r"""
.-o=o-.
, /=o=o=o=\ .--.
_|\|=o=O=o=O=| \\
__.' a`\=o=o=o=(`\ /
'. a 4/`|.-""'`\ \ ;'`) .---.
\ .' / .--' |_.' / .-._)
by 本间白猫 `) _.' / /`-.__.' /
ProxyCat `'-.____; /'-.___.-'
`\"""`
"""
logos_list = [logo1, logo2, logo3, logo4]
def logos():
selected_logo = random.choice(logos_list)
print(selected_logo)
DEFAULT_CONFIG = {
'port': '1080',
'mode': 'cycle',
'interval': '300',
'username': 'neko',
'password': '123456',
'use_getip': 'False',
'proxy_file': 'ip.txt',
'check_proxies': 'True',
'whitelist_file': '',
'blacklist_file': '',
'ip_auth_priority': 'whitelist',
'language': 'cn'
}
def load_config(config_file='config/config.ini'):
try:
config = ConfigParser()
config.read(config_file, encoding='utf-8')
if not config.has_section('Server'):
config.add_section('Server')
for key, value in DEFAULT_CONFIG.items():
config.set('Server', key, str(value))
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
result = dict(config.items('Server'))
if config.has_section('Users'):
result['Users'] = dict(config.items('Users'))
return result
except Exception as e:
logging.error(f"Error loading config: {e}")
return DEFAULT_CONFIG.copy()
def load_ip_list(file_path):
try:
config_path = os.path.join('config', os.path.basename(file_path))
if os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
return set(line.strip() for line in f if line.strip())
except Exception as e:
logging.error(f"Error loading IP list: {e}")
return set()
_proxy_check_cache = {}
_proxy_check_ttl = 10
def parse_proxy(proxy):
try:
protocol = proxy.split('://')[0]
remaining = proxy.split('://')[1]
if '@' in remaining:
auth, address = remaining.split('@')
host, port = address.split(':')
return protocol, auth, host, int(port)
else:
host, port = remaining.split(':')
return protocol, None, host, int(port)
except Exception:
return None, None, None, None
async def check_http_proxy(proxy, test_url=None):
if test_url is None:
test_url = 'https://www.baidu.com'
protocol, auth, host, port = parse_proxy(proxy)
proxies = {}
if auth:
proxies['http://'] = f'{protocol}://{auth}@{host}:{port}'
proxies['https://'] = f'{protocol}://{auth}@{host}:{port}'
else:
proxies['http://'] = f'{protocol}://{host}:{port}'
proxies['https://'] = f'{protocol}://{host}:{port}'
try:
async with httpx.AsyncClient(proxies=proxies, timeout=10, verify=False) as client:
try:
response = await client.get(test_url)
return response.status_code == 200
except:
if test_url.startswith('https://'):
http_url = 'http://' + test_url[8:]
response = await client.get(http_url)
return response.status_code == 200
return False
except:
return False
async def check_socks_proxy(proxy, test_url=None):
if test_url is None:
test_url = 'https://www.baidu.com'
protocol, auth, host, port = parse_proxy(proxy)
if not all([host, port]):
return False
try:
reader, writer = await asyncio.wait_for(asyncio.open_connection(host, port), timeout=5)
if auth:
writer.write(b'\x05\x02\x00\x02')
else:
writer.write(b'\x05\x01\x00')
await writer.drain()
auth_method = await asyncio.wait_for(reader.readexactly(2), timeout=5)
if auth_method[0] != 0x05:
return False
if auth_method[1] == 0x02 and auth:
username, password = auth.split(':')
auth_packet = bytes([0x01, len(username)]) + username.encode() + bytes([len(password)]) + password.encode()
writer.write(auth_packet)
await writer.drain()
auth_response = await asyncio.wait_for(reader.readexactly(2), timeout=5)
if auth_response[1] != 0x00:
return False
from urllib.parse import urlparse
domain = urlparse(test_url).netloc if '://' in test_url else test_url
domain = domain.encode()
writer.write(b'\x05\x01\x00\x03' + bytes([len(domain)]) + domain + b'\x00\x50')
await writer.drain()
response = await asyncio.wait_for(reader.readexactly(10), timeout=5)
writer.close()
try:
await writer.wait_closed()
except:
pass
return response[1] == 0x00
except Exception:
return False
async def check_proxy(proxy, test_url=None):
current_time = time.time()
cache_key = f"{proxy}:{test_url}"
if cache_key in _proxy_check_cache:
cache_time, is_valid = _proxy_check_cache[cache_key]
if current_time - cache_time < _proxy_check_ttl:
return is_valid
proxy_type = proxy.split('://')[0]
check_funcs = {
'http': check_http_proxy,
'https': check_http_proxy,
'socks5': check_socks_proxy
}
if proxy_type not in check_funcs:
return False
try:
test_url = test_url or 'https://www.baidu.com'
is_valid = await check_funcs[proxy_type](proxy, test_url)
_proxy_check_cache[cache_key] = (current_time, is_valid)
return is_valid
except Exception:
_proxy_check_cache[cache_key] = (current_time, False)
return False
async def check_proxies(proxies, test_url=None):
valid_proxies = []
for proxy in proxies:
if await check_proxy(proxy, test_url):
valid_proxies.append(proxy)
return valid_proxies
async def check_for_updates(language='cn'):
try:
async with httpx.AsyncClient() as client:
response = await asyncio.wait_for(client.get("https://y.shironekosan.cn/1.html"), timeout=10)
response.raise_for_status()
content = response.text
match = re.search(r'<p>(ProxyCat-V\d+\.\d+\.\d+)</p>', content)
if match:
latest_version = match.group(1)
CURRENT_VERSION = "ProxyCat-V2.0.4"
if version.parse(latest_version.split('-V')[1]) > version.parse(CURRENT_VERSION.split('-V')[1]):
print(f"{Fore.YELLOW}{get_message('new_version_found', language)} 当前版本: {CURRENT_VERSION}, 最新版本: {latest_version}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{get_message('visit_quark', language)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{get_message('visit_github', language)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{get_message('visit_baidu', language)}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{get_message('latest_version', language)} ({CURRENT_VERSION}){Style.RESET_ALL}")
else:
print(f"{Fore.RED}{get_message('version_info_not_found', language)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{get_message('update_check_error', language, e)}{Style.RESET_ALL}")