├── .github ├── scripts │ └── generate_image_json.py └── workflows │ └── blank.yml ├── README.md ├── icons ├── aliyun_web.png ├── cbd.png ├── cfmoto.png ├── iios.png ├── lhtj.png ├── netease_music.png ├── ningji.png ├── picc.png ├── pyy.png ├── wdzhsy.png ├── wx.png ├── xmyx.png ├── yrh.png ├── zeeho.png └── zmhy.png ├── loon └── plugin │ ├── aliyun_web │ └── aliyun_web.plugin │ ├── cfmoto │ └── cfmoto.plugin │ ├── netease_musician │ └── netease_musician.plugin │ ├── picc │ └── picc_adblock.plugin │ └── xmyx │ └── xmyx.plugin ├── script ├── aliyun_web │ ├── aliyun_web.js │ ├── aliyun_web_ck.js │ ├── aliyun_web_process.js │ └── aliyun_web_scene.js ├── cbd │ └── cbd_ck.js ├── cfmoto │ └── cfmoto.js ├── iios │ └── iios.js ├── lhtj │ └── lhtj.js ├── netease_musician │ ├── cookie.js │ └── task.js ├── ningji │ └── ningji.js ├── picc │ └── picc_adblock.js ├── wdzhsy │ └── wdzhsy.js └── zeeho │ └── zeeho.js └── subscribe ├── leiyiyan.boxjs.json └── leiyiyan.icons.json /.github/scripts/generate_image_json.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | def generate_json(): 5 | image_folder = 'icons' 6 | json_data = { 7 | "name": "Leiyiyan图标订阅", 8 | "description": "脚本图标订阅", 9 | "icons": [] 10 | } 11 | 12 | for filename in os.listdir(image_folder): 13 | if filename.endswith(".png"): 14 | image_path = os.path.join(image_folder, filename) 15 | raw_url = f"https://raw.githubusercontent.com/{os.environ['GITHUB_REPOSITORY']}/main/{image_path}" 16 | icon_data = {"name": filename, "url": raw_url} 17 | json_data["icons"].append(icon_data) 18 | 19 | # Set the output path relative to the repository root 20 | output_path = os.path.join(os.getcwd(), 'subscribe/leiyiyan.icons.json') 21 | 22 | with open(output_path, 'w', encoding='utf-8') as json_file: 23 | json.dump(json_data, json_file, ensure_ascii=False, indent=2) 24 | 25 | # Save output data to the GITHUB_STATE environment file 26 | with open(os.environ['GITHUB_STATE'], 'a') as state_file: 27 | state_file.write(f"ICONS_JSON_PATH={output_path}\n") 28 | 29 | if __name__ == "__main__": 30 | generate_json() 31 | -------------------------------------------------------------------------------- /.github/workflows/blank.yml: -------------------------------------------------------------------------------- 1 | name: Image Workflow 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'icons/**' # 监视 icons 文件夹及其子文件夹下的内容 7 | pull_request: 8 | paths: 9 | - 'icons/**' # 监视 icons 文件夹及其子文件夹下的内容 10 | workflow_dispatch: 11 | 12 | jobs: 13 | generate_json: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v2 19 | 20 | - name: Set up Python 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: '3.x' 24 | 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install requests 29 | 30 | - name: Generate JSON 31 | run: | 32 | python .github/scripts/generate_image_json.py 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | 36 | - name: Upload artifact 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: json-artifact 40 | path: ${{ github.workspace }}/subscribe/leiyiyan.icons.json 41 | 42 | - name: Push to Repository 43 | run: | 44 | git config user.name "${{ github.actor }}" 45 | git config user.email "${{ github.actor }}@protonmail.com" 46 | git add . 47 | git commit -m "自动更新图标仓库" 48 | git push origin HEAD:main 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # resource -------------------------------------------------------------------------------- /icons/aliyun_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/aliyun_web.png -------------------------------------------------------------------------------- /icons/cbd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/cbd.png -------------------------------------------------------------------------------- /icons/cfmoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/cfmoto.png -------------------------------------------------------------------------------- /icons/iios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/iios.png -------------------------------------------------------------------------------- /icons/lhtj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/lhtj.png -------------------------------------------------------------------------------- /icons/netease_music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/netease_music.png -------------------------------------------------------------------------------- /icons/ningji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/ningji.png -------------------------------------------------------------------------------- /icons/picc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/picc.png -------------------------------------------------------------------------------- /icons/pyy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/pyy.png -------------------------------------------------------------------------------- /icons/wdzhsy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/wdzhsy.png -------------------------------------------------------------------------------- /icons/wx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/wx.png -------------------------------------------------------------------------------- /icons/xmyx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/xmyx.png -------------------------------------------------------------------------------- /icons/yrh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/yrh.png -------------------------------------------------------------------------------- /icons/zeeho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/zeeho.png -------------------------------------------------------------------------------- /icons/zmhy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leiyiyan/resource/32ae8fec97c48a66f784da024ccf02cbe42ae450/icons/zmhy.png -------------------------------------------------------------------------------- /loon/plugin/aliyun_web/aliyun_web.plugin: -------------------------------------------------------------------------------- 1 | #!name = 阿里云社区 2 | #!desc = 阿里云服务 每日签到、点赞、评论、收藏,积分可兑换实物。 3 | #!author = leiyiyan 4 | #!homepage = https://github.com/leiyiyan/resource 5 | #!icon = https://raw.githubusercontent.com/leiyiyan/resource/main/icons/aliyun_web.png 6 | #!date = 2024-03-16 13:00:00 7 | 8 | 9 | 10 | [Script] 11 | # 阿里云社区日常任务 12 | cron "0 7,13 * * *" script-path=https://github.com/leiyiyan/resource/blob/main/script/aliyun_web/aliyun_web.js, timeout=600, tag=阿里云社区日常任务 13 | 14 | # 获取Cookie 15 | http-response ^https?:\/\/developer\.aliyun\.com\/developer\/api\/my\/user\/getUser script-path=https://github.com/leiyiyan/resource/blob/main/script/aliyun_web/aliyun_web.js, requires-body=true, timeout=60, tag=阿里云Web Cookie 16 | 17 | 18 | [MitM] 19 | hostname = developer.aliyun.com 20 | -------------------------------------------------------------------------------- /loon/plugin/cfmoto/cfmoto.plugin: -------------------------------------------------------------------------------- 1 | #!name = 春风摩托 2 | #!desc = 每日签到、会员任务、去广告,积分可兑换实物。 3 | #!author = leiyiyan 4 | #!homepage = https://github.com/leiyiyan/resource 5 | #!icon = https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cfmoto.png 6 | #!date = 2024-3-19 13:48:20 7 | 8 | [Script] 9 | # 获取 Cookie 10 | http-response ^https:\/\/c\.cfmoto\.com\/jv\/user\/user_info script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/cfmoto/cfmoto.js, requires-body=true, timeout=60, tag=春风摩托 Cookie 11 | # 脚本任务 12 | cron "0 7 * * *" script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/cfmoto/cfmoto.js, tag=春风摩托 13 | 14 | [Rewrite] 15 | # 开屏广告 16 | ^https:\/\/c\.cfmoto\.com\/cfmotoservermall\/app\/ad$ reject-dict 17 | # 弹窗广告 18 | ^https:\/\/c\.cfmoto\.com\/cfmotoservermall\/app\/popwindow reject-dict 19 | 20 | [MITM] 21 | hostname = c.cfmoto.com -------------------------------------------------------------------------------- /loon/plugin/netease_musician/netease_musician.plugin: -------------------------------------------------------------------------------- 1 | #!name = 网易云音乐人 2 | #!desc = 当前版本仅支持云贝: 签到;音乐人: 每日任务、推荐任务(发表主创说、发布动态、回复粉丝私信);黑胶会员: 会员打卡、每日任务(♥️三首会员歌曲),完成以上任务后可自动领取任务奖励。 3 | #!author = leiyiyan 4 | #!homepage = https://github.com/leiyiyan/resource 5 | #!icon = https://raw.githubusercontent.com/leiyiyan/resource/main/icons/netease_music.png 6 | #!date = 2024-02-21 10:54:00 7 | #!select=开启音乐人任务, 启用, 禁用 8 | #!select=开启会员任务, 启用, 禁用 9 | #!select=开启云贝任务, 启用, 禁用 10 | #!input = Netease_Musician_FansId 11 | #!input = Netease_Musician_Cookie 12 | #!input = Netease_Musician_UserAgent 13 | 14 | 15 | [Script] 16 | # 音乐人任务(默认0点10分执行,如需更改请自行修改corn表达式) 17 | cron "10 0 * * *" script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/netease_musician/task.js, timeout=600, tag=音乐人任务 18 | 19 | # 获取Cookie 20 | http-request ^https?:\/\/music\.163\.com\/weapi\/cloudbean\/records\/incomes script-path = https://raw.githubusercontent.com/leiyiyan/resource/main/script/netease_musician/cookie.js, tag = 获取Cookie 21 | 22 | 23 | [MitM] 24 | hostname = music.163.com 25 | -------------------------------------------------------------------------------- /loon/plugin/picc/picc_adblock.plugin: -------------------------------------------------------------------------------- 1 | #!name = 中国人保去广告 2 | #!desc = 中国人保去除启动开屏、主页、我的、人保头条、猜你喜欢页面广告。 3 | #!author = Sliverkiss 4 | #!homepage = https://github.com/Sliverkiss/QuantumultX 5 | #!icon = https://raw.githubusercontent.com/leiyiyan/resource/main/icons/picc.png 6 | #!date = 2024-02-19 15:35:00 7 | 8 | [Rewrite] 9 | # 人保头条 10 | ^https:\/\/zgrb\.epicc\.com\.cn\/G-HAPP\/h\/headlines\/queryHeadlines reject-dict 11 | 12 | # 启动开屏 13 | ^https:\/\/zgrb\.epicc\.com\.cn\/G-HAPP\/a\/update\/startupPage\/v1 reject-dict 14 | 15 | # 猜你喜欢 16 | ^https:\/\/zgrb\.epicc\.com\.cn\/G-HAPP\/a\/config\/guessYouLike\/v3 reject-dict 17 | 18 | [Script] 19 | # 主页 20 | http-response ^https:\/\/zgrb\.epicc\.com\.cn\/G-HAPP\/a\/config\/homeInit\/v3 script-path = https://raw.githubusercontent.com/leiyiyan/resource/main/script/picc/picc_adblock.js, requires-body = true, tag = 主页去广告 21 | 22 | # 我的页面 23 | http-response ^https:\/\/zgrb\.epicc\.com\.cn\/G-HAPP\/mpageconfig\/myPageConfigList\/v3 script-path = https://raw.githubusercontent.com/leiyiyan/resource/main/script/picc/picc_adblock.js, requires-body = true, tag = 我的页面去广告 24 | 25 | 26 | [MitM] 27 | hostname = zgrb.epicc.com.cn 28 | -------------------------------------------------------------------------------- /loon/plugin/xmyx/xmyx.plugin: -------------------------------------------------------------------------------- 1 | #!name = 星妈优选 2 | #!desc = 星妈优选小程序 每日签到、任务。 3 | #!author = leiyiyan 4 | #!homepage = https://github.com/leiyiyan/resource 5 | #!icon = https://raw.githubusercontent.com/leiyiyan/resource/main/icons/xmyx.png 6 | #!date = 2024-04-10 16:44:00 7 | 8 | 9 | 10 | [Script] 11 | # 定时任务 12 | cron "1 12 * * *" script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/xmyx/xmyx.js, timeout=600, tag=星妈优选, img-url=https://raw.githubusercontent.com/leiyiyan/resource/main/icons/xmyx.png 13 | 14 | # 获取Token 15 | http-response ^https?:\/\/www\.feihevip\.com\/api\/starMember\/getMemberInfo script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/xmyx/xmyx.js, requires-body=true, timeout=10, tag=星妈优选Token, img-url=https://raw.githubusercontent.com/leiyiyan/resource/main/icons/xmyx.png 16 | 17 | [MitM] 18 | hostname = www.feihevip.com 19 | -------------------------------------------------------------------------------- /script/aliyun_web/aliyun_web_ck.js: -------------------------------------------------------------------------------- 1 | /* 2 | 作者:@leiyiyan 致谢@Sliverkiss、@yuheng 3 | 更新日期:2024.04.17 14:30:00 4 | 5 | 阿里云社区自动获取ck并同步到青龙后自动执行脚本,用于解决阿里云社区 Cookie 有效期过短,需要频繁抓取的问题 6 | 脚本兼容:Surge、QuantumultX、Loon、Shadowrocket 7 | 8 | boxjs订阅: https://raw.githubusercontent.com/leiyiyan/resource/main/subscribe/leiyiyan.boxjs.json 9 | 10 | 11 | 使用方法: 12 | 1.在青龙-系统设置-应用设置中创建应用,获取 Client ID 和 Client Secret,权限设置为环境变量和运行任务; 13 | 2.请务必确保青龙任务列表中阿里云社区的任务名称为 "阿里云社区"; 14 | 3.添加该脚本到重写,并配置好 hostname; 15 | 4.配置好boxjs订阅,并按相关提示配置好参数; 16 | 青龙服务地址:http://xx.xx.xx.xx:5700,注意结尾不要带 "/" 17 | clientId:第 1 部中创建的应用的 Client ID 18 | secret:第 1 部中创建的应用的 Client Secret 19 | envName:要同步的环境变量名称,默认为: "aliyunWeb_data" 20 | taskName:要执行的任务名称,默认为: "阿里云社区" 21 | 5.打开阿里云社区app->首页->积分商城,进入"积分商城"页面,自动获取 Cookie; 22 | 6.根据 boxjs 中的配置自动同步青龙变量,并执行任务; 23 | 7.运行后可自行选择关闭获取脚本,防止产生不必要的mitm。 24 | 25 | [Script] 26 | http-response ^https?:\/\/developer\.aliyun\.com\/developer\/api\/my\/user\/getUser script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/aliyun_web/aliyun_web_ck.js, requires-body=true, timeout=60, tag=阿里云同步青龙 27 | 28 | [MITM] 29 | hostname = developer.aliyun.com 30 | 31 | */ 32 | // env.js 全局 33 | const $ = new Env("阿里云社区同步青龙");//脚本名称 34 | // 调试 35 | $.is_debug = ($.isNode() ? process.env.IS_DEDUG : $.getdata('is_debug')) || 'false'; 36 | // 为通知准备的空数组 37 | $.notifyMsg = []; 38 | //青龙配置 39 | let QL = ($.isNode() ? process.env.aliyunWeb_QL : $.getjson("aliyunWeb_QL")) || {}; 40 | //---------------------- 自定义函数区 ----------------------------------- 41 | async function getCookie() { 42 | if (typeof $request === "undefined" || $request.method === 'OPTIONS') return; 43 | const url = $request.url 44 | const headers = ObjectKeys2LowerCase($request.headers); 45 | const cookie = headers.cookie; 46 | debug(cookie, "获取到的cookie如下"); 47 | 48 | if (!cookie) throw new Error(`⛔️ ${QL.envName}获取cookie失败!`); 49 | const body = $.toObj($response.body); 50 | if (!(body?.data)) throw new Error(`⛔️ 获取Body失败!`); 51 | let nickname = ''; 52 | let avatar = ''; 53 | if(url.indexOf('getUser') > -1) { 54 | nickname = body?.data?.nickname; 55 | avatar = body?.data?.avatar 56 | } else if (url.indexOf('queryUserBaseInfo')) { 57 | nickname = body?.data?.userNick; 58 | } 59 | const user = { 60 | "userId": nickname, 61 | "avatar": avatar, 62 | "token": cookie, 63 | "userName": nickname 64 | } 65 | return user; 66 | } 67 | //---------------------- 辅助工具函数 ----------------------------------- 68 | //更新青龙变量 69 | async function main(user) { 70 | try { 71 | QL = typeof QL === "string" ? JSON.parse(QL) : QL; 72 | if (!QL) throw new Error(`⛔️ 请先在boxjs配置青龙应用`); 73 | if (!(QL.envName)) throw new Error(`⛔️ 请先在boxjs配置青龙应用要同步的变量名称`); 74 | 75 | const ql = new QingLong(QL.host, QL.clientId, QL.secret); 76 | await ql.checkLogin(); 77 | 78 | // 获取环境变量并选择特定变量 79 | await ql.getEnvs(); 80 | const envs = ql.selectEnvByName(QL.envName); 81 | if(!envs.length) throw new Error(`⛔️ 请在青龙应用配置环境变量:${QL.envName}`); 82 | const selectedEnv = envs[0] 83 | 84 | debug(selectedEnv); 85 | 86 | if (selectedEnv) { 87 | const envValues = JSON.parse(selectedEnv.value); 88 | console.log(envValues) 89 | const index = envValues.findIndex(e => e.userId == user.userId) 90 | if (envValues[index].token == user.token) { 91 | $.title = `${QL.envName}当前ck未过期,无需同步`; 92 | DoubleLog(`⛔️ ${QL.envName}当前ck未过期,无需同步`); 93 | return; 94 | } 95 | envValues[index] = user; 96 | // 更新已存在的环境变量 97 | await ql.updateEnv({ value: JSON.stringify(envValues), name: QL.envName, id: selectedEnv.id }); 98 | } else { 99 | // 添加新的环境变量 100 | await ql.addEnv([{ value: JSON.stringify(user), name: QL.envName }]) 101 | } 102 | console.log(`🎉${QL.envName}数据同步青龙成功!`); 103 | // 运行任务 104 | 105 | const task = await ql.getTask(); 106 | if(task) { 107 | if(task.status == 1) { 108 | if(QL.autoRunTask == 'true' || QL.autoRunTask == true) { 109 | await ql.runTask([task.id]) 110 | $.title = `🎉${QL.taskName}开始执行任务!`; 111 | DoubleLog(`${QL.taskName}\n开始执行任务!`); 112 | }else{ 113 | $.title = `🎉${QL.envName}数据同步青龙成功!`; 114 | DoubleLog(`${QL.envName}\n数据同步青龙成功!`); 115 | } 116 | }else{ 117 | $.title = `🎉${QL.taskName}任务已被执行!`; 118 | DoubleLog(`${QL.taskName}\n任务已被执行!`); 119 | } 120 | } 121 | } catch (e) { 122 | console.log("操作青龙出错:" + e); 123 | throw new Error("操作青龙出错:" + e); 124 | } 125 | } 126 | 127 | 128 | //调试 129 | function debug(t, l = 'debug') { 130 | if ($.is_debug === 'true') { 131 | $.log(`\n-----------${l}------------\n`); 132 | $.log(typeof t == "string" ? t : $.toStr(t) || `debug error => t=${t}`); 133 | $.log(`\n-----------${l}------------\n`) 134 | } 135 | }; 136 | // 双平台log输出 137 | function DoubleLog(data) { 138 | console.log(`${data}`); 139 | $.notifyMsg.push(`${data}`); 140 | } 141 | //账号通知 142 | async function SendMsg(n) { 143 | $.msg($.name, $.title || "", n, { 144 | "media-url": $.avatar || "" 145 | }) 146 | }; 147 | //将请求头转换为小写 148 | function ObjectKeys2LowerCase(e) { 149 | e = Object.fromEntries(Object.entries(e).map(([e, t]) => [e.toLowerCase(), t])); 150 | return new Proxy(e, { 151 | get: function (e, t, r) { 152 | return Reflect.get(e, t.toLowerCase(), r) 153 | }, 154 | set: function (e, t, r, n) { 155 | return Reflect.set(e, t.toLowerCase(), r, n) 156 | } 157 | }) 158 | }; 159 | //---------------------- 程序执行入口 ----------------------------------- 160 | //主程序执行入口 161 | !(async () => { 162 | let user = await getCookie(); 163 | if (!user) throw new Error(`⛔️获取Cookie失败,请检查配置是否正常`); 164 | await main(user); 165 | })() 166 | .catch((e) => $.notifyMsg.push(e.message || e))//捕获登录函数等抛出的异常, 并把原因添加到全局变量(通知) 167 | .finally(async () => { 168 | if ($.notifyMsg.length) await SendMsg($.notifyMsg.join('\n'))//带上总结推送通知 169 | $.done({ ok: 1 }); //调用Surge、QX内部特有的函数, 用于退出脚本执行 170 | }); 171 | function QingLong(HOST, Client_ID, Client_Secret) { 172 | const Request = (t, m = "GET") => { 173 | return new Promise((resolve, reject) => { 174 | $.http[m.toLowerCase()](t) 175 | .then((response) => { 176 | var resp = response.body; 177 | try { 178 | resp = $.toObj(resp) || resp; 179 | } catch (e) { } 180 | resolve(resp); 181 | }) 182 | .catch((err) => reject(err)); 183 | }); 184 | }; 185 | return new (class { 186 | /** 187 | * 对接青龙API 188 | * @param {*} HOST http://127.0.0.1:5700 189 | * @param {*} Client_ID xxx 190 | * @param {*} Client_Secret xxx 191 | */ 192 | constructor(HOST, Client_ID, Client_Secret) { 193 | this.host = HOST; 194 | this.clientId = Client_ID; 195 | this.clientSecret = Client_Secret; 196 | this.token = ""; 197 | this.envs = []; 198 | } 199 | //用户登录 200 | async checkLogin() { 201 | let tokenObj; 202 | try { 203 | tokenObj = $.getjson("yuheng_ql_token") || {}; 204 | } catch (e) { 205 | console.log(`⛔️ Token无效,开始重新获取`); 206 | await this.getAuthToken(); 207 | return false; 208 | } 209 | if (Object.keys(tokenObj).length > 0) { 210 | const { token, expiration } = tokenObj; 211 | const currentTime = new Date().getTime(); 212 | if (currentTime > expiration) { 213 | $.log("⛔️ Token已过期"); 214 | await this.getAuthToken(); 215 | } else { 216 | this.token = token; 217 | $.log( 218 | `✅ 已成功获取Token (${this.token 219 | }),有效期至 ${$.time( 220 | "yyyy-MM-dd HH:mm:ss", 221 | expiration 222 | )}` 223 | ); 224 | } 225 | } else { 226 | await this.getAuthToken(); 227 | } 228 | } 229 | // 获取用户密钥 230 | async getAuthToken() { 231 | const options = { 232 | url: `${this.host}/open/auth/token`, 233 | params: { 234 | client_id: this.clientId, 235 | client_secret: this.clientSecret, 236 | }, 237 | }; 238 | try { 239 | $.log(`传入参数: ${JSON.stringify(options)}`); 240 | const { code, data, message } = await Request(options); 241 | if (code === 200) { 242 | const { token, token_type, expiration } = data; 243 | $.log( 244 | `✅ 已成功获取Token: ${token}, 有效期至 ${$.time( 245 | "yyyy-MM-dd HH:mm:ss", 246 | expiration * 1e3 247 | )}` 248 | ); 249 | this.token = `${token_type} ${token}`; 250 | $.setjson({ 251 | token: this.token, 252 | expiration: expiration * 1e3, 253 | }, 254 | "yuheng_ql_token" 255 | ); 256 | } else { 257 | throw message || "无法获取Token."; 258 | } 259 | } catch (e) { 260 | throw e 261 | ? typeof e === "object" 262 | ? JSON.stringify(e) 263 | : e 264 | : "Network Error."; 265 | } 266 | } 267 | /** 268 | * 获取所有环境变量详情 269 | */ 270 | async getEnvs() { 271 | const options = { 272 | url: `${this.host}/open/envs`, 273 | headers: { 274 | 'Authorization': this.token, 275 | }, 276 | }; 277 | try { 278 | const { code, data, message } = await Request(options); 279 | if (code === 200) { 280 | this.envs = data; 281 | $.log(`✅ 获取环境变量成功.`); 282 | } else { 283 | throw message || `无法获取环境变量.`; 284 | } 285 | } catch (e) { 286 | throw e 287 | ? typeof e === "object" 288 | ? JSON.stringify(e) 289 | : e 290 | : "Network Error."; 291 | } 292 | } 293 | /** 294 | * 获取所有环境变量详情 295 | */ 296 | async getTask() { 297 | const options = { 298 | url: `${this.host}/open/crons`, 299 | headers: { 300 | 'Authorization': this.token, 301 | }, 302 | }; 303 | try { 304 | const { code, data, message } = await Request(options); 305 | if (code === 200) { 306 | const tasks = data?.data; 307 | const task = tasks.find((item) => item.name == QL.taskName); 308 | $.log(`✅ 获取taskId成功.`); 309 | return { 310 | id: task.id, 311 | status: task.status 312 | }; 313 | } else { 314 | throw message || `无法获取taskId.`; 315 | } 316 | } catch (e) { 317 | throw e 318 | ? typeof e === "object" 319 | ? JSON.stringify(e) 320 | : e 321 | : "Network Error."; 322 | } 323 | } 324 | checkEnvByName(name) { 325 | return this.envs.findIndex((item) => item.name === name); 326 | } 327 | checkEnvByRemarks(remarks) { 328 | return this.envs.findIndex((item) => item.remarks === remarks); 329 | } 330 | checkEnvByValue(value, regex) { 331 | const match = value.match(regex); 332 | if (match) { 333 | const index = this.envs.findIndex((item) => 334 | item.value.includes(match[0]) 335 | ); 336 | if (index > -1) { 337 | $.log(`🆗${value} Matched: ${match[0]}`); 338 | return index; 339 | } else { 340 | $.log(`⭕${value} No Matched`); 341 | return -1; 342 | } 343 | } else { 344 | $.log(`⭕${value} No Matched`); 345 | return -1; 346 | } 347 | } 348 | selectEnvByName(name) { 349 | return this.envs.filter((item) => item.name === name); 350 | } 351 | selectEnvByRemarks(remarks) { 352 | return this.envs.filter((item) => item.remarks === remarks); 353 | } 354 | /** 355 | * 添加环境变量 356 | * @param {*} array [{value:'变量值',name:'变量名',remarks:'备注'}] 357 | */ 358 | async addEnv(array) { 359 | const options = { 360 | url: `${this.host}/open/envs`, 361 | headers: { 362 | Authorization: this.token, 363 | "Content-Type": "application/json;charset=UTF-8", 364 | }, 365 | body: JSON.stringify(array), 366 | }; 367 | try { 368 | const { code, message } = await Request(options, "post"); 369 | if (code === 200) { 370 | $.log(`✅ 已成功添加环境变量.`); 371 | } else { 372 | throw message || "未能添加环境变量."; 373 | } 374 | } catch (e) { 375 | throw e 376 | ? typeof e === "object" 377 | ? JSON.stringify(e) 378 | : e 379 | : "Network Error."; 380 | } 381 | } 382 | /** 383 | * 修改环境变量 384 | * @param {*} obj {value:'变量值',name:'变量名',remarks:'备注',id:0} 385 | */ 386 | async updateEnv(obj) { 387 | const options = { 388 | url: `${this.host}/open/envs`, 389 | method: "put", 390 | headers: { 391 | Authorization: this.token, 392 | "Content-Type": "application/json;charset=UTF-8", 393 | }, 394 | body: JSON.stringify(obj), 395 | }; 396 | try { 397 | const { code, message } = await Request(options, "post"); 398 | if (code === 200) { 399 | $.log(`✅ 已成功更新环境变量.`); 400 | } else { 401 | throw message || "无法更新环境变量."; 402 | } 403 | } catch (e) { 404 | throw e 405 | ? typeof e === "object" 406 | ? JSON.stringify(e) 407 | : e 408 | : "Network Error."; 409 | } 410 | } 411 | 412 | 413 | /** 414 | * 运行任务 415 | * @param {*} array [taskId] 416 | */ 417 | async runTask(taskIds) { 418 | const options = { 419 | url: `${this.host}/open/crons/run`, 420 | method: "put", 421 | headers: { 422 | Authorization: this.token, 423 | "Content-Type": "application/json;charset=UTF-8", 424 | }, 425 | body: JSON.stringify(taskIds), 426 | }; 427 | try { 428 | const { code, message } = await Request(options, "post"); 429 | if (code === 200) { 430 | $.log(`✅ 任务已成功运行.`); 431 | } else { 432 | throw message || "无法运行任务."; 433 | } 434 | } catch (e) { 435 | throw e 436 | ? typeof e === "object" 437 | ? JSON.stringify(e) 438 | : e 439 | : "Network Error."; 440 | } 441 | } 442 | })(HOST, Client_ID, Client_Secret); 443 | } 444 | 445 | //---------------------- 固定不动区域 ----------------------------------- 446 | //From chavyleung's Env.js 447 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, a) => { s.call(this, t, (t, s, r) => { t ? a(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; const a = this.getdata(t); if (a) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, a) => e(a)) }) } runScript(t, e) { return new Promise(s => { let a = this.getdata("@chavy_boxjs_userCfgs.httpapi"); a = a ? a.replace(/\n/g, "").trim() : a; let r = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); r = r ? 1 * r : 20, r = e && e.timeout ? e.timeout : r; const [i, o] = a.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": i, Accept: "*/*" }, timeout: r }; this.post(n, (t, e, a) => s(a)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), a = !s && this.fs.existsSync(e); if (!s && !a) return {}; { const a = s ? t : e; try { return JSON.parse(this.fs.readFileSync(a)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), a = !s && this.fs.existsSync(e), r = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, r) : a ? this.fs.writeFileSync(e, r) : this.fs.writeFileSync(t, r) } } lodash_get(t, e, s) { const a = e.replace(/\[(\d+)\]/g, ".$1").split("."); let r = t; for (const t of a) if (r = Object(r)[t], void 0 === r) return s; return r } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, a) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[a + 1]) >> 0 == +e[a + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, a] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, a, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, a, r] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(a), o = a ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), a) } catch (e) { const i = {}; this.lodash_set(i, r, t), s = this.setval(JSON.stringify(i), a) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, a) => { !t && s && (s.body = a, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, a) }); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: a, headers: r, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: a, headers: r, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: a, statusCode: r, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: a, statusCode: r, headers: i, rawBody: o, body: n }, n) }, t => { const { message: a, response: r } = t; e(a, r, r && s.decode(r.rawBody, this.encoding)) }) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, (t, s, a) => { !t && s && (s.body = a, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, a) }); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: a, headers: r, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: a, headers: r, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let a = require("iconv-lite"); this.initGotEnv(t); const { url: r, ...i } = t; this.got[s](r, i).then(t => { const { statusCode: s, statusCode: r, headers: i, rawBody: o } = t, n = a.decode(o, this.encoding); e(null, { status: s, statusCode: r, headers: i, rawBody: o, body: n }, n) }, t => { const { message: s, response: r } = t; e(s, r, r && a.decode(r.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let a = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in a) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? a[e] : ("00" + a[e]).substr(("" + a[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let a = t[s]; null != a && "" !== a && ("object" == typeof a && (a = JSON.stringify(a)), e += `${s}=${a}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", a = "", r) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } case "Loon": { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } case "Quantumult X": { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl, a = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": a } } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, a, i(r)); break; case "Quantumult X": $notify(e, s, a, i(r)); break; case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), a && t.push(a), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t.stack) } } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${s} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) } 448 | 449 | 450 | -------------------------------------------------------------------------------- /script/cbd/cbd_ck.js: -------------------------------------------------------------------------------- 1 | /* 2 | 作者:@leiyiyan 致谢@Sliverkiss、@yuheng 3 | 更新日期:2024.08.06 10:39:00 4 | 5 | 茶百道自动获取ck并同步到青龙后自动执行脚本,用于解决茶百道 Cookie 有效期过短,需要频繁抓取的问题 6 | 脚本兼容:Surge、QuantumultX、Loon、Shadowrocket 7 | 8 | boxjs订阅: https://raw.githubusercontent.com/leiyiyan/resource/main/subscribe/leiyiyan.boxjs.json 9 | 10 | 11 | 使用方法: 12 | 1.在青龙-系统设置-应用设置中创建应用,获取 Client ID 和 Client Secret,权限设置为环境变量和运行任务; 13 | 2.添加该脚本到重写,并配置好 hostname; 14 | 3.配置好boxjs订阅,并按相关提示配置好参数; 15 | 青龙服务地址:http://xx.xx.xx.xx:5700,注意结尾不要带 "/" 16 | clientId:第 1 部中创建的应用的 Client ID 17 | secret:第 1 部中创建的应用的 Client Secret 18 | envName:要同步的环境变量名称 19 | 4.打开茶百道小程序->点单页面,自动获取 Cookie; 20 | 5.运行后可自行选择关闭获取脚本,防止产生不必要的mitm。 21 | 22 | [Script] 23 | http-request ^https?:\/\/apisix\-gateway\-pro\.shuxinyc\.com\/decrypt script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/cbd/cbd_ck.js, requires-body=false, timeout=60, tag=茶百道同步青龙 24 | 25 | [MITM] 26 | hostname = apisix-gateway-pro.shuxinyc.com 27 | 28 | */ 29 | // env.js 全局 30 | const $ = new Env("茶百道同步青龙");//脚本名称 31 | // 调试 32 | $.is_debug = ($.isNode() ? process.env.IS_DEDUG : $.getdata('is_debug')) || 'false'; 33 | // 为通知准备的空数组 34 | $.notifyMsg = []; 35 | //青龙配置 36 | let QL = ($.isNode() ? process.env.CBD_QL : $.getjson("CBD_QL")) || {}; 37 | //---------------------- 自定义函数区 ----------------------------------- 38 | async function getCookie() { 39 | if (typeof $request === "undefined" || $request.method === 'OPTIONS') return; 40 | 41 | const headers = ObjectKeys2LowerCase($request.headers); 42 | const { csession, versionname, versioncode, referer } = headers 43 | debug(csession, "获取到的csession如下"); 44 | 45 | if (!csession) throw new Error(`⛔️ ${QL.envName}获取cookie失败!`); 46 | const user = { 47 | username: QL.username, 48 | csession, 49 | versionname, 50 | versioncode, 51 | referer 52 | } 53 | return user; 54 | } 55 | //---------------------- 辅助工具函数 ----------------------------------- 56 | //更新青龙变量 57 | async function main(user) { 58 | try { 59 | QL = typeof QL === "string" ? JSON.parse(QL) : QL; 60 | if (!QL) throw new Error(`⛔️ 请先在boxjs配置青龙应用`); 61 | if (!(QL.envName)) throw new Error(`⛔️ 请先在boxjs配置青龙应用要同步的变量名称`); 62 | 63 | const ql = new QingLong(QL.host, QL.clientId, QL.secret); 64 | await ql.checkLogin(); 65 | 66 | // 获取环境变量并选择特定变量 67 | await ql.getEnvs(); 68 | const envs = ql.selectEnvByName(QL.envName); 69 | if(!envs.length) throw new Error(`⛔️ 请在青龙应用配置环境变量:${QL.envName}`); 70 | const selectedEnv = envs[0] 71 | console.log(QL.envName); 72 | console.log(envs); 73 | 74 | debug(selectedEnv); 75 | 76 | if (selectedEnv) { 77 | const envValues = JSON.parse(selectedEnv.value); 78 | console.log(envValues) 79 | let index = envValues.findIndex(e => e.username == user.username) 80 | index = index === -1 ? 0 : index 81 | console.log('index: ' + index) 82 | envValues[index] = user; 83 | // 更新已存在的环境变量 84 | await ql.updateEnv({ value: JSON.stringify(envValues), name: QL.envName, id: selectedEnv.id }); 85 | } else { 86 | // 添加新的环境变量 87 | await ql.addEnv([{ value: JSON.stringify(user), name: QL.envName }]) 88 | } 89 | console.log(`🎉${QL.envName}数据同步青龙成功!`); 90 | $.title = `🎉${QL.envName}数据同步青龙成功!`; 91 | DoubleLog(`${QL.envName}\n数据同步青龙成功!`); 92 | } catch (e) { 93 | console.log("操作青龙出错:" + e); 94 | throw new Error("操作青龙出错:" + e); 95 | } 96 | } 97 | 98 | 99 | //调试 100 | function debug(t, l = 'debug') { 101 | if ($.is_debug === 'true') { 102 | $.log(`\n-----------${l}------------\n`); 103 | $.log(typeof t == "string" ? t : $.toStr(t) || `debug error => t=${t}`); 104 | $.log(`\n-----------${l}------------\n`) 105 | } 106 | }; 107 | // 双平台log输出 108 | function DoubleLog(data) { 109 | console.log(`${data}`); 110 | $.notifyMsg.push(`${data}`); 111 | } 112 | //账号通知 113 | async function SendMsg(n) { 114 | $.msg($.name, $.title || "", n, { 115 | "media-url": $.avatar || "" 116 | }) 117 | }; 118 | //将请求头转换为小写 119 | function ObjectKeys2LowerCase(e) { 120 | e = Object.fromEntries(Object.entries(e).map(([e, t]) => [e.toLowerCase(), t])); 121 | return new Proxy(e, { 122 | get: function (e, t, r) { 123 | return Reflect.get(e, t.toLowerCase(), r) 124 | }, 125 | set: function (e, t, r, n) { 126 | return Reflect.set(e, t.toLowerCase(), r, n) 127 | } 128 | }) 129 | }; 130 | //---------------------- 程序执行入口 ----------------------------------- 131 | //主程序执行入口 132 | !(async () => { 133 | let user = await getCookie(); 134 | if (!user) throw new Error(`⛔️获取Cookie失败,请检查配置是否正常`); 135 | await main(user); 136 | })() 137 | .catch((e) => $.notifyMsg.push(e.message || e))//捕获登录函数等抛出的异常, 并把原因添加到全局变量(通知) 138 | .finally(async () => { 139 | if ($.notifyMsg.length) await SendMsg($.notifyMsg.join('\n'))//带上总结推送通知 140 | $.done({ ok: 1 }); //调用Surge、QX内部特有的函数, 用于退出脚本执行 141 | }); 142 | function QingLong(HOST, Client_ID, Client_Secret) { 143 | const Request = (t, m = "GET") => { 144 | return new Promise((resolve, reject) => { 145 | $.http[m.toLowerCase()](t) 146 | .then((response) => { 147 | var resp = response.body; 148 | try { 149 | resp = $.toObj(resp) || resp; 150 | } catch (e) { } 151 | resolve(resp); 152 | }) 153 | .catch((err) => reject(err)); 154 | }); 155 | }; 156 | return new (class { 157 | /** 158 | * 对接青龙API 159 | * @param {*} HOST http://127.0.0.1:5700 160 | * @param {*} Client_ID xxx 161 | * @param {*} Client_Secret xxx 162 | */ 163 | constructor(HOST, Client_ID, Client_Secret) { 164 | this.host = HOST; 165 | this.clientId = Client_ID; 166 | this.clientSecret = Client_Secret; 167 | this.token = ""; 168 | this.envs = []; 169 | } 170 | //用户登录 171 | async checkLogin() { 172 | let tokenObj; 173 | try { 174 | tokenObj = $.getjson("yuheng_ql_token") || {}; 175 | } catch (e) { 176 | console.log(`⛔️ Token无效,开始重新获取`); 177 | await this.getAuthToken(); 178 | return false; 179 | } 180 | if (Object.keys(tokenObj).length > 0) { 181 | const { token, expiration } = tokenObj; 182 | const currentTime = new Date().getTime(); 183 | if (currentTime > expiration) { 184 | $.log("⛔️ Token已过期"); 185 | await this.getAuthToken(); 186 | } else { 187 | this.token = token; 188 | $.log( 189 | `✅ 已成功获取Token (${this.token 190 | }),有效期至 ${$.time( 191 | "yyyy-MM-dd HH:mm:ss", 192 | expiration 193 | )}` 194 | ); 195 | } 196 | } else { 197 | await this.getAuthToken(); 198 | } 199 | } 200 | // 获取用户密钥 201 | async getAuthToken() { 202 | const options = { 203 | url: `${this.host}/open/auth/token`, 204 | params: { 205 | client_id: this.clientId, 206 | client_secret: this.clientSecret, 207 | }, 208 | }; 209 | try { 210 | $.log(`传入参数: ${JSON.stringify(options)}`); 211 | const { code, data, message } = await Request(options); 212 | if (code === 200) { 213 | const { token, token_type, expiration } = data; 214 | $.log( 215 | `✅ 已成功获取Token: ${token}, 有效期至 ${$.time( 216 | "yyyy-MM-dd HH:mm:ss", 217 | expiration * 1e3 218 | )}` 219 | ); 220 | this.token = `${token_type} ${token}`; 221 | $.setjson({ 222 | token: this.token, 223 | expiration: expiration * 1e3, 224 | }, 225 | "yuheng_ql_token" 226 | ); 227 | } else { 228 | throw message || "无法获取Token."; 229 | } 230 | } catch (e) { 231 | throw e 232 | ? typeof e === "object" 233 | ? JSON.stringify(e) 234 | : e 235 | : "Network Error."; 236 | } 237 | } 238 | /** 239 | * 获取所有环境变量详情 240 | */ 241 | async getEnvs() { 242 | const options = { 243 | url: `${this.host}/open/envs`, 244 | headers: { 245 | 'Authorization': this.token, 246 | }, 247 | }; 248 | try { 249 | const { code, data, message } = await Request(options); 250 | if (code === 200) { 251 | this.envs = data; 252 | $.log(`✅ 获取环境变量成功.`); 253 | } else { 254 | throw message || `无法获取环境变量.`; 255 | } 256 | } catch (e) { 257 | throw e 258 | ? typeof e === "object" 259 | ? JSON.stringify(e) 260 | : e 261 | : "Network Error."; 262 | } 263 | } 264 | /** 265 | * 获取所有环境变量详情 266 | */ 267 | async getTask() { 268 | const options = { 269 | url: `${this.host}/open/crons`, 270 | headers: { 271 | 'Authorization': this.token, 272 | }, 273 | }; 274 | try { 275 | const { code, data, message } = await Request(options); 276 | if (code === 200) { 277 | const tasks = data?.data; 278 | const task = tasks.find((item) => item.name == QL.taskName); 279 | $.log(`✅ 获取taskId成功.`); 280 | return { 281 | id: task.id, 282 | status: task.status 283 | }; 284 | } else { 285 | throw message || `无法获取taskId.`; 286 | } 287 | } catch (e) { 288 | throw e 289 | ? typeof e === "object" 290 | ? JSON.stringify(e) 291 | : e 292 | : "Network Error."; 293 | } 294 | } 295 | checkEnvByName(name) { 296 | return this.envs.findIndex((item) => item.name === name); 297 | } 298 | checkEnvByRemarks(remarks) { 299 | return this.envs.findIndex((item) => item.remarks === remarks); 300 | } 301 | checkEnvByValue(value, regex) { 302 | const match = value.match(regex); 303 | if (match) { 304 | const index = this.envs.findIndex((item) => 305 | item.value.includes(match[0]) 306 | ); 307 | if (index > -1) { 308 | $.log(`🆗${value} Matched: ${match[0]}`); 309 | return index; 310 | } else { 311 | $.log(`⭕${value} No Matched`); 312 | return -1; 313 | } 314 | } else { 315 | $.log(`⭕${value} No Matched`); 316 | return -1; 317 | } 318 | } 319 | selectEnvByName(name) { 320 | return this.envs.filter((item) => item.name === name); 321 | } 322 | selectEnvByRemarks(remarks) { 323 | return this.envs.filter((item) => item.remarks === remarks); 324 | } 325 | /** 326 | * 添加环境变量 327 | * @param {*} array [{value:'变量值',name:'变量名',remarks:'备注'}] 328 | */ 329 | async addEnv(array) { 330 | const options = { 331 | url: `${this.host}/open/envs`, 332 | headers: { 333 | Authorization: this.token, 334 | "Content-Type": "application/json;charset=UTF-8", 335 | }, 336 | body: JSON.stringify(array), 337 | }; 338 | try { 339 | const { code, message } = await Request(options, "post"); 340 | if (code === 200) { 341 | $.log(`✅ 已成功添加环境变量.`); 342 | } else { 343 | throw message || "未能添加环境变量."; 344 | } 345 | } catch (e) { 346 | throw e 347 | ? typeof e === "object" 348 | ? JSON.stringify(e) 349 | : e 350 | : "Network Error."; 351 | } 352 | } 353 | /** 354 | * 修改环境变量 355 | * @param {*} obj {value:'变量值',name:'变量名',remarks:'备注',id:0} 356 | */ 357 | async updateEnv(obj) { 358 | const options = { 359 | url: `${this.host}/open/envs`, 360 | method: "put", 361 | headers: { 362 | Authorization: this.token, 363 | "Content-Type": "application/json;charset=UTF-8", 364 | }, 365 | body: JSON.stringify(obj), 366 | }; 367 | try { 368 | const { code, message } = await Request(options, "post"); 369 | if (code === 200) { 370 | $.log(`✅ 已成功更新环境变量.`); 371 | } else { 372 | throw message || "无法更新环境变量."; 373 | } 374 | } catch (e) { 375 | throw e 376 | ? typeof e === "object" 377 | ? JSON.stringify(e) 378 | : e 379 | : "Network Error."; 380 | } 381 | } 382 | 383 | 384 | /** 385 | * 运行任务 386 | * @param {*} array [taskId] 387 | */ 388 | async runTask(taskIds) { 389 | const options = { 390 | url: `${this.host}/open/crons/run`, 391 | method: "put", 392 | headers: { 393 | Authorization: this.token, 394 | "Content-Type": "application/json;charset=UTF-8", 395 | }, 396 | body: JSON.stringify(taskIds), 397 | }; 398 | try { 399 | const { code, message } = await Request(options, "post"); 400 | if (code === 200) { 401 | $.log(`✅ 任务已成功运行.`); 402 | } else { 403 | throw message || "无法运行任务."; 404 | } 405 | } catch (e) { 406 | throw e 407 | ? typeof e === "object" 408 | ? JSON.stringify(e) 409 | : e 410 | : "Network Error."; 411 | } 412 | } 413 | })(HOST, Client_ID, Client_Secret); 414 | } 415 | 416 | //---------------------- 固定不动区域 ----------------------------------- 417 | //From chavyleung's Env.js 418 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, a) => { s.call(this, t, (t, s, r) => { t ? a(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; const a = this.getdata(t); if (a) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, a) => e(a)) }) } runScript(t, e) { return new Promise(s => { let a = this.getdata("@chavy_boxjs_userCfgs.httpapi"); a = a ? a.replace(/\n/g, "").trim() : a; let r = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); r = r ? 1 * r : 20, r = e && e.timeout ? e.timeout : r; const [i, o] = a.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": i, Accept: "*/*" }, timeout: r }; this.post(n, (t, e, a) => s(a)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), a = !s && this.fs.existsSync(e); if (!s && !a) return {}; { const a = s ? t : e; try { return JSON.parse(this.fs.readFileSync(a)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), a = !s && this.fs.existsSync(e), r = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, r) : a ? this.fs.writeFileSync(e, r) : this.fs.writeFileSync(t, r) } } lodash_get(t, e, s) { const a = e.replace(/\[(\d+)\]/g, ".$1").split("."); let r = t; for (const t of a) if (r = Object(r)[t], void 0 === r) return s; return r } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, a) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[a + 1]) >> 0 == +e[a + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, a] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, a, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, a, r] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(a), o = a ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), a) } catch (e) { const i = {}; this.lodash_set(i, r, t), s = this.setval(JSON.stringify(i), a) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, a) => { !t && s && (s.body = a, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, a) }); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: a, headers: r, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: a, headers: r, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: a, statusCode: r, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: a, statusCode: r, headers: i, rawBody: o, body: n }, n) }, t => { const { message: a, response: r } = t; e(a, r, r && s.decode(r.rawBody, this.encoding)) }) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, (t, s, a) => { !t && s && (s.body = a, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, a) }); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: a, headers: r, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: a, headers: r, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let a = require("iconv-lite"); this.initGotEnv(t); const { url: r, ...i } = t; this.got[s](r, i).then(t => { const { statusCode: s, statusCode: r, headers: i, rawBody: o } = t, n = a.decode(o, this.encoding); e(null, { status: s, statusCode: r, headers: i, rawBody: o, body: n }, n) }, t => { const { message: s, response: r } = t; e(s, r, r && a.decode(r.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let a = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in a) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? a[e] : ("00" + a[e]).substr(("" + a[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let a = t[s]; null != a && "" !== a && ("object" == typeof a && (a = JSON.stringify(a)), e += `${s}=${a}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", a = "", r) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } case "Loon": { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } case "Quantumult X": { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl, a = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": a } } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, a, i(r)); break; case "Quantumult X": $notify(e, s, a, i(r)); break; case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), a && t.push(a), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t.stack) } } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${s} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) } 419 | 420 | 421 | -------------------------------------------------------------------------------- /script/cfmoto/cfmoto.js: -------------------------------------------------------------------------------- 1 | /* 2 | new Env('春风摩托'); 3 | @Author: Leiyiyan 4 | @Date: 2024-06-12 09:55 5 | 6 | @Description: 7 | 春风摩托 每日签到、会员任务,积分可兑换实物 8 | 9 | 获取 Cookie 方式:cfmoto app - 我的 10 | 11 | [Script] 12 | # 获取 Cookie 13 | http-response ^https:\/\/c\.cfmoto\.com\/jv\/user\/user_info script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/cfmoto/cfmoto.js, requires-body=true, timeout=60, tag=春风摩托 Cookie 14 | 15 | # 脚本任务 16 | cron "0 7 * * *" script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/cfmoto/cfmoto.js, tag=春风摩托 17 | 18 | [Rewrite] 19 | # 开屏广告 20 | ^https:\/\/c\.cfmoto\.com\/cfmotoservermall\/app\/ad$ reject-dict 21 | 22 | # 弹窗广告 23 | ^https:\/\/c\.cfmoto\.com\/cfmotoservermall\/app\/popwindow reject-dict 24 | 25 | [MITM] 26 | hostname = c.cfmoto.com 27 | 28 | ==================================== 29 | ⚠️【免责声明】 30 | ------------------------------------------ 31 | 1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 32 | 2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 33 | 3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。 34 | 4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 35 | 5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 36 | 6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。 37 | 7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。 38 | */ 39 | 40 | // env.js 全局 41 | const $ = new Env("春风摩托"); 42 | const ckName = "cfmoto_data"; 43 | //-------------------- 一般不动变量区域 ------------------------------------- 44 | const Notify = 1;//0为关闭通知,1为打开通知,默认为1 45 | const notify = $.isNode() ? require('./sendNotify') : ''; 46 | let envSplitor = ["@"]; //多账号分隔符 47 | var userCookie = ($.isNode() ? process.env[ckName] : $.getdata(ckName)) || ''; 48 | let userList = []; 49 | let userIdx = 0; 50 | let userCount = 0; 51 | 52 | // 调试 53 | $.is_debug = ($.isNode() ? process.env.IS_DEDUG : $.getdata('is_debug')) || 'false'; 54 | // 为多用户准备的通知数组 55 | $.notifyList = []; 56 | // 为通知准备的空数组 57 | $.notifyMsg = []; 58 | 59 | //---------------------- 自定义变量区域 ----------------------------------- 60 | //脚本入口函数main() 61 | async function main() { 62 | try { 63 | $.log('\n================== 任务 ==================\n'); 64 | for (let user of userList) { 65 | console.log(`🔷账号${user.index} >> Start work`) 66 | console.log(`随机延迟${user.getRandomTime()}ms`); 67 | // 签到 68 | const integral = await user.signin(); 69 | if (user.ckStatus) { 70 | await $.wait(user.getRandomTime()); 71 | // 查看签到记录 72 | const {count, prize} = await user.getSignRecord() 73 | await $.wait(user.getRandomTime()); 74 | if(prize) { 75 | // 盲盒抽奖 76 | await user.lottery() 77 | await $.wait(user.getRandomTime()); 78 | } 79 | // 限时抽奖 80 | await user.getLotteryList() 81 | await $.wait(user.getRandomTime()); 82 | 83 | for(let i = 0; i < 3; i++) { 84 | // 创建帖子 85 | const postId = await user.createArticle() 86 | await $.wait(user.getRandomTime()); 87 | // 评论帖子 88 | await user.postComments(postId) 89 | await $.wait(user.getRandomTime()); 90 | // 点赞 91 | await user.thumbsUp(postId) 92 | await $.wait(user.getRandomTime()); 93 | // 分享帖子 94 | await user.share(postId) 95 | await $.wait(user.getRandomTime()); 96 | // 删除帖子 97 | await user.deletePost(postId) 98 | await $.wait(user.getRandomTime()); 99 | } 100 | //查询待领取积分 101 | const integralTotal = await user.getSignInfo(); 102 | $.title = `本次运行共获得${(integral + 36)}积分`; 103 | DoubleLog(`「${user.userName}」当前积分:${integralTotal}分,累计签到:${count}天`); 104 | } else { 105 | //将ck过期消息存入消息数组 106 | $.notifyMsg.push(`❌账号${user.userName || user.index} >> Check ck error!`) 107 | } 108 | //账号通知 109 | $.notifyList.push({ "id": user.index, "avatar": user.avatar, "message": $.notifyMsg }); 110 | //清空数组 111 | $.notifyMsg = []; 112 | } 113 | } catch (e) { 114 | $.log(`⛔️ main run error => ${e}`); 115 | throw new Error(`⛔️ main run error => ${e}`); 116 | } 117 | } 118 | 119 | 120 | class UserInfo { 121 | constructor(user) { 122 | //默认属性 123 | this.index = ++userIdx; 124 | this.token = user.token || user; 125 | this.userId = user.userId; 126 | this.userName = user.userName; 127 | this.avatar = user.avatar; 128 | this.ckStatus = true; 129 | //请求封装 130 | this.baseUrl = ``; 131 | this.host = "https://c.cfmoto.com"; 132 | this.headers = { 133 | "Cookie": this.token, 134 | "User-Agent": "'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 cfmoto/1.0.0" 135 | } 136 | this.getRandomTime = () => randomInt(1e3, 3e3); 137 | this.fetch = async (o) => { 138 | try { 139 | if (typeof o === 'string') o = { url: o }; 140 | if (o?.url?.startsWith("/")) o.url = this.host + o.url 141 | const res = await Request({ ...o, headers: o.headers || this.headers, url: o.url || this.baseUrl }) 142 | debug(res, o?.url?.replace(/\/+$/, '').substring(o?.url?.lastIndexOf('/') + 1)); 143 | if (res?.code == 40001) throw new Error(res?.message || `用户需要去登录`); 144 | return res; 145 | } catch (e) { 146 | this.ckStatus = false; 147 | $.log(`⛔️ 请求发起失败!${e}`); 148 | } 149 | } 150 | } 151 | //签到 152 | async signin() { 153 | try { 154 | const opts = { 155 | url: this.host + "/cfmotoservermall/app/integral/task/complete/v1", 156 | method: "put", 157 | headers: { 158 | "Cookie": this.token, 159 | "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 cfmoto/1.0.0", 160 | "Content-Type": "application/json;charset=UTF-8" 161 | }, 162 | body: JSON.stringify({ 163 | completeStatu: 1, 164 | taskDetail: 8 165 | }) 166 | } 167 | let res = await new Promise((resolve, reject) => { 168 | $.http['post'](opts) 169 | .then((response) => { 170 | var resp = response.body; 171 | try { 172 | resp = $.toObj(resp) || resp; 173 | } catch (e) { } 174 | resolve(resp); 175 | }) 176 | .catch((err) => reject(err)); 177 | }); 178 | if (res.code == 0) { 179 | $.log(`✅ 签到任务: 已完成`); 180 | const point = parseInt(res.data) 181 | return point 182 | } else { 183 | $.log(`✅ 签到任务: 今日已签到`); 184 | return null 185 | } 186 | } catch (e) { 187 | this.ckStatus = false; 188 | $.log(`⛔️ 签到失败! ${e}`); 189 | } 190 | } 191 | // 开启盲盒 192 | async lottery() { 193 | try { 194 | const opts = { 195 | url: `/cfmotoservermall/app/user/prize/sign`, 196 | type: "post", 197 | dataType: "json", 198 | body: {} 199 | } 200 | let res = await this.fetch(opts); 201 | if(res?.code == 0) { 202 | const prizeSignName = res?.data?.prizeSignName 203 | $.log(`✅ 盲盒抽奖获得: ${prizeSignName}`); 204 | }else{ 205 | $.log(`⛔️ 盲盒抽奖失败! ${res?.msg}`); 206 | } 207 | } catch (e) { 208 | this.ckStatus = false; 209 | $.log(`⛔️ 盲盒抽奖失败! ${e}`); 210 | } 211 | } 212 | // 创建帖子 213 | async createArticle() { 214 | try { 215 | const opts = { 216 | url: `/jv/bbs/post/create-v5/`, 217 | type: "post", 218 | dataType: "json", 219 | body: { 220 | share_content: "", 221 | media_type: "TEXT", 222 | longitude: "0.000000", 223 | latitude: "0.000000", 224 | share_type: "", 225 | share_title: "", 226 | address: "", 227 | share_product_type: "", 228 | share_whether_journey: "", 229 | share_image: "", 230 | city_code: "", 231 | is_vote: false, 232 | content: "加油" 233 | } 234 | } 235 | let res = await this.fetch(opts); 236 | const postId = res.data.id 237 | $.log(`✅ 创建帖子: ${postId}`); 238 | return postId 239 | } catch (e) { 240 | this.ckStatus = false; 241 | $.log(`⛔️ 创建帖子失败! ${e}`); 242 | } 243 | } 244 | // 评论帖子 245 | async postComments(postId) { 246 | try { 247 | const opts = { 248 | url: `/jv/bbs/article/comment-v1/`, 249 | type: "post", 250 | dataType: "json", 251 | body: { 252 | post_id: postId, 253 | content: "666" 254 | } 255 | } 256 | await this.fetch(opts); 257 | $.log(`✅ 评论帖子: ${postId}`) 258 | } catch (e) { 259 | this.ckStatus = false; 260 | $.log(`⛔️ 评论帖子失败! ${e}`); 261 | } 262 | } 263 | // 点赞帖子 264 | async thumbsUp(postId) { 265 | try { 266 | const opts = { 267 | url: `/jv/bbs/post/thumbs_up/${postId}`, 268 | type: "post", 269 | dataType: "json", 270 | body: {} 271 | } 272 | await this.fetch(opts); 273 | $.log(`✅ 点赞帖子: ${postId}`) 274 | } catch (e) { 275 | this.ckStatus = false; 276 | $.log(`⛔️ 点赞帖子失败! ${e}`); 277 | } 278 | } 279 | // 删除帖子 280 | async deletePost(postId) { 281 | try { 282 | const opts = { 283 | url: this.host + "/jv/bbs/post/?id=" + postId, 284 | method: "delete", 285 | headers: { 286 | "Cookie": this.token, 287 | "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 cfmoto/1.0.0", 288 | "Content-Type": "application/json;charset=UTF-8" 289 | }, 290 | body: JSON.stringify({}) 291 | } 292 | await new Promise((resolve, reject) => { 293 | $.http['post'](opts) 294 | .then((response) => { 295 | var resp = response.body; 296 | try { 297 | resp = $.toObj(resp) || resp; 298 | } catch (e) { } 299 | resolve(resp); 300 | }) 301 | .catch((err) => reject(err)); 302 | }); 303 | $.log(`✅ 删除帖子: ${postId}`) 304 | } catch (e) { 305 | this.ckStatus = false; 306 | $.log(`⛔️ 删除帖子失败! ${e}`); 307 | } 308 | } 309 | // 分享帖子 310 | async share(postId) { 311 | try { 312 | const opts = { 313 | url: this.host + "/cfmotoservermall/app/integral/task/complete", 314 | method: "put", 315 | headers: { 316 | "Cookie": this.token, 317 | "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 cfmoto/1.0.0", 318 | "Content-Type": "application/json;charset=UTF-8" 319 | }, 320 | body: JSON.stringify({ 321 | completeStatu: 1, 322 | taskDetail: 13 323 | }) 324 | } 325 | await new Promise((resolve, reject) => { 326 | $.http['post'](opts) 327 | .then((response) => { 328 | var resp = response.body; 329 | try { 330 | resp = $.toObj(resp) || resp; 331 | } catch (e) { } 332 | resolve(resp); 333 | }) 334 | .catch((err) => reject(err)); 335 | }); 336 | $.log(`✅ 分享帖子: ${postId}`) 337 | } catch (e) { 338 | this.ckStatus = false; 339 | $.log(`⛔️ 分享失败! ${e}`); 340 | } 341 | } 342 | // 查询用户信息 343 | async getSignInfo() { 344 | try { 345 | const opts = { 346 | url: `/cfmotoservermall/app/user/integral/current/user/info`, 347 | type: "get", 348 | dataType: "json" 349 | } 350 | let res = await this.fetch(opts); 351 | if (res.code == 0) { 352 | const integralTotal = res.data.integralTotal 353 | return integralTotal 354 | } 355 | return null 356 | } catch (e) { 357 | this.ckStatus = false; 358 | $.log(`⛔️ 查询用户信息失败! ${e}`); 359 | } 360 | } 361 | // 查询签到记录 362 | async getSignRecord() { 363 | try { 364 | const opts = { 365 | url: `/cfmotoservermall/app/integral/task/signinlist/v1`, 366 | type: "get", 367 | params: { 368 | year: new Date().getFullYear(), 369 | month: new Date().getMonth() + 1 370 | }, 371 | dataType: "json" 372 | } 373 | let res = await this.fetch(opts); 374 | if (res.code == 0) { 375 | const count = res?.data?.count 376 | const prize = res?.data?.prize 377 | $.log(prize ? `✅ 满足盲盒抽奖条件` : `✅ 未满足盲盒抽奖条件`) 378 | return {count, prize} 379 | } 380 | return null 381 | } catch (e) { 382 | this.ckStatus = false; 383 | $.log(`⛔️ 查询签到记录失败! ${e}`); 384 | } 385 | } 386 | // 获取限时抽奖活动列表 387 | async getLotteryList() { 388 | try { 389 | const opts = { 390 | url: `/jv/bbs/lottery/listgoing/`, 391 | type: "get", 392 | params: {}, 393 | dataType: "json" 394 | } 395 | let res = await this.fetch(opts); 396 | if (res.code == 0) { 397 | const list = res?.data?.list 398 | if(list && list.length) { 399 | for(let item of list) { 400 | if(item.lottery_status_name === "进行中") { 401 | $.log(`✅ 开始参与限时抽奖活动`) 402 | await this.getLotteryDetail(item.id) 403 | } 404 | } 405 | if(list[list.length - 1].lottery_status_name !== '进行中') { 406 | $.log(`✅ 当前无限时抽奖活动`) 407 | } 408 | } else { 409 | $.log(`✅ 当前无限时抽奖活动`) 410 | } 411 | } 412 | } catch (e) { 413 | this.ckStatus = false; 414 | $.log(`⛔️ 查询限时抽奖活动列表失败! ${e}`); 415 | } 416 | } 417 | // 获取限时抽奖活动详情 418 | async getLotteryDetail(id) { 419 | try { 420 | const opts = { 421 | url: `/jv/bbs/lottery/${id}/detail/`, 422 | type: "get", 423 | params: {}, 424 | dataType: "json" 425 | } 426 | let res = await this.fetch(opts); 427 | if (res.code == 0) { 428 | const tickitList = res?.data?.lottery_user_ticket_list 429 | if(!tickitList) { 430 | await this.lotteryApply(id) 431 | } 432 | await this.lotteryShare(id) 433 | } 434 | } catch (e) { 435 | this.ckStatus = false; 436 | $.log(`⛔️ 查询限时抽奖活动详情失败! ${e}`); 437 | } 438 | } 439 | // 报名限时抽奖活动 440 | async lotteryApply(id) { 441 | try { 442 | const opts = { 443 | url: `/jv/bbs/lottery/${id}/apply/`, 444 | type: "get", 445 | params: {}, 446 | dataType: "json" 447 | } 448 | let res = await this.fetch(opts); 449 | if (res.code == 0) { 450 | $.log(`✅ 报名限时抽奖活动成功!`) 451 | } 452 | } catch (e) { 453 | this.ckStatus = false; 454 | $.log(`⛔️ 报名限时抽奖活动失败! ${e}`); 455 | } 456 | } 457 | // 报名限时抽奖活动 458 | async lotteryShare(id) { 459 | try { 460 | const opts = { 461 | url: `/jv/bbs/lottery/${id}/share/`, 462 | type: "get", 463 | params: {}, 464 | dataType: "json" 465 | } 466 | let res = await this.fetch(opts); 467 | if (res.code == 0) { 468 | $.log(`✅ 分享限时抽奖活动成功!`) 469 | } 470 | } catch (e) { 471 | this.ckStatus = false; 472 | $.log(`⛔️ 分享限时抽奖活动失败! ${e}`); 473 | } 474 | } 475 | } 476 | async function getCookie() { 477 | if ($request && $request.method === 'OPTIONS') return; 478 | 479 | const header = ObjectKeys2LowerCase($request.headers); 480 | const cookie = header.cookie; 481 | const body = $.toObj($response.body); 482 | if (!(body?.data)) { 483 | $.msg($.name, `❌获取Cookie失败!`, "") 484 | return; 485 | } 486 | 487 | const { id, nickname, photo } = body?.data; 488 | const newData = { 489 | "userId": id, 490 | "avatar": photo, 491 | "token": cookie, 492 | "userName": nickname, 493 | } 494 | 495 | userCookie = userCookie ? JSON.parse(userCookie) : []; 496 | const index = userCookie.findIndex(e => e.userId == newData.userId); 497 | 498 | userCookie[index] ? userCookie[index] = newData : userCookie.push(newData); 499 | 500 | $.setjson(userCookie, ckName); 501 | $.msg($.name, `🎉${newData.userName}更新token成功!`, ``); 502 | } 503 | //-------------------------- 辅助函数区域 ----------------------------------- 504 | //请求二次封装 505 | async function Request(o) { 506 | if (typeof o === 'string') o = { url: o }; 507 | try { 508 | if (!o?.url) throw new Error('[发送请求] 缺少 url 参数'); 509 | // type => 因为env中使用method处理post的特殊请求(put/delete/patch), 所以这里使用type 510 | let { url: u, type, headers = {}, body: b, params, dataType = 'form', resultType = 'data' } = o; 511 | // post请求需要处理params参数(get不需要, env已经处理) 512 | const method = type ? type?.toLowerCase() : ('body' in o ? 'post' : 'get'); 513 | const url = u.concat(method === 'post' ? '?' + $.queryStr(params) : ''); 514 | 515 | const timeout = o.timeout ? ($.isSurge() ? o.timeout / 1e3 : o.timeout) : 1e4 516 | // 根据jsonType处理headers 517 | if (dataType === 'json') headers['Content-Type'] = 'application/json;charset=UTF-8'; 518 | // post请求处理body 519 | const body = b && dataType == 'form' ? $.queryStr(b) : $.toStr(b); 520 | const request = { ...o, ...(o?.opts ? o.opts : {}), url, headers, ...(method === 'post' && { body }), ...(method === 'get' && params && { params }), timeout: timeout } 521 | const httpPromise = $.http[method.toLowerCase()](request) 522 | .then(response => resultType == 'data' ? ($.toObj(response.body) || response.body) : ($.toObj(response) || response)) 523 | .catch(err => $.log(`❌请求发起失败!原因为:${err}`)); 524 | // 使用Promise.race来强行加入超时处理 525 | return Promise.race([ 526 | new Promise((_, e) => setTimeout(() => e('当前请求已超时'), timeout)), 527 | httpPromise 528 | ]); 529 | } catch (e) { 530 | console.log(`❌请求发起失败!原因为:${e}`); 531 | } 532 | }; 533 | //生成随机数 534 | function randomInt(n, r) { 535 | return Math.round(Math.random() * (r - n) + n) 536 | }; 537 | //控制台打印 538 | function DoubleLog(data) { 539 | if (data && $.isNode()) { 540 | console.log(`${data}`); 541 | $.notifyMsg.push(`${data}`) 542 | } else if (data) { 543 | console.log(`${data}`); 544 | $.notifyMsg.push(`${data}`) 545 | } 546 | }; 547 | //调试 548 | function debug(t, l = 'debug') { 549 | if ($.is_debug === 'true') { 550 | $.log(`\n-----------${l}------------\n`); 551 | $.log(typeof t == "string" ? t : $.toStr(t) || `debug error => t=${t}`); 552 | $.log(`\n-----------${l}------------\n`) 553 | } 554 | }; 555 | //对多账号通知进行兼容 556 | async function SendMsgList(l) { 557 | await Promise.allSettled(l?.map(u => SendMsg(u.message.join('\n'), u.avatar))); 558 | }; 559 | //账号通知 560 | async function SendMsg(n, o) { 561 | n && (0 < Notify ? $.isNode() ? await notify.sendNotify($.name, n) : $.msg($.name, $.title || "", n, { 562 | "media-url": o 563 | }) : console.log(n)) 564 | }; 565 | //将请求头转换为小写 566 | function ObjectKeys2LowerCase(obj) { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])) } 567 | //---------------------- 主程序执行入口 ----------------------------------- 568 | !(async () => { 569 | if (typeof $request != "undefined") { 570 | await getCookie(); 571 | } else { 572 | const e = envSplitor.find(o => userCookie.includes(o)) || envSplitor[0]; 573 | userCookie = $.toObj(userCookie) || userCookie.split(e); 574 | 575 | userList.push(...userCookie.map(n => new UserInfo(n)).filter(Boolean)); 576 | 577 | userCount = userList.length; 578 | console.log(`共找到${userCount}个账号`); 579 | if (userList.length > 0) await main(); 580 | } 581 | })() 582 | .catch(e => $.notifyMsg.push(e.message || e)) 583 | .finally(async () => { 584 | await SendMsgList($.notifyList); 585 | $.done({ ok: 1 }); 586 | }); 587 | /** ---------------------------------固定不动区域----------------------------------------- */ 588 | // prettier-ignore 589 | //From chavyleung's Env.js 590 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise(((e, r) => { s.call(this, t, ((t, s, a) => { t ? r(t) : e(s) })) })) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; if (this.getdata(t)) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise((e => { this.get({ url: t }, ((t, s, r) => e(r))) })) } runScript(t, e) { return new Promise((s => { let r = this.getdata("@chavy_boxjs_userCfgs.httpapi"); r = r ? r.replace(/\n/g, "").trim() : r; let a = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); a = a ? 1 * a : 20, a = e && e.timeout ? e.timeout : a; const [i, o] = r.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: a }, headers: { "X-Key": i, Accept: "*/*" }, timeout: a }; this.post(n, ((t, e, r) => s(r))) })).catch((t => this.logErr(t))) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e); if (!s && !r) return {}; { const r = s ? t : e; try { return JSON.parse(this.fs.readFileSync(r)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e), a = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, a) : r ? this.fs.writeFileSync(e, a) : this.fs.writeFileSync(t, a) } } lodash_get(t, e, s = void 0) { const r = e.replace(/\[(\d+)\]/g, ".$1").split("."); let a = t; for (const t of r) if (a = Object(a)[t], void 0 === a) return s; return a } lodash_set(t, e, s) { return Object(t) !== t || (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce(((t, s, r) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[r + 1]) >> 0 == +e[r + 1] ? [] : {}), t)[e[e.length - 1]] = s), t } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, r] = /^@(.*?)\.(.*?)$/.exec(t), a = s ? this.getval(s) : ""; if (a) try { const t = JSON.parse(a); e = t ? this.lodash_get(t, r, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, r, a] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(r), o = r ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, a, t), s = this.setval(JSON.stringify(e), r) } catch (e) { const i = {}; this.lodash_set(i, a, t), s = this.setval(JSON.stringify(i), r) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, ((t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) })); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then((t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }), (t => e(t && t.error || "UndefinedError"))); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", ((t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } })).then((t => { const { statusCode: r, statusCode: a, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: r, statusCode: a, headers: i, rawBody: o, body: n }, n) }), (t => { const { message: r, response: a } = t; e(r, a, a && s.decode(a.rawBody, this.encoding)) })) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, ((t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) })); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then((t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }), (t => e(t && t.error || "UndefinedError"))); break; case "Node.js": let r = require("iconv-lite"); this.initGotEnv(t); const { url: a, ...i } = t; this.got[s](a, i).then((t => { const { statusCode: s, statusCode: a, headers: i, rawBody: o } = t, n = r.decode(o, this.encoding); e(null, { status: s, statusCode: a, headers: i, rawBody: o, body: n }, n) }), (t => { const { message: s, response: a } = t; e(s, a, a && r.decode(a.rawBody, this.encoding)) })) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let r = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in r) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? r[e] : ("00" + r[e]).substr(("" + r[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let r = t[s]; null != r && "" !== r && ("object" == typeof r && (r = JSON.stringify(r)), e += `${s}=${r}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", r = "", a) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: return { url: t.url || t.openUrl || t["open-url"] }; case "Loon": return { openUrl: t.openUrl || t.url || t["open-url"], mediaUrl: t.mediaUrl || t["media-url"] }; case "Quantumult X": return { "open-url": t["open-url"] || t.url || t.openUrl, "media-url": t["media-url"] || t.mediaUrl, "update-pasteboard": t["update-pasteboard"] || t.updatePasteboard }; case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, r, i(a)); break; case "Quantumult X": $notify(e, s, r, i(a)); case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), r && t.push(r), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t.stack) } } wait(t) { return new Promise((e => setTimeout(e, t))) } done(t = {}) { const e = ((new Date).getTime() - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${e} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) } 591 | -------------------------------------------------------------------------------- /script/lhtj/lhtj.js: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------ 3 | @Author: Leiyiyan 4 | @Date: 2024-10-08 11:25:00 5 | @Description: 龙湖天街小程序签到、抽奖 6 | ------------------------------------------ 7 | 获取 Cookie:打开龙湖天街小程序,进入 我的 - 签到赚珑珠 - 任务赚奖励 - 马上签到。 8 | 9 | 图标:https://raw.githubusercontent.com/leiyiyan/resource/main/icons/lhtj.png 10 | 11 | [Script] 12 | http-request ^https?:\/\/gw2c\-hw\-open\.longfor\.com\/lmarketing\-task\-api\-mvc\-prod\/openapi\/task\/v1\/signature\/clock script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/lhtj/lhtj.js, timeout=60, tag=龙湖天街获取Cookie 13 | 14 | [MITM] 15 | hostname = gw2c-hw-open.longfor.com 16 | 17 | ⚠️【免责声明】 18 | ------------------------------------------ 19 | 1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 20 | 2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 21 | 3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。 22 | 4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 23 | 5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 24 | 6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。 25 | 7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。 26 | */ 27 | const $ = new Env("龙湖天街"); 28 | const ckName = "lhtj_data"; 29 | const userCookie = $.toObj($.isNode() ? process.env[ckName] : $.getdata(ckName)) || []; 30 | //notify 31 | const notify = $.isNode() ? require('./sendNotify') : ''; 32 | $.notifyMsg = [] 33 | //debug 34 | $.is_debug = ($.isNode() ? process.env.IS_DEDUG : $.getdata('is_debug')) || 'false'; 35 | $.doFlag = { "true": "✅", "false": "⛔️" }; 36 | //------------------------------------------ 37 | const baseUrl = "" 38 | const _headers = {} 39 | 40 | //------------------------------------------ 41 | const fetch = async (o) => { 42 | try { 43 | if (typeof o === 'string') o = { url: o }; 44 | if (o?.url?.startsWith("/") || o?.url?.startsWith(":")) o.url = baseUrl + o.url 45 | const res = await Request({ ...o, headers: o.headers || _headers, url: o.url }) 46 | debug(res, o?.url?.replace(/\/+$/, '').substring(o?.url?.lastIndexOf('/') + 1)); 47 | if (res?.message.match(/登录已过期|用户未登录/)) throw new Error(`用户需要去登录`); 48 | return res; 49 | } catch (e) { 50 | $.ckStatus = false; 51 | $.log(`⛔️ 请求发起失败!${e}`); 52 | } 53 | } 54 | async function main() { 55 | try { 56 | //check accounts 57 | if (!userCookie?.length) throw new Error("找不到可用的帐户"); 58 | $.log(`⚙️ 发现 ${userCookie?.length ?? 0} 个帐户\n`); 59 | let index = 0; 60 | //doTask of userList 61 | for (let user of userCookie) { 62 | //init of user 63 | $.log(`🚀 开始任务`), 64 | $.notifyMsg = [], 65 | $.ckStatus = true, 66 | $.title = "", 67 | $.avatar = ""; 68 | 69 | // 签到 70 | const reward_num = await signin(user); 71 | if ($.ckStatus) { 72 | // 抽奖签到 73 | await lotterySignin(user) 74 | // 抽奖 75 | await lotteryClock(user) 76 | //查询用户信息 77 | const { nick_name, growth_value, level, head_portrait } = await getUserInfo(user) 78 | //查询珑珠 79 | const { balance } = await getBalance(user) 80 | $.avatar = head_portrait; 81 | $.title = `本次运行共获得${reward_num}积分` 82 | DoubleLog(`当前用户:${nick_name}\n成长值: ${growth_value} 等级: V${level} 珑珠: ${balance}`) 83 | } else { 84 | DoubleLog(`⛔️ 「${user.userName ?? `账号${index}`}」check ck error!`) 85 | } 86 | //notify 87 | await sendMsg($.notifyMsg.join("\n")); 88 | } 89 | } catch (e) { 90 | throw e 91 | } 92 | } 93 | 94 | //签到 95 | async function signin(user) { 96 | try { 97 | const opts = { 98 | url: "https://gw2c-hw-open.longfor.com/lmarketing-task-api-mvc-prod/openapi/task/v1/signature/clock", 99 | headers: { 100 | 'cookie': user.cookie, 101 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x18003029) NetType/4G Language/zh_CN miniProgram/wx50282644351869da', 102 | 'token': user.token, 103 | 'x-lf-dxrisk-token': user['x-lf-dxrisk-token'], 104 | 'x-gaia-api-key': 'c06753f1-3e68-437d-b592-b94656ea5517', 105 | 'x-lf-bu-code': user['x-lf-bu-code'], 106 | 'x-lf-channel': user['x-lf-channel'], 107 | 'origin': 'https://longzhu.longfor.com', 108 | 'referer': 'https://longzhu.longfor.com/', 109 | 'x-lf-dxrisk-source': user['x-lf-dxrisk-source'], 110 | 'x-lf-usertoken': user['x-lf-usertoken'] 111 | }, 112 | type: 'post', 113 | dataType: "json", 114 | body: { 115 | 'activity_no': '11111111111686241863606037740000' 116 | } 117 | } 118 | let res = await fetch(opts); 119 | const reward_num = res?.data?.is_popup == 1 ? res?.data?.reward_info[0]?.reward_num : 0 120 | $.log(`${$.doFlag[res?.data?.is_popup == 1]} ${res?.data?.is_popup == 1 ? '每日签到: 成功, 获得' + res?.data?.reward_info[0]?.reward_num + '分' : '每日签到: 今日已签到'}\n`); 121 | return reward_num 122 | } catch (e) { 123 | $.log(`⛔️ 每日签到失败!${e}\n`) 124 | } 125 | } 126 | // 抽奖签到 127 | async function lotterySignin(user) { 128 | try { 129 | const opts = { 130 | url: "https://gw2c-hw-open.longfor.com/lmarketing-task-api-mvc-prod/openapi/task/v1/lottery/sign", 131 | headers: { 132 | 'cookie': user.cookie, 133 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x18003029) NetType/4G Language/zh_CN miniProgram/wx50282644351869da', 134 | 'x-lf-usertoken': user.token, 135 | 'x-gaia-api-key': 'c06753f1-3e68-437d-b592-b94656ea5517', 136 | 'x-lf-bu-code': user['x-lf-bu-code'], 137 | 'x-lf-channel': user['x-lf-channel'], 138 | 'origin': 'https://longzhu.longfor.com', 139 | 'referer': 'https://longzhu.longfor.com/' 140 | }, 141 | type: 'post', 142 | dataType: "json", 143 | body: { 144 | "task_id": "", 145 | "activity_no": "11111111111727686365925771280000" 146 | } 147 | } 148 | let res = await fetch(opts); 149 | $.log(`${$.doFlag[res?.code == '0000']} ${res?.code == '0000' ? '抽奖签到: 成功, 获得' + res?.data?.ticket_times + '次抽奖机会' : '抽奖签到: ' + res?.message}\n`); 150 | } catch (e) { 151 | $.log(`⛔️ 抽奖签到失败!${e}\n`) 152 | } 153 | } 154 | // 抽奖 155 | async function lotteryClock(user) { 156 | try { 157 | const opts = { 158 | url: "https://gw2c-hw-open.longfor.com/lmarketing-task-api-mvc-prod/openapi/task/v1/lottery/luck", 159 | headers: { 160 | 'cookie': user.cookie, 161 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x18003029) NetType/4G Language/zh_CN miniProgram/wx50282644351869da', 162 | 'x-lf-usertoken': user.token, 163 | 'x-lf-dxrisk-token': user['x-lf-dxrisk-token'], 164 | 'x-gaia-api-key': 'c06753f1-3e68-437d-b592-b94656ea5517', 165 | 'x-lf-bu-code': user['x-lf-bu-code'], 166 | 'x-lf-channel': user['x-lf-channel'], 167 | 'origin': 'https://longzhu.longfor.com', 168 | 'referer': 'https://longzhu.longfor.com/', 169 | 'x-lf-dxrisk-source': user['x-lf-dxrisk-source'] 170 | }, 171 | type: 'post', 172 | dataType: "json", 173 | body: { 174 | "task_id": "", 175 | "time": getDateTime(), 176 | "activity_no": "11111111111727686365925771280000", 177 | "use_luck": 0 178 | } 179 | } 180 | let res = await fetch(opts); 181 | $.log(`${$.doFlag[res?.code == '0000']} ${res?.code == '0000' ? '抽奖成功, 获得' + res?.data?.desc : '抽奖: ' + res?.message}\n`); 182 | } catch (e) { 183 | $.log(`⛔️ 抽奖失败!${e}\n`) 184 | } 185 | } 186 | //查询用户信息 187 | async function getUserInfo(user) { 188 | try { 189 | const opts = { 190 | url: "https://longzhu-api.longfor.com/lmember-member-open-api-prod/api/member/v1/mine-info", 191 | headers: { 192 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x18003029) NetType/4G Language/zh_CN miniProgram/wx50282644351869da', 193 | 'Referer': 'https://servicewechat.com/wx50282644351869da/424/page-frame.html', 194 | 'token': user.token, 195 | 'X-Gaia-Api-Key': 'd1eb973c-64ec-4dbe-b23b-22c8117c4e8e' 196 | }, 197 | type: 'post', 198 | dataType: "json", 199 | body: { 200 | "channel": user['x-lf-channel'], 201 | "bu_code": user['x-lf-bu-code'], 202 | "token": user.token 203 | } 204 | } 205 | let res = await fetch(opts); 206 | let growth_value = res?.data?.growth_value || 0; 207 | $.log(`🎉 ${res?.code == '0000' ? '您当前成长值: ' + growth_value : res?.message}\n`); 208 | return res?.data 209 | } catch (e) { 210 | $.log(`⛔️ 查询用户信息失败!${e}\n`) 211 | } 212 | } 213 | //查询珑珠 214 | async function getBalance(user) { 215 | try { 216 | const opts = { 217 | url: "https://longzhu-api.longfor.com/lmember-member-open-api-prod/api/member/v1/balance", 218 | headers: { 219 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x18003029) NetType/4G Language/zh_CN miniProgram/wx50282644351869da', 220 | 'Referer': 'https://servicewechat.com/wx50282644351869da/424/page-frame.html', 221 | 'token': user.token, 222 | 'X-Gaia-Api-Key': 'd1eb973c-64ec-4dbe-b23b-22c8117c4e8e' 223 | }, 224 | type: 'post', 225 | dataType: "json", 226 | body: { 227 | "channel": user['x-lf-channel'], 228 | "bu_code": user['x-lf-bu-code'], 229 | "token": user.token 230 | } 231 | } 232 | let res = await fetch(opts); 233 | let balance = res?.data.balance || 0; 234 | let expiring_lz = res?.data.expiring_lz || 0; 235 | $.log(`🎉 ${res?.code == '0000' ? '您当前珑珠: ' + balance + ', 即将过期: ' + expiring_lz : res?.message}\n`); 236 | return res?.data 237 | } catch (e) { 238 | $.log(`⛔️ 查询用户珑珠失败!${e}\n`) 239 | } 240 | } 241 | //获取Cookie 242 | async function getCookie() { 243 | try { 244 | if ($request && $request.method === 'OPTIONS') return; 245 | 246 | const header = ObjectKeys2LowerCase($request.headers); 247 | if (!header.cookie) throw new Error("获取Cookie错误,值为空"); 248 | 249 | const newData = { 250 | "userName": '微信用户', 251 | 'x-lf-dxrisk-token': header['x-lf-dxrisk-token'], 252 | "x-lf-channel": header['x-lf-channel'], 253 | "token": header.token, 254 | 'x-lf-usertoken': header['x-lf-usertoken'], 255 | "cookie": header.cookie, 256 | "x-lf-bu-code": header['x-lf-bu-code'], 257 | 'x-lf-dxrisk-source': header['x-lf-dxrisk-source'] 258 | } 259 | const index = userCookie.findIndex(e => e.token == newData.token); 260 | index !== -1 ? userCookie[index] = newData : userCookie.push(newData); 261 | $.setjson(userCookie, ckName); 262 | $.msg($.name, `🎉获取Cookie成功!`, ``) 263 | } catch (e) { 264 | throw e; 265 | } 266 | } 267 | 268 | 269 | //主程序执行入口 270 | !(async () => { 271 | try { 272 | if (typeof $request != "undefined") { 273 | await getCookie(); 274 | } else { 275 | await main(); 276 | } 277 | } catch (e) { 278 | throw e; 279 | } 280 | })() 281 | .catch((e) => { $.logErr(e), $.msg($.name, `⛔️ script run error!`, e.message || e) }) 282 | .finally(async () => { 283 | $.done({ ok: 1 }); 284 | }); 285 | function getDateTime() { 286 | const date = new Date(); 287 | const year = date.getFullYear(); 288 | const month = String(date.getMonth() + 1).padStart(2, '0'); 289 | const day = String(date.getDate()).padStart(2, '0'); 290 | const hours = String(date.getHours()).padStart(2, '0'); 291 | const minutes = String(date.getMinutes()).padStart(2, '0'); 292 | const seconds = String(date.getSeconds()).padStart(2, '0'); 293 | return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; 294 | } 295 | /** ---------------------------------固定不动区域----------------------------------------- */ 296 | //prettier-ignore 297 | async function sendMsg(a) { a && ($.isNode() ? await notify.sendNotify($.name, a) : $.msg($.name, $.title || "", a, { "media-url": $.avatar })) } 298 | function DoubleLog(o) { o && ($.log(`${o}`), $.notifyMsg.push(`${o}`)) }; 299 | function debug(g, e = "debug") { "true" === $.is_debug && ($.log(`\n-----------${e}------------\n`), $.log("string" == typeof g ? g : $.toStr(g) || `debug error => t=${g}`), $.log(`\n-----------${e}------------\n`)) } 300 | //From xream's ObjectKeys2LowerCase 301 | function ObjectKeys2LowerCase(obj) { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])) }; 302 | //From sliverkiss's Request 303 | async function Request(t) { "string" == typeof t && (t = { url: t }); try { if (!t?.url) throw new Error("[发送请求] 缺少 url 参数"); let { url: o, type: e, headers: r = {}, body: s, params: a, dataType: n = "form", resultType: u = "data" } = t; const p = e ? e?.toLowerCase() : "body" in t ? "post" : "get", c = o.concat("post" === p ? "?" + $.queryStr(a) : ""), i = t.timeout ? $.isSurge() ? t.timeout / 1e3 : t.timeout : 1e4; "json" === n && (r["Content-Type"] = "application/json;charset=UTF-8"); const y = s && "form" == n ? $.queryStr(s) : $.toStr(s), l = { ...t, ...t?.opts ? t.opts : {}, url: c, headers: r, ..."post" === p && { body: y }, ..."get" === p && a && { params: a }, timeout: i }, m = $.http[p.toLowerCase()](l).then((t => "data" == u ? $.toObj(t.body) || t.body : $.toObj(t) || t)).catch((t => $.log(`❌请求发起失败!原因为:${t}`))); return Promise.race([new Promise(((t, o) => setTimeout((() => o("当前请求已超时")), i))), m]) } catch (t) { console.log(`❌请求发起失败!原因为:${t}`) } } 304 | //From chavyleung's Env.js 305 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise(((e, r) => { s.call(this, t, ((t, s, a) => { t ? r(t) : e(s) })) })) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; if (this.getdata(t)) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise((e => { this.get({ url: t }, ((t, s, r) => e(r))) })) } runScript(t, e) { return new Promise((s => { let r = this.getdata("@chavy_boxjs_userCfgs.httpapi"); r = r ? r.replace(/\n/g, "").trim() : r; let a = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); a = a ? 1 * a : 20, a = e && e.timeout ? e.timeout : a; const [i, o] = r.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: a }, headers: { "X-Key": i, Accept: "*/*" }, timeout: a }; this.post(n, ((t, e, r) => s(r))) })).catch((t => this.logErr(t))) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e); if (!s && !r) return {}; { const r = s ? t : e; try { return JSON.parse(this.fs.readFileSync(r)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e), a = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, a) : r ? this.fs.writeFileSync(e, a) : this.fs.writeFileSync(t, a) } } lodash_get(t, e, s = void 0) { const r = e.replace(/\[(\d+)\]/g, ".$1").split("."); let a = t; for (const t of r) if (a = Object(a)[t], void 0 === a) return s; return a } lodash_set(t, e, s) { return Object(t) !== t || (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce(((t, s, r) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[r + 1]) >> 0 == +e[r + 1] ? [] : {}), t)[e[e.length - 1]] = s), t } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, r] = /^@(.*?)\.(.*?)$/.exec(t), a = s ? this.getval(s) : ""; if (a) try { const t = JSON.parse(a); e = t ? this.lodash_get(t, r, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, r, a] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(r), o = r ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, a, t), s = this.setval(JSON.stringify(e), r) } catch (e) { const i = {}; this.lodash_set(i, a, t), s = this.setval(JSON.stringify(i), r) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, ((t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) })); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then((t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }), (t => e(t && t.error || "UndefinedError"))); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", ((t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } })).then((t => { const { statusCode: r, statusCode: a, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: r, statusCode: a, headers: i, rawBody: o, body: n }, n) }), (t => { const { message: r, response: a } = t; e(r, a, a && s.decode(a.rawBody, this.encoding)) })) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, ((t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) })); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then((t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }), (t => e(t && t.error || "UndefinedError"))); break; case "Node.js": let r = require("iconv-lite"); this.initGotEnv(t); const { url: a, ...i } = t; this.got[s](a, i).then((t => { const { statusCode: s, statusCode: a, headers: i, rawBody: o } = t, n = r.decode(o, this.encoding); e(null, { status: s, statusCode: a, headers: i, rawBody: o, body: n }, n) }), (t => { const { message: s, response: a } = t; e(s, a, a && r.decode(a.rawBody, this.encoding)) })) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let r = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in r) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? r[e] : ("00" + r[e]).substr(("" + r[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let r = t[s]; null != r && "" !== r && ("object" == typeof r && (r = JSON.stringify(r)), e += `${s}=${r}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", r = "", a) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: return { url: t.url || t.openUrl || t["open-url"] }; case "Loon": return { openUrl: t.openUrl || t.url || t["open-url"], mediaUrl: t.mediaUrl || t["media-url"] }; case "Quantumult X": return { "open-url": t["open-url"] || t.url || t.openUrl, "media-url": t["media-url"] || t.mediaUrl, "update-pasteboard": t["update-pasteboard"] || t.updatePasteboard }; case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, r, i(a)); break; case "Quantumult X": $notify(e, s, r, i(a)); case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), r && t.push(r), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, t.stack) } } wait(t) { return new Promise((e => setTimeout(e, t))) } done(t = {}) { const e = ((new Date).getTime() - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${e} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) } 306 | -------------------------------------------------------------------------------- /script/netease_musician/cookie.js: -------------------------------------------------------------------------------- 1 | // 网易云音乐人获取Cookie 2 | const $ = new Env("网易云Cookie") 3 | 4 | !(async () => { 5 | 6 | if (!$.getdata("Netease_Musician_Cookie") || $.getdata("Netease_Musician_Cookie") == "") { 7 | 8 | const cookie = $request.headers["cookie"] || $request.headers["Cookie"] 9 | const userAgent = $request.headers["user-agent"] || $request.headers["User-Agent"] 10 | 11 | $.setdata(cookie, "Netease_Musician_Cookie") 12 | $.setdata(userAgent, "Netease_Musician_UserAgent") 13 | 14 | $.log($.name, "Cookie获取成功", cookie) 15 | $.msg($.name, "Cookie获取成功!", "cookie: " + cookie) 16 | $.done() 17 | } 18 | 19 | }) (); 20 | 21 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, i) => { s.call(this, t, (t, s, r) => { t ? i(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `\ud83d\udd14${this.name}, \u5f00\u59cb!`) } isNode() { return "undefined" != typeof module && !!module.exports } isQuanX() { return "undefined" != typeof $task } isSurge() { return "undefined" != typeof $httpClient && "undefined" == typeof $loon } isLoon() { return "undefined" != typeof $loon } isShadowrocket() { return "undefined" != typeof $rocket } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; const i = this.getdata(t); if (i) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, i) => e(i)) }) } runScript(t, e) { return new Promise(s => { let i = this.getdata("@chavy_boxjs_userCfgs.httpapi"); i = i ? i.replace(/\n/g, "").trim() : i; let r = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); r = r ? 1 * r : 20, r = e && e.timeout ? e.timeout : r; const [o, h] = i.split("@"), n = { url: `http://${h}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": o, Accept: "*/*" } }; this.post(n, (t, e, i) => s(i)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), i = !s && this.fs.existsSync(e); if (!s && !i) return {}; { const i = s ? t : e; try { return JSON.parse(this.fs.readFileSync(i)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), i = !s && this.fs.existsSync(e), r = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, r) : i ? this.fs.writeFileSync(e, r) : this.fs.writeFileSync(t, r) } } lodash_get(t, e, s) { const i = e.replace(/\[(\d+)\]/g, ".$1").split("."); let r = t; for (const t of i) if (r = Object(r)[t], void 0 === r) return s; return r } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, i) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[i + 1]) >> 0 == +e[i + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, i] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, i, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, i, r] = /^@(.*?)\.(.*?)$/.exec(e), o = this.getval(i), h = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(h); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), i) } catch (e) { const o = {}; this.lodash_set(o, r, t), s = this.setval(JSON.stringify(o), i) } } else s = this.setval(t, e); return s } getval(t) { return this.isSurge() || this.isLoon() ? $persistentStore.read(t) : this.isQuanX() ? $prefs.valueForKey(t) : this.isNode() ? (this.data = this.loaddata(), this.data[t]) : this.data && this.data[t] || null } setval(t, e) { return this.isSurge() || this.isLoon() ? $persistentStore.write(t, e) : this.isQuanX() ? $prefs.setValueForKey(t, e) : this.isNode() ? (this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0) : this.data && this.data[e] || null } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { if (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"]), this.isSurge() || this.isLoon()) this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, i) => { !t && s && (s.body = i, s.statusCode = s.status), e(t, s, i) }); else if (this.isQuanX()) this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: i, headers: r, body: o } = t; e(null, { status: s, statusCode: i, headers: r, body: o }, o) }, t => e(t)); else if (this.isNode()) { let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: i, statusCode: r, headers: o, rawBody: h } = t; e(null, { status: i, statusCode: r, headers: o, rawBody: h }, s.decode(h, this.encoding)) }, t => { const { message: i, response: r } = t; e(i, r, r && s.decode(r.rawBody, this.encoding)) }) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; if (t.body && t.headers && !t.headers["Content-Type"] && (t.headers["Content-Type"] = "application/x-www-form-urlencoded"), t.headers && delete t.headers["Content-Length"], this.isSurge() || this.isLoon()) this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, (t, s, i) => { !t && s && (s.body = i, s.statusCode = s.status), e(t, s, i) }); else if (this.isQuanX()) t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: i, headers: r, body: o } = t; e(null, { status: s, statusCode: i, headers: r, body: o }, o) }, t => e(t)); else if (this.isNode()) { let i = require("iconv-lite"); this.initGotEnv(t); const { url: r, ...o } = t; this.got[s](r, o).then(t => { const { statusCode: s, statusCode: r, headers: o, rawBody: h } = t; e(null, { status: s, statusCode: r, headers: o, rawBody: h }, i.decode(h, this.encoding)) }, t => { const { message: s, response: r } = t; e(s, r, r && i.decode(r.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let i = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in i) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? i[e] : ("00" + i[e]).substr(("" + i[e]).length))); return t } msg(e = t, s = "", i = "", r) { const o = t => { if (!t) return t; if ("string" == typeof t) return this.isLoon() ? t : this.isQuanX() ? { "open-url": t } : this.isSurge() ? { url: t } : void 0; if ("object" == typeof t) { if (this.isLoon()) { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } if (this.isQuanX()) { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl; return { "open-url": e, "media-url": s } } if (this.isSurge()) { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } } }; if (this.isMute || (this.isSurge() || this.isLoon() ? $notification.post(e, s, i, o(r)) : this.isQuanX() && $notify(e, s, i, o(r))), !this.isMuteLog) { let t = ["", "==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="]; t.push(e), s && t.push(s), i && t.push(i), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { const s = !this.isSurge() && !this.isQuanX() && !this.isLoon(); s ? this.log("", `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t.stack) : this.log("", `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t) } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; this.log("", `\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${s} \u79d2`), this.log(), (this.isSurge() || this.isQuanX() || this.isLoon()) && $done(t) } }(t, e) } 22 | 23 | -------------------------------------------------------------------------------- /script/ningji/ningji.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 脚本名称:柠季 3 | * 活动规则:每日签到 4 | * 脚本说明:添加重写进入"柠季"小程序-顶部轮播图-4月签到界面,即可获取 Token,支持多账号,兼容🐉青龙。 5 | * 环境变量:ningji_data=[{"cardId": "抓包响应体cardId","campaignId":"抓包请求头campaignId","token": "抓包* 抓包请求头x-token"}] 6 | * 更新时间:2024-08-01 11:43 7 | * 图标地址:https://raw.githubusercontent.com/leiyiyan/resource/main/icons/ningji.png 8 | 9 | ------------------ Surge 配置 ------------------ 10 | 11 | [MITM] 12 | hostname = %APPEND% pos.meituan.com 13 | 14 | [Script] 15 | 柠季Token = type=http-response,pattern=^https?:\/\/pos\.meituan\.com\/api\/v1\/crm\/frontend\/campaign\/display,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js,script-update-interval=0 16 | 17 | 柠季 = type=cron,cronexp=30 9 * * *,timeout=60,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js,script-update-interval=0 18 | 19 | ------------------- Loon 配置 ------------------- 20 | 21 | [MITM] 22 | hostname = pos.meituan.com 23 | 24 | [Script] 25 | http-response ^https?:\/\/pos\.meituan\.com\/api\/v1\/crm\/frontend\/campaign\/display tag=柠季²,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js,requires-body=1 26 | 27 | cron "30 9 * * *" script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js,tag=柠季,enable=true 28 | 29 | --------------- Quantumult X 配置 --------------- 30 | 31 | [MITM] 32 | hostname = pos.meituan.com 33 | 34 | [rewrite_local] 35 | ^https?:\/\/pos\.meituan\.com\/api\/v1\/crm\/frontend\/campaign\/display url script-response-body https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js 36 | 37 | [task_local] 38 | 30 9 * * * https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js, tag=柠季, img-url=https://raw.githubusercontent.com/leiyiyan/resource/main/icons/ningji.png, enabled=true 39 | 40 | */ 41 | 42 | const $ = new Env('柠季'); 43 | $.is_debug = getEnv('is_debug') || 'false'; // 调试模式 44 | $.userInfo = getEnv('ningji_data') || ''; // Token 45 | $.userArr = $.toObj($.userInfo) || []; // 用户数组 46 | $.appid = 'wx177c513cc05c325d'; // 小程序 appId 47 | $.tenantid = '10159618' 48 | $.orgid = '429605' 49 | $.csecversionname = '67.44.000' 50 | $.csecversion = '1.4.0' 51 | $.messages = []; 52 | $.campaignId = "1008342767"; 53 | 54 | // 主函数 55 | async function main() { 56 | if ($.userArr.length) { 57 | $.log(`✅ 找到: ${$.userArr.length} 个账号`); 58 | for (let i = 0; i < $.userArr.length; i++) { 59 | $.log(`----- 账号 [${i + 1}] 开始执行 -----`); 60 | // 初始化 61 | $.is_login = true; 62 | $.token = $.userArr[i]['token']; 63 | $.cardId = $.userArr[i]['cardId']; 64 | //$.campaignId = $.userArr[i]['campaignId']; 65 | $.headers = { 66 | 'referer': `https://servicewechat.com/${$.appid}/169/page-frame.html`, 67 | 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.46(0x18002e2c) NetType/WIFI Language/zh_CN', 68 | 'content-type': 'application/json', 69 | 'x-token': $.token, 70 | 'orgid': $.orgid, 71 | 'poitype': '1', 72 | 'tenantid': $.tenantid, 73 | 'poiid': '0' 74 | } 75 | 76 | // 获取用户信息, 不打印日志 77 | await getUserInfo(); 78 | await $.wait(1000); 79 | // 无效 token 跳出 80 | if (!$.is_login) continue; 81 | 82 | // 签到 83 | await signin(); 84 | await $.wait(1000); 85 | // 获取用户信息, 打印日志 86 | await getUserInfo(true); 87 | } 88 | $.log(`----- 所有账号执行完成 -----`); 89 | } else { 90 | throw new Error('⛔️ 未找到账号'); 91 | } 92 | } 93 | 94 | // 签到 95 | async function signin() { 96 | let msg = ''; 97 | // 构造请求 98 | let opt = { 99 | url: `https://pos.meituan.com/api/v1/crm/frontend/campaign/sign-in/participate`, 100 | headers: $.headers, 101 | params: { 102 | yodaReady: 'wx', 103 | csecappid: $.appid, 104 | csecplatform: '3', 105 | csecversionname: $.csecversionname, 106 | csecversion: $.csecversion 107 | }, 108 | body: $.toStr({ 109 | campaignId: $.campaignId, 110 | cardId: $.cardId, 111 | couponDisplayScene: 44, 112 | styleVersion: 2 113 | }) 114 | }; 115 | 116 | // 发起请求 117 | var result = await Request(opt); 118 | if (result?.code == 0 && result?.issuedCouponDisplayInfos && result?.issuedPointAmount) { 119 | const { issuedPointAmount, issuedCouponDisplayInfos } = result; 120 | 121 | msg += `✅ 签到:获得${issuedPointAmount}积分 ${issuedCouponDisplayInfos[0]?.displayData?.name?.value || ''}\n`; 122 | } else if (result?.code == 90600 || result?.code == 500) { 123 | msg += `⛔️ 签到:${result?.msg}\n`; 124 | } else { 125 | msg += `⛔️ 签到信息失败\n`; 126 | $.log($.toStr(result)); 127 | } 128 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 129 | } 130 | 131 | 132 | // 获取用户信息 133 | async function getUserInfo(isShowMsg = false) { 134 | let msg = ''; 135 | // 构造请求 136 | let opt = { 137 | url: `https://rms.meituan.com/api/v1/rmsmina/c/comp/member/memberinfo`, 138 | headers: $.headers, 139 | params: { 140 | mtShopId: '', 141 | yodaReady: 'wx', 142 | csecappid: $.appid, 143 | csecplatform: '3', 144 | csecversionname: $.csecversionname, 145 | csecversion: $.csecversion 146 | }, 147 | body: $.toStr({ 148 | cardId: $.cardId, 149 | showNickName: "true", 150 | showAvatarUrl: "true", 151 | showMemberGrade: "true", 152 | showAssertList: "[1,2,3]", 153 | showCardTitle: "true", 154 | showProgressBar: "true", 155 | showMemberCodeUrl: "true" 156 | }) 157 | }; 158 | 159 | // 发起请求 160 | var result = await Request(opt); 161 | if (result?.code == 200 && result?.data) { 162 | const { nickName, assets } = result?.data; 163 | if(isShowMsg) { 164 | msg += `✅ 账号: ${nickName}`; 165 | for(let item of assets) { 166 | msg += `,${item.name}:${item.value}`; 167 | } 168 | } 169 | } else if (result?.code == 400) { 170 | $.is_login = false; // Token 失效 171 | msg += `⛔️ ${result?.message} \n`; 172 | } else { 173 | msg += `⛔️ 获取用户信息失败\n`; 174 | $.log($.toStr(result)); 175 | } 176 | if(isShowMsg) { 177 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 178 | } 179 | } 180 | 181 | 182 | // 脚本执行入口 183 | !(async () => { 184 | if (typeof $request !== `undefined`) { 185 | GetCookie(); 186 | } else { 187 | await main(); // 主函数 188 | } 189 | })() 190 | .catch((e) => $.messages.push(e.message || e) && $.logErr(e)) 191 | .finally(async () => { 192 | await sendMsg($.messages.join('\n').trimStart().trimEnd()); // 推送通知 193 | $.done(); 194 | }) 195 | 196 | 197 | 198 | // 获取用户数据 199 | function GetCookie() { 200 | try { 201 | let msg = ''; 202 | debug($response.body); 203 | const header = ObjectKeys2LowerCase($request.headers); 204 | const token = header['x-token']; 205 | const campaignId = header['campaignid']; 206 | const body = $.toObj($response.body); 207 | const { cardId } = body?.data?.userInfo?.cardInfo; 208 | if (token && cardId) { 209 | const user = $.userArr.find(user => user.cardId === cardId); 210 | if (user) { 211 | msg += `更新用户 [${cardId}] Token: ${token}\n`; 212 | user.token = token; 213 | user.campaignId = campaignId; 214 | } else { 215 | msg += `新增用户 [${cardId}] Token: ${token}\n`; 216 | $.userArr.push({ "cardId": cardId, "campaignId": campaignId, "token": token }); 217 | } 218 | // 写入数据持久化 219 | $.setdata($.toStr($.userArr), 'ningji_data'); 220 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 221 | } 222 | } catch (e) { 223 | $.log("⛔️ 获取Cookie失败"), $.log(e); 224 | } 225 | } 226 | 227 | 228 | // 获取环境变量 229 | function getEnv(...keys) { 230 | for (let key of keys) { 231 | var value = $.isNode() ? process.env[key] || process.env[key.toUpperCase()] || process.env[key.toLowerCase()] || $.getdata(key) : $.getdata(key); 232 | if (value) return value; 233 | } 234 | } 235 | 236 | 237 | 238 | /** 239 | * 请求函数二次封装 240 | * @param {(object|string)} options - 构造请求内容,可传入对象或 Url 241 | * @returns {(object|string)} - 根据 options['respType'] 传入的 {status|headers|rawBody} 返回对象或字符串,默认为 body 242 | */ 243 | async function Request(options) { 244 | try { 245 | options = options.url ? options : { url: options }; 246 | const _method = options?._method || ('body' in options ? 'post' : 'get'); 247 | const _respType = options?._respType || 'body'; 248 | const _timeout = options?._timeout || 15e3; 249 | const _http = [ 250 | new Promise((_, reject) => setTimeout(() => reject(`⛔️ 请求超时: ${options['url']}`), _timeout)), 251 | new Promise((resolve, reject) => { 252 | debug(options, '[Request]'); 253 | $[_method.toLowerCase()](options, (error, response, data) => { 254 | debug(response, '[response]'); 255 | error && $.log($.toStr(error)); 256 | if (_respType !== 'all') { 257 | resolve($.toObj(response?.[_respType], response?.[_respType])); 258 | } else { 259 | resolve(response); 260 | } 261 | }) 262 | }) 263 | ]; 264 | return await Promise.race(_http); 265 | } catch (err) { 266 | $.logErr(err); 267 | } 268 | } 269 | 270 | 271 | // 发送消息 272 | async function sendMsg(message) { 273 | if (!message) return; 274 | try { 275 | if ($.isNode()) { 276 | try { 277 | var notify = require('./sendNotify'); 278 | } catch (e) { 279 | var notify = require('./utils/sendNotify'); 280 | } 281 | await notify.sendNotify($.name, message); 282 | } else { 283 | $.msg($.name, '', message); 284 | } 285 | } catch (e) { 286 | $.log(`\n\n----- ${$.name} -----\n${message}`); 287 | } 288 | } 289 | 290 | 291 | /** 292 | * DEBUG 293 | * @param {*} content - 传入内容 294 | * @param {*} title - 标题 295 | */ 296 | function debug(content, title = "debug") { 297 | let start = `\n----- ${title} -----\n`; 298 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 299 | if ($.is_debug === 'true') { 300 | if (typeof content == "string") { 301 | $.log(start + content + end); 302 | } else if (typeof content == "object") { 303 | $.log(start + $.toStr(content) + end); 304 | } 305 | } 306 | } 307 | 308 | //转换为小写 309 | function ObjectKeys2LowerCase(obj) { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])) }; 310 | 311 | // prettier-ignore 312 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, r) => { s.call(this, t, (t, s, a) => { t ? r(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null, ...s) { try { return JSON.stringify(t, ...s) } catch { return e } } getjson(t, e) { let s = e; const r = this.getdata(t); if (r) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, r) => e(r)) }) } runScript(t, e) { return new Promise(s => { let r = this.getdata("@chavy_boxjs_userCfgs.httpapi"); r = r ? r.replace(/\n/g, "").trim() : r; let a = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); a = a ? 1 * a : 20, a = e && e.timeout ? e.timeout : a; const [i, o] = r.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: a }, headers: { "X-Key": i, Accept: "*/*" }, timeout: a }; this.post(n, (t, e, r) => s(r)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e); if (!s && !r) return {}; { const r = s ? t : e; try { return JSON.parse(this.fs.readFileSync(r)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e), a = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, a) : r ? this.fs.writeFileSync(e, a) : this.fs.writeFileSync(t, a) } } lodash_get(t, e, s) { const r = e.replace(/\[(\d+)\]/g, ".$1").split("."); let a = t; for (const t of r) if (a = Object(a)[t], void 0 === a) return s; return a } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, r) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[r + 1]) >> 0 == +e[r + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, r] = /^@(.*?)\.(.*?)$/.exec(t), a = s ? this.getval(s) : ""; if (a) try { const t = JSON.parse(a); e = t ? this.lodash_get(t, r, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, r, a] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(r), o = r ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, a, t), s = this.setval(JSON.stringify(e), r) } catch (e) { const i = {}; this.lodash_set(i, a, t), s = this.setval(JSON.stringify(i), r) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) }); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: r, statusCode: a, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: r, statusCode: a, headers: i, rawBody: o, body: n }, n) }, t => { const { message: r, response: a } = t; e(r, a, a && s.decode(a.rawBody, this.encoding)) }) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, (t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) }); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let r = require("iconv-lite"); this.initGotEnv(t); const { url: a, ...i } = t; this.got[s](a, i).then(t => { const { statusCode: s, statusCode: a, headers: i, rawBody: o } = t, n = r.decode(o, this.encoding); e(null, { status: s, statusCode: a, headers: i, rawBody: o, body: n }, n) }, t => { const { message: s, response: a } = t; e(s, a, a && r.decode(a.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let r = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in r) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? r[e] : ("00" + r[e]).substr(("" + r[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let r = t[s]; null != r && "" !== r && ("object" == typeof r && (r = JSON.stringify(r)), e += `${s}=${r}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", r = "", a) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } case "Loon": { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } case "Quantumult X": { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl, r = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": r } } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, r, i(a)); break; case "Quantumult X": $notify(e, s, r, i(a)); break; case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), r && t.push(r), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, e, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, e, void 0 !== t.message ? t.message : t, t.stack) } } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${s} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) } 313 | -------------------------------------------------------------------------------- /script/picc/picc_adblock.js: -------------------------------------------------------------------------------- 1 | const Body = JSON.parse($response.body); 2 | const url = $request.url; 3 | 4 | const actions = { 5 | 'homeInit': () => { 6 | Body.data.startupPage = {}; 7 | Body.data.templates = Body.data.templates.filter(e => !e.name.match(/主轮播图|保险推荐|专享|腰封轮播图|浮标配置|二楼营销位|首页主题/)); 8 | }, 9 | 'myPageConfigList': () => { 10 | Body.data.YFList = []; 11 | } 12 | }; 13 | 14 | const actionKey = Object.keys(actions).find(key => url.includes(key)); 15 | actions[actionKey]?.(); 16 | 17 | $done({ body: JSON.stringify(Body) }); -------------------------------------------------------------------------------- /script/wdzhsy/wdzhsy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 脚本名称:万达智慧商业 3 | * 活动规则:完成每日任务 4 | * 脚本说明:添加重写进入"万达智慧商业"小程序-"我的"界面,即可获取 Token,支持多账号,兼容 NE / Node.js 环境。 5 | * 环境变量:wdzhsy_token / CODESERVER_ADDRESS、CODESERVER_FUN 6 | * 更新时间:2024-11-05 09:12 7 | * 图标地址:https://raw.githubusercontent.com/leiyiyan/resource/main/icons/wdzhsy.png 8 | 9 | ------------------ Surge 配置 ------------------ 10 | 11 | [MITM] 12 | hostname = %APPEND% www.wandawic.com 13 | 14 | [Script] 15 | 万达智慧商业² = type=http-response,pattern=^https?:\/\/www\.wandawic\.com\/api\/wic\-member\-application\/api\/user\/queryUser,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js,script-update-interval=0 16 | 17 | 万达智慧商业 = type=cron,cronexp=30 9 * * *,timeout=60,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js,script-update-interval=0 18 | 19 | ------------------- Loon 配置 ------------------- 20 | 21 | [MITM] 22 | hostname = www.wandawic.com 23 | 24 | [Script] 25 | http-response ^https?:\/\/www\.wandawic\.com\/api\/wic\-member\-application\/api\/user\/queryUser tag=万达智慧商业²,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js,requires-body=1 26 | 27 | cron "30 9 * * *" script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js,tag=万达智慧商业,enable=true 28 | 29 | --------------- Quantumult X 配置 --------------- 30 | 31 | [MITM] 32 | hostname = www.wandawic.com 33 | [rewrite_local] 34 | ^https?:\/\/www\.wandawic\.com\/api\/wic\-member\-application\/api\/user\/queryUser url script-response-body https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js 35 | 36 | [task_local] 37 | 30 9 * * * https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js, tag=万达智慧商业, img-url=https://raw.githubusercontent.com/leiyiyan/resource/main/icons/wdzhsy.png, enabled=true 38 | 39 | ------------------ Stash 配置 ------------------ 40 | 41 | cron: 42 | script: 43 | - name: 万达智慧商业 44 | cron: '30 9 * * *' 45 | timeout: 10 46 | 47 | http: 48 | mitm: 49 | - "www.wandawic.com" 50 | script: 51 | - match: ^https?:\/\/www\.wandawic\.com\/api\/wic\-member\-application\/api\/user\/queryUser 52 | name: 万达智慧商业 53 | type: response 54 | require-body: true 55 | 56 | script-providers: 57 | 万达智慧商业: 58 | url: https://raw.githubusercontent.com/leiyiyan/resource/main/script/wdzhsy/wdzhsy.js 59 | interval: 86400 60 | 61 | */ 62 | 63 | const $ = new Env('万达智慧商业'); 64 | $.is_debug = getEnv('is_debug') || 'false'; // 调试模式 65 | $.userInfo = getEnv('wdzhsy_token') || ''; // Token 66 | $.userArr = $.toObj($.userInfo) || []; // 用户数组 67 | $.appid = 'wx8e4763f7ed741710'; // 小程序 appId 68 | $.messages = []; 69 | 70 | 71 | // 主函数 72 | async function main() { 73 | // 获取微信 Code 74 | // await getWxCode(); 75 | // for (let i = 0; i < $.codeList.length; i++) { 76 | // await getToken($.codeList[i]); // 获取 Token 77 | // } 78 | 79 | if ($.userArr.length) { 80 | $.log(`✅ 找到:${$.userArr.length}个Token变量`); 81 | for (let i = 0; i < $.userArr.length; i++) { 82 | $.log(`----- 账号 [${i + 1}] 开始执行 -----`); 83 | // 初始化 84 | $.is_login = true; 85 | $.token = $.userArr[i]['token']; 86 | $.headers = { 87 | 'Referer': 'https://servicewechat.com/wx8e4763f7ed741710/112/page-frame.html', 88 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x1800302b) NetType/4G Language/zh_CN', 89 | 'content-type': 'application/json', 90 | 'wic-member-token': $.token 91 | } 92 | 93 | // 获取用户信息, 不打印日志 94 | await getUserInfo(); 95 | await $.wait(2000); 96 | // 无效 token 跳出 97 | if (!$.is_login) continue; 98 | 99 | // 获取任务列表 100 | await getTask(); 101 | await $.wait(2000); 102 | // 获取用户信息, 打印日志 103 | await getUserInfo(true); 104 | } 105 | $.log(`----- 所有账号执行完成 -----`); 106 | } else { 107 | throw new Error('⛔️ 未找到Token变量'); 108 | } 109 | } 110 | 111 | // 获取 Token 112 | async function getToken(code) { 113 | // 构造请求 114 | const options = { 115 | url: `https://www.wandawic.com/api/foreground/loginregister/loginByCode`, 116 | headers: { 117 | 'content-type': 'application/json', 118 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.48(0x1800302b) NetType/4G Language/zh_CN', 119 | 'Referer': 'https://servicewechat.com/wx8e4763f7ed741710/112/page-frame.html', 120 | 'wic-member-token': '' 121 | }, 122 | body: $.toStr({ 123 | time: getDateTime(), 124 | channelCode: "ch_xcx", 125 | data: { 126 | code, 127 | tryAutoLogin: false, 128 | flag: "Y" 129 | } 130 | }) 131 | } 132 | 133 | // 发起请求 134 | const result = await Request(options); 135 | if (result?.code == '200' && result?.data) { 136 | const { token } = result?.data; 137 | const { userId } = result?.data?.userInfo; 138 | // 把新的 Token 添加到 $.userArr 139 | token && userId && $.userArr.push({ "userId": userId, "token": token }); 140 | $.log(`✅ 获取:1个Token变量 `); 141 | } else { 142 | $.log(`⛔️ 获取Token失败: ${$.toStr(result)}`); 143 | } 144 | } 145 | 146 | 147 | // 获取任务列表 148 | async function getTask() { 149 | let msg = '' 150 | // 构造请求 151 | const options = { 152 | url: `https://www.wandawic.com/wact-web/wd/yypt/task/ebTaskList`, 153 | headers: $.headers, 154 | body: $.toStr({ 155 | time: getDateTime2(), 156 | data: { 157 | taskType: "daily" 158 | }, 159 | channelCode: "ch_xcx", 160 | token: $.token 161 | }) 162 | } 163 | 164 | // 发起请求 165 | const result = await Request(options); 166 | if (result?.code == '200' && result?.data) { 167 | let task_list = result?.data?.productList; 168 | for (let i = 0; i < task_list.length; i++) { 169 | // 任务名称、奖励、是否完成、任务进度 170 | const { taskName, prizePrice, isFinish, taskPeriodTimes } = task_list[i]; 171 | // 当前任务进度 172 | let currentCount = 0; 173 | // 任务总进度 174 | let totalCount = 0; 175 | const taskPeriodTime = JSON.parse(taskPeriodTimes) 176 | if(taskPeriodTime?.d) { 177 | currentCount = taskPeriodTime?.d.split('/')[0]; 178 | totalCount = taskPeriodTime?.d.split('/')[1]; 179 | } else if(taskPeriodTime?.m) { 180 | currentCount = taskPeriodTime?.m.split('/')[0]; 181 | totalCount = taskPeriodTime?.m.split('/')[1]; 182 | } 183 | switch (isFinish) { 184 | case 'y': // 任务已完成 185 | msg += `✅ 任务:${taskName},已完成\n`; 186 | break; 187 | case 'n': // 任务未完成 188 | for(let j = 0; j < totalCount - currentCount; j++) { 189 | // 随机获取一个商铺 ID 190 | const {total} = await getShops() 191 | const {shopId} = await getShops(total); 192 | switch (taskName) { 193 | case '浏览看铺': 194 | await doTask(taskName, shopId, j + 1, totalCount, prizePrice, "SCEN_SACN", 'scanshop'); 195 | break; 196 | case '收藏意向铺位': 197 | // 收藏意向铺位 198 | await storeUp(taskName, shopId, j + 1, totalCount, prizePrice); 199 | await $.wait(2000); 200 | // 取消收藏意向铺位 201 | await cancelStoreUp(taskName, shopId); 202 | break; 203 | case '转发分享': 204 | await doTask(taskName, shopId, j + 1, totalCount, prizePrice, "SCEN_SHARE", 'shareshop'); 205 | break; 206 | case '预约、咨询': 207 | await doTask(taskName, shopId, j + 1, totalCount, prizePrice, "SCEN_CONSULT", 'phoneshop'); 208 | break; 209 | } 210 | await $.wait(2000); 211 | } 212 | await $.wait(2000); 213 | } 214 | } 215 | } else { 216 | msg += `⛔️ 获取任务列表失败\n`; 217 | $.log($.toStr(result)); 218 | } 219 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 220 | } 221 | 222 | 223 | // 完成任务 224 | async function doTask(taskName, shopId, currentCount, totalCount, prizePrice, taskTemplateId, actionType) { 225 | let msg = ''; 226 | // 构造请求 227 | let opt = { 228 | url: `https://www.wandawic.com/wact-web/wd/yypt/task/doTask`, 229 | headers: $.headers, 230 | body: $.toStr({ 231 | time: getDateTime(), 232 | traceId: getUUID(32), 233 | channelCode: "ch_xcx", 234 | data: { 235 | taskTemplateId, 236 | actionType, 237 | targetId: shopId 238 | }, 239 | token: $.token 240 | }) 241 | }; 242 | 243 | // 发起请求 244 | var result = await Request(opt); 245 | if (result?.code == '200') { 246 | msg += `✅ 任务:${taskName},进度: ${currentCount}/${totalCount},获得${prizePrice}万商分\n`; 247 | } else { 248 | $.log(`⛔️ 完成任务${taskName}失败: ${result?.message}`); 249 | } 250 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 251 | } 252 | // 收藏意向铺位 253 | async function storeUp(taskName, shopId, currentCount, totalCount, prizePrice) { 254 | let msg = ''; 255 | // 构造请求 256 | let opt = { 257 | url: `https://www.wandawic.com/api/foreground/store/storeUp`, 258 | headers: $.headers, 259 | body: $.toStr({ 260 | time: getDateTime(), 261 | traceId: getUUID(32), 262 | channelCode: "ch_xcx", 263 | data: { 264 | collectType: "2", 265 | collectObject: shopId 266 | }, 267 | token: $.token 268 | }) 269 | }; 270 | 271 | // 发起请求 272 | var result = await Request(opt); 273 | if (result?.code == '200') { 274 | msg += `✅ 任务:${taskName},进度: ${currentCount}/${totalCount},获得${prizePrice}万商分\n`; 275 | } else { 276 | $.log(`⛔️ 完成任务${taskName}失败: ${result?.message}`); 277 | } 278 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 279 | } 280 | // 取消收藏意向铺位 281 | async function cancelStoreUp(taskName, shopId) { 282 | let msg = ''; 283 | // 构造请求 284 | let opt = { 285 | url: `https://www.wandawic.com/api/foreground/store/cancelStoreUp`, 286 | headers: $.headers, 287 | body: $.toStr({ 288 | time: getDateTime(), 289 | traceId: getUUID(32), 290 | channelCode: "ch_xcx", 291 | data: { 292 | collectType: "2", 293 | collectObject: shopId 294 | }, 295 | token: $.token 296 | }) 297 | }; 298 | 299 | // 发起请求 300 | var result = await Request(opt); 301 | if (result?.code == '200') { 302 | msg += `✅ 任务:${taskName},取消店铺收藏\n`; 303 | } else { 304 | $.log(`⛔️ 完成任务${taskName}失败: ${result?.message}`); 305 | } 306 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 307 | } 308 | 309 | 310 | // 获取用户信息 311 | async function getUserInfo(isShowMsg = false) { 312 | let msg = ''; 313 | // 构造请求 314 | let opt = { 315 | url: `https://www.wandawic.com/api/foreground/loginregister/queryUser`, 316 | headers: $.headers, 317 | body: $.toStr({ 318 | time: getDateTime(), 319 | channelCode: "ch_xcx", 320 | token: $.token 321 | }) 322 | }; 323 | 324 | // 发起请求 325 | var result = await Request(opt); 326 | if (result?.code == '200' && result?.data) { 327 | const { desePhone, balance } = result?.data; 328 | if(isShowMsg) { 329 | msg += `✅ 账号:${desePhone},帐户共计:${balance}万商分\n`; 330 | } 331 | } else if (result?.code == '401') { 332 | $.is_login = false; // Token 失效 333 | msg += `⛔️ ${result?.message} \n`; 334 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 335 | } else { 336 | $.log(`查询用户信息失败 `); 337 | } 338 | if(isShowMsg) { 339 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 340 | } 341 | } 342 | // 随机获取一个商铺 ID 343 | async function getShops(size=10) { 344 | let msg = ''; 345 | // 构造请求 346 | let opt = { 347 | url: `https://www.wandawic.com/api/foreground/shops/queryShops`, 348 | headers: $.headers, 349 | body: $.toStr({ 350 | time: getDateTime(), 351 | traceId: getUUID(32), 352 | channelCode: "ch_xcx", 353 | data: { 354 | current: 1, 355 | size, 356 | thisUnitType: "1" 357 | }, 358 | token: $.token 359 | }) 360 | }; 361 | 362 | // 发起请求 363 | var result = await Request(opt); 364 | if (result?.code == '200' && result?.data) { 365 | const { total, records } = result?.data; 366 | const random = Math.floor(Math.random() * size) 367 | const shopId = records[random].id; 368 | if(size > 10) { 369 | msg += `✅ 从${total}个商铺中随机获取一个商铺ID:${shopId}\n`; 370 | } 371 | return {total,shopId}; 372 | } else { 373 | $.log(`查询商户列表失败 `); 374 | } 375 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 376 | } 377 | 378 | // 脚本执行入口 379 | !(async () => { 380 | if (typeof $request !== `undefined`) { 381 | GetCookie(); 382 | } else { 383 | await main(); // 主函数 384 | } 385 | })() 386 | .catch((e) => $.messages.push(e.message || e) && $.logErr(e)) 387 | .finally(async () => { 388 | await sendMsg($.messages.join('\n').trimStart().trimEnd()); // 推送通知 389 | $.done(); 390 | }) 391 | 392 | 393 | 394 | // 获取用户数据 395 | function GetCookie() { 396 | try { 397 | let msg = ''; 398 | debug($response.body); 399 | const header = ObjectKeys2LowerCase($request.headers); 400 | const token = header['wic-member-token']; 401 | const body = $.toObj($response.body); 402 | const { userId } = body['data']; 403 | if (token && userId) { 404 | // 使用 find() 方法找到与 member_id 匹配的对象,以新增/更新用户 token 405 | const user = $.userArr.find(user => user.userId === userId); 406 | if (user) { 407 | if (user.token == token) return; 408 | msg += `更新用户 [${userId}] Token: ${token}\n`; 409 | user.token = token; 410 | } else { 411 | msg += `新增用户 [${userId}] Token: ${token}\n`; 412 | $.userArr.push({ "userId": userId, "token": token }); 413 | } 414 | // 写入数据持久化 415 | $.setdata($.toStr($.userArr), 'wdzhsy_token'); 416 | $.messages.push(msg.trimEnd()), $.log(msg.trimEnd()); 417 | } 418 | } catch (e) { 419 | $.log("⛔️ 签到数据获取失败"), $.log(e); 420 | } 421 | } 422 | 423 | 424 | // 获取环境变量 425 | function getEnv(...keys) { 426 | for (let key of keys) { 427 | var value = $.isNode() ? process.env[key] || process.env[key.toUpperCase()] || process.env[key.toLowerCase()] || $.getdata(key) : $.getdata(key); 428 | if (value) return value; 429 | } 430 | } 431 | 432 | 433 | // 获取微信 Code 434 | async function getWxCode() { 435 | try { 436 | $.codeList = []; 437 | $.codeServer = getEnv("CODESERVER_ADDRESS", "@codeServer.address"); 438 | $.codeFuc = getEnv("CODESERVER_FUN", "@codeServer.fun"); 439 | if (!$.codeServer) return $.log(`⛔️ 提示:未配置微信CodeServer\n`); 440 | 441 | $.codeList = ($.codeFuc 442 | ? (eval($.codeFuc), await WxCode($.appid)) 443 | : (await Request(`${$.codeServer}/?wxappid=${$.appid}`))?.split("|")) 444 | .filter(item => item.length === 32); 445 | $.log(`✅ 获取:${$.codeList.length}个微信Code`); 446 | } catch (e) { 447 | $.logErr(`⛔️ 获取微信Code失败!`); 448 | } 449 | } 450 | 451 | 452 | /** 453 | * 请求函数二次封装 454 | * @param {(object|string)} options - 构造请求内容,可传入对象或 Url 455 | * @returns {(object|string)} - 根据 options['respType'] 传入的 {status|headers|rawBody} 返回对象或字符串,默认为 body 456 | */ 457 | async function Request(options) { 458 | try { 459 | options = options.url ? options : { url: options }; 460 | const _method = options?._method || ('body' in options ? 'post' : 'get'); 461 | const _respType = options?._respType || 'body'; 462 | const _timeout = options?._timeout || 15e3; 463 | const _http = [ 464 | new Promise((_, reject) => setTimeout(() => reject(`⛔️ 请求超时: ${options['url']}`), _timeout)), 465 | new Promise((resolve, reject) => { 466 | debug(options, '[Request]'); 467 | $[_method.toLowerCase()](options, (error, response, data) => { 468 | debug(response, '[response]'); 469 | error && $.log($.toStr(error)); 470 | if (_respType !== 'all') { 471 | resolve($.toObj(response?.[_respType], response?.[_respType])); 472 | } else { 473 | resolve(response); 474 | } 475 | }) 476 | }) 477 | ]; 478 | return await Promise.race(_http); 479 | } catch (err) { 480 | $.logErr(err); 481 | } 482 | } 483 | 484 | 485 | // 发送消息 486 | async function sendMsg(message) { 487 | if (!message) return; 488 | try { 489 | if ($.isNode()) { 490 | try { 491 | var notify = require('./sendNotify'); 492 | } catch (e) { 493 | var notify = require('./utils/sendNotify'); 494 | } 495 | await notify.sendNotify($.name, message); 496 | } else { 497 | $.msg($.name, '', message); 498 | } 499 | } catch (e) { 500 | $.log(`\n\n----- ${$.name} -----\n${message}`); 501 | } 502 | } 503 | 504 | 505 | /** 506 | * DEBUG 507 | * @param {*} content - 传入内容 508 | * @param {*} title - 标题 509 | */ 510 | function debug(content, title = "debug") { 511 | let start = `\n----- ${title} -----\n`; 512 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 513 | if ($.is_debug === 'true') { 514 | if (typeof content == "string") { 515 | $.log(start + content + end); 516 | } else if (typeof content == "object") { 517 | $.log(start + $.toStr(content) + end); 518 | } 519 | } 520 | } 521 | function getUUID(size) { 522 | let result = [] 523 | let hexRef = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] 524 | 525 | for (let n = 0; n < size; n++) { 526 | result.push(hexRef[Math.floor(Math.random() * 16)]) 527 | } 528 | return result.join('') 529 | } 530 | // 获取当前日期时间 531 | function getDateTime() { 532 | const date = new Date(); 533 | // 获取年、月、日、时、分、秒 534 | const year = date.getFullYear(); 535 | const month = String(date.getMonth() + 1).padStart(2, '0'); 536 | const day = String(date.getDate()).padStart(2, '0'); 537 | const hours = String(date.getHours()).padStart(2, '0'); 538 | const minutes = String(date.getMinutes()).padStart(2, '0'); 539 | const seconds = String(date.getSeconds()).padStart(2, '0'); 540 | const milliseconds = String(date.getMilliseconds()).padStart(3, '0'); 541 | // 拼接字符串 542 | const formattedDate = `${year}${month}${day}${hours}${minutes}${seconds}${milliseconds}S`; 543 | return formattedDate; 544 | } 545 | // 获取当前日期时间 546 | function getDateTime2() { 547 | const date = new Date(); 548 | // 获取年、月、日、时、分、秒 549 | const year = date.getFullYear(); 550 | const month = String(date.getMonth() + 1); 551 | const day = String(date.getDate()); 552 | const hours = String(date.getHours()); 553 | const minutes = String(date.getMinutes()); 554 | const seconds = String(date.getSeconds()); 555 | 556 | // 拼接字符串 557 | const formattedDate = `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`; 558 | 559 | return formattedDate; 560 | } 561 | 562 | //转换为小写 563 | function ObjectKeys2LowerCase(obj) { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])) }; 564 | 565 | // prettier-ignore 566 | function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, r) => { s.call(this, t, (t, s, a) => { t ? r(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.encoding = "utf-8", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `🔔${this.name}, 开始!`) } getEnv() { return "undefined" != typeof $environment && $environment["surge-version"] ? "Surge" : "undefined" != typeof $environment && $environment["stash-version"] ? "Stash" : "undefined" != typeof module && module.exports ? "Node.js" : "undefined" != typeof $task ? "Quantumult X" : "undefined" != typeof $loon ? "Loon" : "undefined" != typeof $rocket ? "Shadowrocket" : void 0 } isNode() { return "Node.js" === this.getEnv() } isQuanX() { return "Quantumult X" === this.getEnv() } isSurge() { return "Surge" === this.getEnv() } isLoon() { return "Loon" === this.getEnv() } isShadowrocket() { return "Shadowrocket" === this.getEnv() } isStash() { return "Stash" === this.getEnv() } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null, ...s) { try { return JSON.stringify(t, ...s) } catch { return e } } getjson(t, e) { let s = e; const r = this.getdata(t); if (r) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, r) => e(r)) }) } runScript(t, e) { return new Promise(s => { let r = this.getdata("@chavy_boxjs_userCfgs.httpapi"); r = r ? r.replace(/\n/g, "").trim() : r; let a = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); a = a ? 1 * a : 20, a = e && e.timeout ? e.timeout : a; const [i, o] = r.split("@"), n = { url: `http://${o}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: a }, headers: { "X-Key": i, Accept: "*/*" }, timeout: a }; this.post(n, (t, e, r) => s(r)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e); if (!s && !r) return {}; { const r = s ? t : e; try { return JSON.parse(this.fs.readFileSync(r)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), r = !s && this.fs.existsSync(e), a = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, a) : r ? this.fs.writeFileSync(e, a) : this.fs.writeFileSync(t, a) } } lodash_get(t, e, s) { const r = e.replace(/\[(\d+)\]/g, ".$1").split("."); let a = t; for (const t of r) if (a = Object(a)[t], void 0 === a) return s; return a } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, r) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[r + 1]) >> 0 == +e[r + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, r] = /^@(.*?)\.(.*?)$/.exec(t), a = s ? this.getval(s) : ""; if (a) try { const t = JSON.parse(a); e = t ? this.lodash_get(t, r, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, r, a] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(r), o = r ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(o); this.lodash_set(e, a, t), s = this.setval(JSON.stringify(e), r) } catch (e) { const i = {}; this.lodash_set(i, a, t), s = this.setval(JSON.stringify(i), r) } } else s = this.setval(t, e); return s } getval(t) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.read(t); case "Quantumult X": return $prefs.valueForKey(t); case "Node.js": return this.data = this.loaddata(), this.data[t]; default: return this.data && this.data[t] || null } } setval(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": return $persistentStore.write(t, e); case "Quantumult X": return $prefs.setValueForKey(t, e); case "Node.js": return this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0; default: return this.data && this.data[e] || null } } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { switch (t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"], delete t.headers["content-type"], delete t.headers["content-length"]), t.params && (t.url += "?" + this.queryStr(t.params)), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) }); break; case "Quantumult X": this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let s = require("iconv-lite"); this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); s && this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: r, statusCode: a, headers: i, rawBody: o } = t, n = s.decode(o, this.encoding); e(null, { status: r, statusCode: a, headers: i, rawBody: o, body: n }, n) }, t => { const { message: r, response: a } = t; e(r, a, a && s.decode(a.rawBody, this.encoding)) }) } } post(t, e = (() => { })) { const s = t.method ? t.method.toLocaleLowerCase() : "post"; switch (t.body && t.headers && !t.headers["Content-Type"] && !t.headers["content-type"] && (t.headers["content-type"] = "application/x-www-form-urlencoded"), t.headers && (delete t.headers["Content-Length"], delete t.headers["content-length"]), void 0 === t.followRedirect || t.followRedirect || ((this.isSurge() || this.isLoon()) && (t["auto-redirect"] = !1), this.isQuanX() && (t.opts ? t.opts.redirection = !1 : t.opts = { redirection: !1 })), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient[s](t, (t, s, r) => { !t && s && (s.body = r, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, r) }); break; case "Quantumult X": t.method = s, this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: r, headers: a, body: i, bodyBytes: o } = t; e(null, { status: s, statusCode: r, headers: a, body: i, bodyBytes: o }, i, o) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let r = require("iconv-lite"); this.initGotEnv(t); const { url: a, ...i } = t; this.got[s](a, i).then(t => { const { statusCode: s, statusCode: a, headers: i, rawBody: o } = t, n = r.decode(o, this.encoding); e(null, { status: s, statusCode: a, headers: i, rawBody: o, body: n }, n) }, t => { const { message: s, response: a } = t; e(s, a, a && r.decode(a.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let r = { "M+": s.getMonth() + 1, "d+": s.getDate(), "H+": s.getHours(), "m+": s.getMinutes(), "s+": s.getSeconds(), "q+": Math.floor((s.getMonth() + 3) / 3), S: s.getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, (s.getFullYear() + "").substr(4 - RegExp.$1.length))); for (let e in r) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? r[e] : ("00" + r[e]).substr(("" + r[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let r = t[s]; null != r && "" !== r && ("object" == typeof r && (r = JSON.stringify(r)), e += `${s}=${r}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", r = "", a) { const i = t => { switch (typeof t) { case void 0: return t; case "string": switch (this.getEnv()) { case "Surge": case "Stash": default: return { url: t }; case "Loon": case "Shadowrocket": return t; case "Quantumult X": return { "open-url": t }; case "Node.js": return }case "object": switch (this.getEnv()) { case "Surge": case "Stash": case "Shadowrocket": default: { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } case "Loon": { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } case "Quantumult X": { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl, r = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": r } } case "Node.js": return }default: return } }; if (!this.isMute) switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": default: $notification.post(e, s, r, i(a)); break; case "Quantumult X": $notify(e, s, r, i(a)); break; case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), r && t.push(r), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { switch (this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: this.log("", `❗️${this.name}, 错误!`, e, t); break; case "Node.js": this.log("", `❗️${this.name}, 错误!`, e, void 0 !== t.message ? t.message : t, t.stack) } } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; switch (this.log("", `🔔${this.name}, 结束! 🕛 ${s} 秒`), this.log(), this.getEnv()) { case "Surge": case "Loon": case "Stash": case "Shadowrocket": case "Quantumult X": default: $done(t); break; case "Node.js": process.exit(1) } } }(t, e) } 567 | 568 | -------------------------------------------------------------------------------- /subscribe/leiyiyan.boxjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Leiyiyan.sub", 3 | "name": "Leiyiyan脚本订阅", 4 | "author": "@leiyiyan", 5 | "icon": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/pyy.png", 6 | "repo": "", 7 | "apps": [ 8 | { 9 | "id": "Code Server", 10 | "name": "Code Server", 11 | "author": "仅作学习交流", 12 | "repo": "", 13 | "descs_html": [ 14 | "
本接口及源码仅限学习研究,严禁将接口使用于电信诈骗、网络赌博、恶意营销、非法传销 等违法活动,引起任何法律责任概不负责。
" 15 | ], 16 | "icons": [ 17 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/pyy.png", 18 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/pyy.png" 19 | ], 20 | "keys": [ 21 | "codeServer" 22 | ], 23 | "settings": [ 24 | { 25 | "id": "@codeServer.open", 26 | "name": "开启code模式", 27 | "val": false, 28 | "type": "boolean", 29 | "desc": "默认关闭" 30 | },{ 31 | "id": "@codeServer.address", 32 | "name": "服务器地址", 33 | "val": "", 34 | "type": "text", 35 | "desc": "填入服务器地址,如http://127.0.0.1:99" 36 | },{ 37 | "id": "@codeServer.wxId", 38 | "name": "微信wxId", 39 | "val": "", 40 | "type": "textarea", 41 | "autoGrow": true, 42 | "rows": 1, 43 | "desc": "微信wxId,多账号用,分隔,非必需参数,仅当服务器有需求时填入" 44 | } 45 | ] 46 | }, 47 | { 48 | "id": "Leiyiyan_Netease", 49 | "name": "网易云音乐签到", 50 | "author": "@leiyiyan", 51 | "repo": "", 52 | "icons": [ 53 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/netease_music.png", 54 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/netease_music.png" 55 | ], 56 | "keys": [ 57 | "Netease_Musician_Cookie", 58 | "Netease_Musician_UserAgent", 59 | "Netease_Musician_FansId", 60 | "Netease_Musician_Enable_Cloud_Shell_Task", 61 | "Netease_Musician_Enable_Musician_Task", 62 | "Netease_Musician_Enable_Vip_Task" 63 | ], 64 | "settings": [ 65 | { 66 | "id": "Netease_Musician_Enable_Cloud_Shell_Task", 67 | "name": "开启云贝任务", 68 | "val": true, 69 | "type": "boolean", 70 | "desc": "默认开启" 71 | },{ 72 | "id": "Netease_Musician_Enable_Musician_Task", 73 | "name": "开启音乐人任务", 74 | "val": true, 75 | "type": "boolean", 76 | "desc": "默认开启" 77 | },{ 78 | "id": "Netease_Musician_Enable_Vip_Task", 79 | "name": "开启会员任务", 80 | "val": true, 81 | "type": "boolean", 82 | "desc": "默认开启" 83 | },{ 84 | "id": "Netease_Musician_Cookie", 85 | "name": "网易云音乐Cookie数据", 86 | "val": null, 87 | "type": "textarea", 88 | "autoGrow": true, 89 | "desc": "网易云音乐Cookie数据" 90 | },{ 91 | "id": "Netease_Musician_UserAgent", 92 | "name": "网易云音乐User-Agent数据", 93 | "val": null, 94 | "type": "textarea", 95 | "autoGrow": true, 96 | "desc": "网易云音乐User-Agent数据" 97 | },{ 98 | "id": "Netease_Musician_FansId", 99 | "name": "网易云音乐粉丝ID数据", 100 | "val": null, 101 | "type": "textarea", 102 | "autoGrow": false, 103 | "rows": 1, 104 | "desc": "网易云音乐粉丝ID数据" 105 | } 106 | ] 107 | }, 108 | { 109 | "id": "Leiyiyan_Aliyun_Web", 110 | "name": "阿里云社区", 111 | "author": "@leiyiyan", 112 | "repo": "", 113 | "icons": [ 114 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/aliyun_web.png", 115 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/aliyun_web.png" 116 | ], 117 | "keys": [ 118 | "is_debug", 119 | "aliyunWeb_data", 120 | "aliyunWeb_time" 121 | ], 122 | "settings": [ 123 | { 124 | "id": "is_debug", 125 | "name": "开启调试模式", 126 | "val": false, 127 | "type": "boolean", 128 | "desc": "默认关闭" 129 | }, 130 | { 131 | "id": "aliyunWeb_data", 132 | "name": "阿里云社区Cookie数据", 133 | "val": null, 134 | "type": "textarea", 135 | "autoGrow": true, 136 | "desc": "阿里云社区Cookie数据" 137 | }, 138 | { 139 | "id": "aliyunWeb_time", 140 | "name": "阿里云社区控制时间", 141 | "val": "12", 142 | "type": "input", 143 | "desc": "早于该时间则执行任务,晚于该时间则领取积分,默认: 12(即中午12点,为避免出现乱七八糟的问题,请设置: 1-23的整数)" 144 | }, 145 | { 146 | "id": "aliyunWeb_scene", 147 | "name": "开启场景任务", 148 | "val": false, 149 | "type": "boolean", 150 | "desc": "控制是否开启场景任务,默认:关闭(false)" 151 | }, 152 | { 153 | "id": "aliyunWeb_stock", 154 | "name": "开启库存查询", 155 | "val": false, 156 | "type": "boolean", 157 | "desc": "控制是否开启库存查询,默认:关闭(false)" 158 | }, 159 | { 160 | "id": "aliyunWeb_video", 161 | "name": "开启视频任务", 162 | "val": false, 163 | "type": "boolean", 164 | "desc": "控制是否开启视频任务,默认:关闭(false)" 165 | } 166 | ] 167 | }, 168 | { 169 | "id": "Leiyiyan_Aliyun_Web_CK", 170 | "name": "阿里云社区同步青龙", 171 | "author": "@leiyiyan", 172 | "repo": "", 173 | "icons": [ 174 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/aliyun_web.png", 175 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/aliyun_web.png" 176 | ], 177 | "keys": [ 178 | "aliyunWeb_QL" 179 | ], 180 | "settings": [ 181 | { 182 | "id": "is_debug", 183 | "name": "开启调试模式", 184 | "val": false, 185 | "type": "boolean", 186 | "desc": "默认关闭" 187 | }, 188 | { 189 | "id": "@aliyunWeb_QL.host", 190 | "name": "青龙服务器地址", 191 | "val": "", 192 | "type": "textarea", 193 | "autoGrow": true, 194 | "rows": 1, 195 | "desc": "填入服务器地址,不包括/,如http://127.0.0.1:5700" 196 | },{ 197 | "id": "@aliyunWeb_QL.clientId", 198 | "name": "clientId", 199 | "val": "", 200 | "type": "textarea", 201 | "autoGrow": true, 202 | "rows": 1, 203 | "desc": "填入青龙应用的clientId" 204 | }, 205 | { 206 | "id": "@aliyunWeb_QL.secret", 207 | "name": "secret", 208 | "val": "", 209 | "type": "textarea", 210 | "autoGrow": true, 211 | "rows": 1, 212 | "desc": "填入青龙应用的secret" 213 | }, 214 | { 215 | "id": "@aliyunWeb_QL.envName", 216 | "name": "变量名称", 217 | "val": "", 218 | "type": "textarea", 219 | "autoGrow": true, 220 | "rows": 1, 221 | "desc": "填入要同步到青龙的变量名称,默认:aliyunWeb_data" 222 | }, 223 | { 224 | "id": "@aliyunWeb_QL.taskName", 225 | "name": "任务名称", 226 | "val": "", 227 | "type": "textarea", 228 | "autoGrow": true, 229 | "rows": 1, 230 | "desc": "填入要运行的青龙的任务名称,默认:阿里云社区" 231 | }, 232 | { 233 | "id": "@aliyunWeb_QL.autoRunTask", 234 | "name": "开启自动运行任务", 235 | "val": false, 236 | "type": "boolean", 237 | "desc": "默认关闭" 238 | } 239 | ] 240 | }, 241 | { 242 | "id": "Leiyiyan_CBD_CK", 243 | "name": "茶百道同步青龙", 244 | "author": "@leiyiyan", 245 | "repo": "", 246 | "icons": [ 247 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cbd.png", 248 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cbd.png" 249 | ], 250 | "keys": [ 251 | "CBD_QL" 252 | ], 253 | "settings": [ 254 | { 255 | "id": "@CBD_QL.host", 256 | "name": "青龙服务器地址", 257 | "val": "", 258 | "type": "textarea", 259 | "autoGrow": true, 260 | "rows": 1, 261 | "desc": "填入服务器地址,不包括/,如http://127.0.0.1:5700" 262 | },{ 263 | "id": "@CBD_QL.clientId", 264 | "name": "clientId", 265 | "val": "", 266 | "type": "textarea", 267 | "autoGrow": true, 268 | "rows": 1, 269 | "desc": "填入青龙应用的clientId" 270 | }, 271 | { 272 | "id": "@CBD_QL.secret", 273 | "name": "secret", 274 | "val": "", 275 | "type": "textarea", 276 | "autoGrow": true, 277 | "rows": 1, 278 | "desc": "填入青龙应用的secret" 279 | }, 280 | { 281 | "id": "@CBD_QL.envName", 282 | "name": "变量名称", 283 | "val": "", 284 | "type": "textarea", 285 | "autoGrow": true, 286 | "rows": 1, 287 | "desc": "填入要同步到青龙的变量名称" 288 | }, 289 | { 290 | "id": "@CBD_QL.username", 291 | "name": "用户名", 292 | "val": "", 293 | "type": "textarea", 294 | "autoGrow": true, 295 | "rows": 1, 296 | "desc": "填入要同步到青龙的用户名(自定义)" 297 | } 298 | ] 299 | }, 300 | { 301 | "id": "Leiyiyan_Cfmoto", 302 | "name": "春风摩托", 303 | "author": "@leiyiyan", 304 | "repo": "", 305 | "icons": [ 306 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cfmoto.png", 307 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cfmoto.png" 308 | ], 309 | "keys": [ 310 | "is_debug", 311 | "cfmoto_data" 312 | ], 313 | "settings": [ 314 | { 315 | "id": "is_debug", 316 | "name": "开启调试模式", 317 | "val": false, 318 | "type": "boolean", 319 | "desc": "默认关闭" 320 | },{ 321 | "id": "cfmoto_data", 322 | "name": "春风摩托Cookie数据", 323 | "val": null, 324 | "type": "textarea", 325 | "autoGrow": true, 326 | "desc": "春风摩托Cookie数据" 327 | } 328 | ] 329 | }, 330 | { 331 | "id": "Leiyiyan_Zeeho", 332 | "name": "极核-ZEEHO", 333 | "author": "@leiyiyan", 334 | "repo": "", 335 | "icons": [ 336 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/zeeho.png", 337 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/zeeho.png" 338 | ], 339 | "keys": [ 340 | "is_debug", 341 | "zeeho_data" 342 | ], 343 | "settings": [ 344 | { 345 | "id": "is_debug", 346 | "name": "开启调试模式", 347 | "val": false, 348 | "type": "boolean", 349 | "desc": "默认关闭" 350 | },{ 351 | "id": "zeeho_data", 352 | "name": "极核Cookie数据", 353 | "val": null, 354 | "type": "textarea", 355 | "autoGrow": true, 356 | "desc": "极核Cookie数据" 357 | } 358 | ] 359 | }, 360 | { 361 | "id": "Leiyiyan_Xingma", 362 | "name": "星妈优选小程序", 363 | "author": "@leiyiyan", 364 | "repo": "", 365 | "icons": [ 366 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/xmyx.png", 367 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/xmyx.png" 368 | ], 369 | "keys": [ 370 | "is_debug", 371 | "xmyx_data" 372 | ], 373 | "settings": [ 374 | { 375 | "id": "is_debug", 376 | "name": "开启调试模式", 377 | "val": false, 378 | "type": "boolean", 379 | "desc": "默认关闭" 380 | },{ 381 | "id": "xmyx_data", 382 | "name": "星妈优选小程序Token", 383 | "val": null, 384 | "type": "textarea", 385 | "autoGrow": true, 386 | "desc": "星妈优选小程序Token" 387 | } 388 | ] 389 | }, 390 | { 391 | "id": "Leiyiyan_Longhu", 392 | "name": "龙湖天街小程序", 393 | "author": "@leiyiyan", 394 | "repo": "", 395 | "icons": [ 396 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/lhtj.png", 397 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/lhtj.png" 398 | ], 399 | "keys": [ 400 | "is_debug", 401 | "lhtj_data" 402 | ], 403 | "settings": [ 404 | { 405 | "id": "is_debug", 406 | "name": "开启调试模式", 407 | "val": false, 408 | "type": "boolean", 409 | "desc": "默认关闭" 410 | },{ 411 | "id": "lhtj_data", 412 | "name": "龙湖天街小程序Token", 413 | "val": null, 414 | "type": "textarea", 415 | "autoGrow": true, 416 | "desc": "龙湖天街小程序Token" 417 | } 418 | ] 419 | }, 420 | { 421 | "id": "Leiyiyan_Wanda", 422 | "name": "万达智慧商业小程序", 423 | "author": "@leiyiyan", 424 | "repo": "", 425 | "icons": [ 426 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/wdzhsy.png", 427 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/wdzhsy.png" 428 | ], 429 | "keys": [ 430 | "is_debug", 431 | "wdzhsy_token" 432 | ], 433 | "settings": [ 434 | { 435 | "id": "is_debug", 436 | "name": "开启调试模式", 437 | "val": false, 438 | "type": "boolean", 439 | "desc": "默认关闭" 440 | },{ 441 | "id": "wdzhsy_token", 442 | "name": "万达智慧商业小程序Token", 443 | "val": null, 444 | "type": "textarea", 445 | "autoGrow": true, 446 | "desc": "万达智慧商业小程序Token" 447 | } 448 | ] 449 | }, 450 | { 451 | "id": "Leiyiyan_Ningji", 452 | "name": "柠季小程序", 453 | "author": "@leiyiyan", 454 | "repo": "", 455 | "icons": [ 456 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/ningji.png", 457 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/ningji.png" 458 | ], 459 | "keys": [ 460 | "is_debug", 461 | "ningji_data" 462 | ], 463 | "settings": [ 464 | { 465 | "id": "is_debug", 466 | "name": "开启调试模式", 467 | "val": false, 468 | "type": "boolean", 469 | "desc": "默认关闭" 470 | },{ 471 | "id": "ningji_data", 472 | "name": "柠季小程序Token", 473 | "val": null, 474 | "type": "textarea", 475 | "autoGrow": true, 476 | "desc": "柠季小程序Token" 477 | } 478 | ] 479 | }, 480 | { 481 | "id": "Leiyiyan_iios", 482 | "name": "iios-苹果软件站", 483 | "author": "@leiyiyan", 484 | "repo": "", 485 | "icons": [ 486 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/iios.png", 487 | "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/iios.png" 488 | ], 489 | "keys": [ 490 | "iios_data" 491 | ], 492 | "settings": [ 493 | { 494 | "id": "iios_data", 495 | "name": "账号信息", 496 | "val": null, 497 | "type": "textarea", 498 | "autoGrow": true, 499 | "desc": "账号信息" 500 | } 501 | ] 502 | } 503 | ] 504 | } 505 | -------------------------------------------------------------------------------- /subscribe/leiyiyan.icons.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Leiyiyan图标订阅", 3 | "description": "脚本图标订阅", 4 | "icons": [ 5 | { 6 | "name": "zmhy.png", 7 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/zmhy.png" 8 | }, 9 | { 10 | "name": "aliyun_web.png", 11 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/aliyun_web.png" 12 | }, 13 | { 14 | "name": "wx.png", 15 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/wx.png" 16 | }, 17 | { 18 | "name": "yrh.png", 19 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/yrh.png" 20 | }, 21 | { 22 | "name": "xmyx.png", 23 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/xmyx.png" 24 | }, 25 | { 26 | "name": "picc.png", 27 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/picc.png" 28 | }, 29 | { 30 | "name": "cbd.png", 31 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cbd.png" 32 | }, 33 | { 34 | "name": "ningji.png", 35 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/ningji.png" 36 | }, 37 | { 38 | "name": "zeeho.png", 39 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/zeeho.png" 40 | }, 41 | { 42 | "name": "wdzhsy.png", 43 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/wdzhsy.png" 44 | }, 45 | { 46 | "name": "lhtj.png", 47 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/lhtj.png" 48 | }, 49 | { 50 | "name": "pyy.png", 51 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/pyy.png" 52 | }, 53 | { 54 | "name": "cfmoto.png", 55 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/cfmoto.png" 56 | }, 57 | { 58 | "name": "netease_music.png", 59 | "url": "https://raw.githubusercontent.com/leiyiyan/resource/main/icons/netease_music.png" 60 | } 61 | ] 62 | } --------------------------------------------------------------------------------