# cron: 16 6 * * * # new Env("ipzan代理白名单管理") import requests import os import sys import time from datetime import datetime import warnings from urllib3.exceptions import InsecureRequestWarning from Crypto.Cipher import AES from Crypto.Util.Padding import pad import json # 禁用SSL警告 warnings.filterwarnings("ignore", category=InsecureRequestWarning) # ================= 配置区 ================= # 从单个环境变量中读取所有配置 CONFIG_STR = os.getenv('API_KEY_PZ') if not CONFIG_STR: print("错误:未找到API_KEY_PZ环境变量") sys.exit(1) # 解析环境变量:USER_ID#LOGIN_PASSWORD#KEY#ORDER_NO#EXTRACT_KEY try: parts = CONFIG_STR.split('#') if len(parts) < 5: raise ValueError("环境变量格式不正确") USER_ID = parts[0] LOGIN_PASSWORD = parts[1] API_KEY = parts[2] ORDER_NO = parts[3] EXTRACT_KEY = parts[4] # 打印配置信息(调试用) print(f"USER_ID: {USER_ID}") print(f"LOGIN_PASSWORD: {LOGIN_PASSWORD[:2]}****{LOGIN_PASSWORD[-2:]}") print(f"API_KEY: {API_KEY[:2]}****{API_KEY[-2:]}") print(f"ORDER_NO: {ORDER_NO}") print(f"EXTRACT_KEY: {EXTRACT_KEY[:2]}****{EXTRACT_KEY[-2:]}") except Exception as e: print(f"解析环境变量失败:{str(e)}") print("请确保环境变量格式为:USER_ID#LOGIN_PASSWORD#KEY#ORDER_NO#EXTRACT_KEY") sys.exit(1) BASE_URL = "https://service.ipzan.com" IP_SERVICES = [ {'url': 'http://ifconfig.me/ip', 'verify': False}, {'url': 'http://ip.3322.net', 'verify': False}, {'url': 'http://myip.ipip.net', 'verify': False} ] MAX_RETRIES = 3 REQUEST_DELAY = 2 # ========================================== def debug_log(msg, level="INFO"): """分级日志系统""" timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') color_code = { "DEBUG": "\033[94m", # 蓝色 "INFO": "\033[92m", # 绿色 "WARNING": "\033[93m", # 黄色 "ERROR": "\033[91m", # 红色 "SUCCESS": "\033[92m", # 绿色(成功) "SYSTEM": "\033[95m" # 紫色 }.get(level, "\033[0m") print(f"{color_code}## [{timestamp}] [{level}] {msg}\033[0m") def get_public_ip(): """获取公网IP""" debug_log("正在获取公网IP...", "DEBUG") for service in IP_SERVICES: try: response = requests.get(service['url'], timeout=5, verify=service['verify']) if response.status_code == 200: ip = response.text.strip() if validate_ip(ip): debug_log(f"验证通过的公网IP:{ip}", "SUCCESS") return ip except Exception as e: debug_log(f"{service['url']}查询失败:{str(e)}", "DEBUG") debug_log("所有IP服务不可用", "ERROR") return None def validate_ip(ip): """IP地址格式验证""" parts = ip.split('.') return len(parts) == 4 and all(part.isdigit() and 0 <= int(part) < 256 for part in parts) def generate_aes_signature(): """生成AES加密签名""" try: # 获取当前时间戳(秒) timestamp = str(int(time.time())) # 构造签名内容:登录密码:套餐提取密匙:时间戳 data = f"{LOGIN_PASSWORD}:{EXTRACT_KEY}:{timestamp}" # 将API_KEY转换为16字节密钥 key = API_KEY.encode('utf-8') # 确保密钥长度为16字节(AES-128) key = key.ljust(16, b'\0')[:16] # 创建AES加密器(ECB模式) cipher = AES.new(key, AES.MODE_ECB) # 对数据进行PKCS7填充 padded_data = pad(data.encode('utf-8'), AES.block_size) # 加密数据 encrypted_data = cipher.encrypt(padded_data) # 返回十六进制字符串 return encrypted_data.hex() except Exception as e: debug_log(f"AES签名生成失败:{str(e)}", "ERROR") return None def add_ip(ip): """添加IP到白名单(使用AES签名)""" url = f"{BASE_URL}/whiteList-add" # 生成AES签名 sign = generate_aes_signature() if not sign: debug_log("无法生成AES签名", "ERROR") return False params = { 'no': ORDER_NO, 'sign': sign, 'ip': ip, 'replace': 1 # 启用自动替换功能 } debug_log(f"正在添加IP:{ip}", "INFO") try: response = requests.get(url, params=params, timeout=15) debug_log(f"添加接口原始响应:{response.text}", "DEBUG") # 尝试解析JSON响应 try: data = response.json() except ValueError: debug_log("响应不是有效的JSON格式", "ERROR") return False if data.get('code') == 0: debug_log(f"IP {ip} 添加成功", "SUCCESS") return True error_msg = data.get('message') or '未知错误' debug_log(f"添加失败:{error_msg}(状态码:{data.get('code')})", "ERROR") except Exception as e: debug_log(f"请求添加接口失败:{str(e)}", "ERROR") return False def verify_ip(ip): """校验IP是否在白名单中""" url = f"{BASE_URL}/whiteList-verify" params = { 'no': ORDER_NO, 'ip': ip } try: response = requests.get(url, params=params, timeout=10) debug_log(f"校验接口原始响应:{response.text}", "DEBUG") # 尝试解析JSON响应 try: data = response.json() except ValueError: debug_log("响应不是有效的JSON格式", "ERROR") return None if data.get('code') == 0: # 根据实际接口返回结构调整 if data.get('data') is True: debug_log(f"IP {ip} 已在白名单中", "SUCCESS") return True else: debug_log(f"IP {ip} 不在白名单中", "INFO") return False error_msg = data.get('message') or '未知错误' debug_log(f"校验失败:{error_msg}(状态码:{data.get('code')})", "ERROR") except Exception as e: debug_log(f"请求校验接口失败:{str(e)}", "ERROR") return None def get_balance(): """查询套餐余额和余量""" url = f"{BASE_URL}/userProduct-get" params = { 'no': ORDER_NO, 'userId': USER_ID # 保持参数名不变 } try: # 打印完整的请求URL以便调试 full_url = f"{url}?no={ORDER_NO}&userId={USER_ID}" debug_log(f"余额查询请求URL: {full_url}", "DEBUG") response = requests.get(url, params=params, timeout=10) debug_log(f"余额查询接口原始响应:{response.text}", "DEBUG") # 尝试解析JSON响应 try: data = response.json() except ValueError: debug_log("响应不是有效的JSON格式", "ERROR") return None if data.get('code') == 0: balance_info = data.get('data', {}) debug_log("余额查询成功", "SUCCESS") return balance_info error_msg = data.get('message') or '未知错误' debug_log(f"余额查询失败:{error_msg}(状态码:{data.get('code')})", "ERROR") except Exception as e: debug_log(f"请求余额查询接口失败:{str(e)}", "ERROR") return None def main(): debug_log("===== ipzan代理白名单管理 =====", "SYSTEM") # 第一步:查询套餐余额(可选步骤) debug_log("查询套餐余额...", "INFO") try: balance_info = get_balance() if balance_info: # 格式化显示余额信息 balance_str = [] if 'balance' in balance_info: balance_str.append(f"钱包余额: {balance_info['balance']}") if 'limitDaySurplus' in balance_info: balance_str.append(f"今日余量: {balance_info['limitDaySurplus']}") if 'totalSurplus' in balance_info: balance_str.append(f"套餐总量: {balance_info['totalSurplus']}") if balance_str: debug_log(" | ".join(balance_str), "INFO") else: debug_log("余额查询失败,跳过余额显示", "WARNING") except Exception as e: debug_log(f"余额查询过程中出错:{str(e)}", "ERROR") # 第二步:获取当前公网IP public_ip = get_public_ip() if not public_ip: debug_log("无法获取有效公网IP", "FATAL") sys.exit(1) # 第三步:检查当前IP是否已在白名单 debug_log("检查当前IP状态...", "INFO") ip_status = verify_ip(public_ip) if ip_status is None: debug_log("IP校验失败,请检查网络或服务状态", "FATAL") sys.exit(1) if ip_status: debug_log("当前IP已在白名单中,无需操作", "SUCCESS") sys.exit(0) debug_log("当前IP不在白名单中,开始添加流程...", "WARNING") # 第四步:添加当前公网IP(启用自动替换功能) if not add_ip(public_ip): debug_log("新IP添加失败", "FATAL") sys.exit(1) # 第五步:最终验证 debug_log("等待接口数据刷新...", "SYSTEM") time.sleep(5) final_status = verify_ip(public_ip) if final_status is True: debug_log(f"最终验证通过:{public_ip} 已生效", "SUCCESS") else: debug_log(f"最终验证失败:请手动检查白名单状态", "FATAL") sys.exit(1) debug_log("===== 操作成功完成 =====", "SYSTEM") if __name__ == '__main__': main()