# 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())