Files

290 lines
9.6 KiB
Python
Raw Permalink 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.
# 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()