Continue seeded Qinglong script collection
This commit is contained in:
@@ -0,0 +1,540 @@
|
||||
# Source: https://github.com/3ixi/CodeScripts/blob/main/envRefresh.py
|
||||
# Raw: https://raw.githubusercontent.com/3ixi/CodeScripts/main/envRefresh.py
|
||||
# Repo: 3ixi/CodeScripts
|
||||
# Path: envRefresh.py
|
||||
# UploadedAt: 2025-09-23T14:07:43+08:00
|
||||
# SHA256: 9b6e74e8f24750c6fa1c5311177fdffc0a57605c711d266f5e28ad75f4aed03f
|
||||
# Category: 微信小程序/抓包版
|
||||
# Evidence: appid/openid/session_key/encryptedData等小程序字段
|
||||
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
青龙面板环境变量更新脚本
|
||||
用于自动获取并更新青龙面板中的指定环境变量值
|
||||
脚本作者:3iXi
|
||||
创建时间:2025/09/22
|
||||
!!此脚本需要配合iPad协议服务使用!!
|
||||
需要自定义依赖:
|
||||
getCode.py(https://github.com/3ixi/CodeScripts)
|
||||
cloud_auth.py(https://github.com/3ixi/CloudScripts)
|
||||
SendNotify.py(https://github.com/3ixi/CloudScripts)
|
||||
--------------------------
|
||||
2025/09/22 更新支持:捷停车、老板电器、雀巢会员俱乐部
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Tuple, Any, Optional
|
||||
|
||||
import getCode
|
||||
import cloud_auth
|
||||
import SendNotify
|
||||
|
||||
# 青龙面板API相关常量
|
||||
API_ENDPOINTS = {
|
||||
'auth': '/open/auth/token',
|
||||
'envs': '/open/envs'
|
||||
}
|
||||
BASE_HEADERS = {
|
||||
'User-Agent': 'envRefresh_3iXi/1.0.1',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
|
||||
ENV_UPDATE_CONFIG = {
|
||||
'quechao': {
|
||||
'appid': 'wxc5db704249c9bb31',
|
||||
'api_url': 'https://crm.nestlechinese.com/openapi/identityservice/connect/token',
|
||||
'api_host': 'crm.nestlechinese.com',
|
||||
'name': '雀巢会员俱乐部'
|
||||
},
|
||||
'laobandianqi': {
|
||||
'appid': 'wxba70fb8e3eb3aab9',
|
||||
'api_url': 'https://aio.myroki.com/api/v1/mini-app/user/login',
|
||||
'api_host': 'aio.myroki.com',
|
||||
'name': '老板电器'
|
||||
},
|
||||
'jtc': {
|
||||
'appid': 'wx24b70f0ad2a9a89a',
|
||||
'api_url': 'https://www.jslife.com.cn/wxhttp/weixin/xcx/get_openid_by_code',
|
||||
'api_host': 'www.jslife.com.cn',
|
||||
'name': '捷停车'
|
||||
}
|
||||
}
|
||||
|
||||
class QinglongEnvUpdater:
|
||||
"""青龙面板环境变量更新器"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化,获取必要的环境变量"""
|
||||
# 获取青龙面板认证信息
|
||||
env_refresh = os.getenv('envRefresh')
|
||||
if not env_refresh:
|
||||
raise ValueError("未找到环境变量'envRefresh',请访问青龙面板的“系统设置”-“应用设置”创建包含“环境变量”权限的应用,按照client_id&client_secret格式创建envRefresh环境变量")
|
||||
|
||||
parts = env_refresh.split('&')
|
||||
if len(parts) != 2:
|
||||
raise ValueError("环境变量'envRefresh'格式错误,应为client_id&client_secret")
|
||||
|
||||
self.client_id = parts[0]
|
||||
self.client_secret = parts[1]
|
||||
|
||||
# 获取青龙面板URL:优先使用环境变量,其次尝试本地默认地址
|
||||
env_qinglong = os.getenv('qinglongURL')
|
||||
|
||||
if env_qinglong:
|
||||
self.qinglong_url = env_qinglong.rstrip('/')
|
||||
else:
|
||||
# 默认尝试本地青龙地址
|
||||
default_url = 'http://127.0.0.1:5700'
|
||||
# 测试默认地址是否可达
|
||||
try:
|
||||
test_resp = requests.get(default_url, headers=BASE_HEADERS, timeout=3)
|
||||
if 200 <= test_resp.status_code < 400:
|
||||
self.qinglong_url = default_url
|
||||
else:
|
||||
raise Exception('默认地址返回异常状态码')
|
||||
except Exception:
|
||||
# 默认地址不可达,提示用户需要创建环境变量
|
||||
raise ValueError("未检测到可用的青龙面板地址。请创建环境变量'qinglongURL',值为青龙面板的Web访问地址(例如 http://192.168.110.102:5700)")
|
||||
|
||||
self.token = None
|
||||
self.env_list = []
|
||||
|
||||
def _make_request(self, method: str, url: str, **kwargs) -> Dict:
|
||||
"""统一请求处理"""
|
||||
headers = BASE_HEADERS.copy()
|
||||
if 'headers' in kwargs:
|
||||
headers.update(kwargs['headers'])
|
||||
del kwargs['headers']
|
||||
|
||||
try:
|
||||
response = requests.request(method, url, headers=headers, **kwargs, timeout=30)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# 统一错误处理
|
||||
if 'statusCode' in data:
|
||||
raise Exception(f"请求失败: {data.get('message', '未知错误')}")
|
||||
|
||||
return data
|
||||
except requests.RequestException as e:
|
||||
raise Exception(f"网络请求失败: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
raise Exception(f"响应数据解析失败: {e}")
|
||||
|
||||
def authenticate(self):
|
||||
"""获取青龙面板认证token"""
|
||||
url = f"{self.qinglong_url}{API_ENDPOINTS['auth']}"
|
||||
params = {
|
||||
'client_id': self.client_id,
|
||||
'client_secret': self.client_secret
|
||||
}
|
||||
|
||||
data = self._make_request('GET', url, params=params)
|
||||
|
||||
if 'data' in data and 'token' in data['data']:
|
||||
self.token = data['data']['token']
|
||||
print("✅ 青龙面板认证成功")
|
||||
else:
|
||||
raise Exception("认证失败,未找到token")
|
||||
def get_envs(self):
|
||||
"""获取环境变量列表"""
|
||||
if not self.token:
|
||||
raise Exception("请先进行认证")
|
||||
|
||||
url = f"{self.qinglong_url}{API_ENDPOINTS['envs']}"
|
||||
headers = {
|
||||
'Authorization': f'Bearer {self.token}'
|
||||
}
|
||||
|
||||
data = self._make_request('GET', url, headers=headers)
|
||||
|
||||
if 'data' in data:
|
||||
self.env_list = data['data']
|
||||
print(f"✅ 获取到{len(self.env_list)}个环境变量")
|
||||
else:
|
||||
raise Exception("获取环境变量列表失败")
|
||||
|
||||
def _get_env_by_name(self, name: str) -> Optional[Dict]:
|
||||
"""根据名称获取环境变量"""
|
||||
for env in self.env_list:
|
||||
if env.get('name') == name:
|
||||
return env
|
||||
return None
|
||||
|
||||
def _update_env_value(self, env_id: int, name: str, value: str, remarks_prefix: str):
|
||||
"""更新环境变量值"""
|
||||
if not self.token:
|
||||
raise Exception("请先进行认证")
|
||||
|
||||
url = f"{self.qinglong_url}{API_ENDPOINTS['envs']}"
|
||||
headers = {
|
||||
'Authorization': f'Bearer {self.token}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
today = datetime.now().strftime('%Y%m%d')
|
||||
remarks = f"{remarks_prefix}{today}更新"
|
||||
|
||||
payload = {
|
||||
'id': env_id,
|
||||
'name': name,
|
||||
'value': value,
|
||||
'remarks': remarks
|
||||
}
|
||||
|
||||
data = self._make_request('PUT', url, headers=headers, json=payload)
|
||||
|
||||
if data.get('code') == 200:
|
||||
print(f"✅ 环境变量{name}更新成功")
|
||||
print("-" * 30)
|
||||
else:
|
||||
raise Exception(f"环境变量{name}更新失败: {data}")
|
||||
|
||||
def _update_quechao_env(self, env_info: Dict):
|
||||
"""更新雀巢环境变量"""
|
||||
print("🔄 开始更新雀巢会员俱乐部环境变量...")
|
||||
|
||||
# 获取Code列表
|
||||
app_id = ENV_UPDATE_CONFIG['quechao']['appid']
|
||||
codes = getCode.get_wechat_codes(app_id)
|
||||
|
||||
if not codes:
|
||||
raise Exception("未获取到任何Code")
|
||||
|
||||
# 获取新Token
|
||||
access_tokens = []
|
||||
for nick_name, code in codes.items():
|
||||
print(f"🎫 正在获取{nick_name}的新Token...")
|
||||
|
||||
headers = {
|
||||
'host': ENV_UPDATE_CONFIG['quechao']['api_host'],
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541022) XWEB/16467'
|
||||
}
|
||||
|
||||
payload = {
|
||||
'client_id': 'wechatMini',
|
||||
'client_secret': 'secret',
|
||||
'grant_type': 'wechat_auth_code',
|
||||
'auth_code': code
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
ENV_UPDATE_CONFIG['quechao']['api_url'],
|
||||
headers=headers,
|
||||
data=payload,
|
||||
timeout=30
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if 'access_token' in result:
|
||||
access_tokens.append(result['access_token'])
|
||||
print(f"✅ {nick_name}新Token获取成功")
|
||||
else:
|
||||
error_msg = result.get('error', '未知错误')
|
||||
print(f"❌ {nick_name}新Token获取失败: {error_msg}")
|
||||
except Exception as e:
|
||||
print(f"❌ {nick_name}新Token获取失败: {e}")
|
||||
|
||||
if not access_tokens:
|
||||
raise Exception("未获取到任何有效的新Token")
|
||||
|
||||
# 更新环境变量
|
||||
new_value = "#".join(access_tokens)
|
||||
self._update_env_value(
|
||||
env_info['id'],
|
||||
env_info['name'],
|
||||
new_value,
|
||||
ENV_UPDATE_CONFIG['quechao']['name']
|
||||
)
|
||||
|
||||
return len(access_tokens)
|
||||
|
||||
def _update_laobandianqi_env(self, env_info: Dict):
|
||||
"""更新老板电器环境变量"""
|
||||
print("🔄 开始更新老板电器环境变量...")
|
||||
|
||||
# 获取Code列表
|
||||
app_id = ENV_UPDATE_CONFIG['laobandianqi']['appid']
|
||||
codes = getCode.get_wechat_codes(app_id)
|
||||
|
||||
if not codes:
|
||||
raise Exception("未获取到任何Code")
|
||||
|
||||
# 获取新Token
|
||||
tokens = []
|
||||
for nick_name, code in codes.items():
|
||||
print(f"🎫 正在获取{nick_name}的新Token...")
|
||||
|
||||
# 生成时间戳
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 使用云函数认证模块生成签名相关参数,调用云服务获取secret和signature
|
||||
auth_client = cloud_auth.get_auth_client()
|
||||
try:
|
||||
auth_resp = auth_client.call_service('roki', timestamp=timestamp)
|
||||
if auth_resp.get('success'):
|
||||
secret = auth_resp.get('secret', '')
|
||||
signature = auth_resp.get('signature', '')
|
||||
if not secret or not signature:
|
||||
print(f"❌ {nick_name} 获取认证信息失败: secret或signature为空")
|
||||
continue
|
||||
else:
|
||||
error_msg = auth_resp.get('error', '未知错误')
|
||||
print(f"❌ {nick_name} 获取认证信息失败: {error_msg}")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"❌ {nick_name} 获取认证信息失败: {e}")
|
||||
continue
|
||||
|
||||
headers = {
|
||||
'host': ENV_UPDATE_CONFIG['laobandianqi']['api_host'],
|
||||
'content-type': 'application/json',
|
||||
'x-app-env': 'release',
|
||||
'timestamp': str(timestamp),
|
||||
'nonce': '1234567890123456',
|
||||
'app-version': '5000',
|
||||
'app-id': 'roki_app',
|
||||
'signature': signature,
|
||||
'secret': secret
|
||||
}
|
||||
|
||||
payload = {
|
||||
"code": code,
|
||||
"param": {
|
||||
"qr-code": ""
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
ENV_UPDATE_CONFIG['laobandianqi']['api_url'],
|
||||
headers=headers,
|
||||
json=payload,
|
||||
timeout=30
|
||||
)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if result.get('success', False) and 'data' in result and 'token' in result['data']:
|
||||
tokens.append(result['data']['token'])
|
||||
print(f"✅ {nick_name}新Token获取成功")
|
||||
else:
|
||||
error_msg = result.get('message', '未知错误')
|
||||
print(f"❌ {nick_name}新Token获取失败: {error_msg}")
|
||||
except Exception as e:
|
||||
print(f"❌ {nick_name}新Token获取失败: {e}")
|
||||
|
||||
if not tokens:
|
||||
raise Exception("未获取到任何有效的新Token")
|
||||
|
||||
# 更新环境变量
|
||||
new_value = "#".join(tokens)
|
||||
self._update_env_value(
|
||||
env_info['id'],
|
||||
env_info['name'],
|
||||
new_value,
|
||||
ENV_UPDATE_CONFIG['laobandianqi']['name']
|
||||
)
|
||||
|
||||
return len(tokens)
|
||||
|
||||
def _update_jtc_env(self, env_info: Dict):
|
||||
"""更新捷停车环境变量(jtc)"""
|
||||
print("🔄 开始更新捷停车环境变量...")
|
||||
|
||||
app_id = ENV_UPDATE_CONFIG['jtc']['appid']
|
||||
codes = getCode.get_wechat_codes(app_id)
|
||||
|
||||
if not codes:
|
||||
raise Exception("未获取到任何Code")
|
||||
|
||||
tokens = []
|
||||
for nick_name, code in codes.items():
|
||||
print(f"🎫 正在获取{nick_name}的新Token...")
|
||||
|
||||
t = int(time.time() * 1000)
|
||||
|
||||
url = f"{ENV_UPDATE_CONFIG['jtc']['api_url']}?t={t}"
|
||||
|
||||
headers = {
|
||||
'Host': ENV_UPDATE_CONFIG['jtc']['api_host'],
|
||||
'Connection': 'keep-alive',
|
||||
'applicationVersion': '1.0.1',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf2541022) XWEB/16467',
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
'Accept': '*/*',
|
||||
'Referer': f"https://servicewechat.com/{app_id}/285/page-frame.html",
|
||||
'Accept-Encoding': 'gzip, deflate, br',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9'
|
||||
}
|
||||
|
||||
payload = {
|
||||
'code': code,
|
||||
'userType': 'WX_XCX_JTC',
|
||||
'appId': app_id
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=payload, timeout=30)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
|
||||
if str(result.get('resultCode')) != '0':
|
||||
message = result.get('message', '未知错误')
|
||||
print(f"❌ {nick_name} 获取新Token失败: {message}")
|
||||
continue
|
||||
|
||||
obj = result.get('obj', {})
|
||||
token = obj.get('token')
|
||||
if token:
|
||||
tokens.append(token)
|
||||
print(f"✅ {nick_name}新Token获取成功")
|
||||
else:
|
||||
print(f"❌ {nick_name} 响应中未包含token")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ {nick_name}新Token获取失败: {e}")
|
||||
|
||||
if not tokens:
|
||||
raise Exception("未获取到任何有效的新Token")
|
||||
|
||||
new_value = "#".join(tokens)
|
||||
self._update_env_value(
|
||||
env_info['id'],
|
||||
env_info['name'],
|
||||
new_value,
|
||||
ENV_UPDATE_CONFIG['jtc']['name']
|
||||
)
|
||||
|
||||
return len(tokens)
|
||||
|
||||
def update_envs(self) -> Tuple[int, int, List[Dict[str, Any]]]:
|
||||
"""更新所有支持的环境变量"""
|
||||
if not self.env_list:
|
||||
raise Exception("请先获取环境变量列表")
|
||||
|
||||
updated_env_count = 0
|
||||
updated_value_count = 0
|
||||
|
||||
# 收集每个环境变量的更新结果,便于推送通知
|
||||
per_env_results: List[Dict[str, Any]] = []
|
||||
|
||||
# 遍历支持更新的环境变量
|
||||
for env_name, config in ENV_UPDATE_CONFIG.items():
|
||||
env_info = self._get_env_by_name(env_name)
|
||||
if not env_info:
|
||||
msg = f"⚠️ 您未创建环境变量{env_name},本次跳过更新"
|
||||
print(msg)
|
||||
per_env_results.append({
|
||||
'key': env_name,
|
||||
'name': config.get('name', ''),
|
||||
'success': False,
|
||||
'count': 0,
|
||||
'message': '未创建此环境变量'
|
||||
})
|
||||
continue
|
||||
|
||||
try:
|
||||
if env_name == 'quechao':
|
||||
value_count = self._update_quechao_env(env_info)
|
||||
elif env_name == 'laobandianqi':
|
||||
value_count = self._update_laobandianqi_env(env_info)
|
||||
elif env_name == 'jtc':
|
||||
value_count = self._update_jtc_env(env_info)
|
||||
else:
|
||||
print(f"⚠️ 环境变量{env_name}暂不支持更新")
|
||||
per_env_results.append({
|
||||
'key': env_name,
|
||||
'name': config.get('name', ''),
|
||||
'success': False,
|
||||
'count': 0,
|
||||
'message': '不支持更新'
|
||||
})
|
||||
continue
|
||||
|
||||
# 成功更新
|
||||
updated_env_count += 1
|
||||
updated_value_count += value_count
|
||||
per_env_results.append({
|
||||
'key': env_name,
|
||||
'name': config.get('name', ''),
|
||||
'success': True,
|
||||
'count': value_count,
|
||||
'message': ''
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 环境变量{env_name}更新失败: {e}")
|
||||
per_env_results.append({
|
||||
'key': env_name,
|
||||
'name': config.get('name', ''),
|
||||
'success': False,
|
||||
'count': 0,
|
||||
'message': str(e)
|
||||
})
|
||||
|
||||
return updated_env_count, updated_value_count, per_env_results
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("🔄 青龙面板环境变量更新脚本开始执行...")
|
||||
print("=" * 30)
|
||||
try:
|
||||
updater = QinglongEnvUpdater()
|
||||
updater.authenticate()
|
||||
updater.get_envs()
|
||||
env_count, value_count, per_env_results = updater.update_envs()
|
||||
|
||||
print("=" * 30)
|
||||
print(f"✅ 脚本执行完成")
|
||||
# 构造更详细的推送内容,包含每个环境的中文名、key、成功/失败、数量与错误信息
|
||||
lines = []
|
||||
lines.append(f"本次成功更新了{env_count}个环境变量,共{value_count}个环境变量值")
|
||||
lines.append("")
|
||||
for r in per_env_results:
|
||||
name = r.get('name') or ''
|
||||
key = r.get('key')
|
||||
success = r.get('success')
|
||||
count = r.get('count')
|
||||
message = r.get('message') or ''
|
||||
|
||||
if success:
|
||||
lines.append(f"{name}({key})更新成功,更新了{count}个环境变量值")
|
||||
lines.append("=" * 30)
|
||||
else:
|
||||
lines.append(f"{name}({key})更新失败,更新了{count}个环境变量值")
|
||||
if message:
|
||||
lines.append(f" 错误信息: {message}")
|
||||
lines.append("=" * 30)
|
||||
|
||||
content = "\n".join(lines)
|
||||
# 发送推送通知
|
||||
try:
|
||||
title = "青龙环境变量更新脚本执行完成"
|
||||
SendNotify.SendNotify(title, content)
|
||||
except Exception as e:
|
||||
print(f"⚠️ 发送通知时出错: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 脚本执行失败: {e}")
|
||||
# 失败时也尝试发送通知
|
||||
try:
|
||||
title = "青龙环境变量更新脚本执行失败"
|
||||
content = f"脚本执行失败: {e}"
|
||||
SendNotify.SendNotify(title, content)
|
||||
except Exception as e2:
|
||||
print(f"⚠️ 发送失败通知时出错: {e2}")
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
Reference in New Issue
Block a user