├── images ├── hch.png ├── tsl.png ├── yrh.png ├── 10086.png ├── 12123.png ├── 95598.png ├── WeChat.png ├── aeon.png ├── check.png ├── debug.png ├── huawei.png ├── jdjoy.png ├── jhsh.png ├── sync.png ├── tuhu.png ├── update.png ├── default.png ├── download.png ├── hisense.png ├── iMaoTai.png ├── icon │ ├── gwd.png │ ├── jf.png │ ├── mmm.png │ ├── zdm.png │ ├── copy.png │ └── icon.psd ├── jingfen.png ├── jparking.png ├── mi_sport.png ├── ThomasCook.png ├── analyzing.png ├── geocaching.png ├── livingmall.png ├── pp_parking.png ├── background_1.jpg ├── background_2.jpg ├── background_3.jpg ├── background_4.jpg ├── china_telecom.png └── wechat_pay_coupon.png ├── rewrite ├── get_10000_cookie.conf ├── get_10000_cookie.sgmodule ├── get_jd_wskey.conf ├── Cainiao_NoAD.conf ├── get_jd_wskey.sgmodule ├── geocaching_helper.sgmodule └── get_user_data.sgmodule ├── scripts ├── jhsh_modifyTime.js ├── zbt.js ├── ql_api.js ├── get_10000_cookie.js ├── get_maotai_token.js ├── ql_to_boxjs.js ├── weifeng.js ├── ql_env_sync.js ├── reRequest.js ├── jd_cookie_search.js ├── hch_sign.js ├── ThomasCook.js ├── jparking_sign.js └── get_jd_wskey.js └── README.md /images/hch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/hch.png -------------------------------------------------------------------------------- /images/tsl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/tsl.png -------------------------------------------------------------------------------- /images/yrh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/yrh.png -------------------------------------------------------------------------------- /images/10086.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/10086.png -------------------------------------------------------------------------------- /images/12123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/12123.png -------------------------------------------------------------------------------- /images/95598.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/95598.png -------------------------------------------------------------------------------- /images/WeChat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/WeChat.png -------------------------------------------------------------------------------- /images/aeon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/aeon.png -------------------------------------------------------------------------------- /images/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/check.png -------------------------------------------------------------------------------- /images/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/debug.png -------------------------------------------------------------------------------- /images/huawei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/huawei.png -------------------------------------------------------------------------------- /images/jdjoy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/jdjoy.png -------------------------------------------------------------------------------- /images/jhsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/jhsh.png -------------------------------------------------------------------------------- /images/sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/sync.png -------------------------------------------------------------------------------- /images/tuhu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/tuhu.png -------------------------------------------------------------------------------- /images/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/update.png -------------------------------------------------------------------------------- /images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/default.png -------------------------------------------------------------------------------- /images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/download.png -------------------------------------------------------------------------------- /images/hisense.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/hisense.png -------------------------------------------------------------------------------- /images/iMaoTai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/iMaoTai.png -------------------------------------------------------------------------------- /images/icon/gwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/icon/gwd.png -------------------------------------------------------------------------------- /images/icon/jf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/icon/jf.png -------------------------------------------------------------------------------- /images/icon/mmm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/icon/mmm.png -------------------------------------------------------------------------------- /images/icon/zdm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/icon/zdm.png -------------------------------------------------------------------------------- /images/jingfen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/jingfen.png -------------------------------------------------------------------------------- /images/jparking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/jparking.png -------------------------------------------------------------------------------- /images/mi_sport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/mi_sport.png -------------------------------------------------------------------------------- /images/ThomasCook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/ThomasCook.png -------------------------------------------------------------------------------- /images/analyzing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/analyzing.png -------------------------------------------------------------------------------- /images/geocaching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/geocaching.png -------------------------------------------------------------------------------- /images/icon/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/icon/copy.png -------------------------------------------------------------------------------- /images/icon/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/icon/icon.psd -------------------------------------------------------------------------------- /images/livingmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/livingmall.png -------------------------------------------------------------------------------- /images/pp_parking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/pp_parking.png -------------------------------------------------------------------------------- /images/background_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/background_1.jpg -------------------------------------------------------------------------------- /images/background_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/background_2.jpg -------------------------------------------------------------------------------- /images/background_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/background_3.jpg -------------------------------------------------------------------------------- /images/background_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/background_4.jpg -------------------------------------------------------------------------------- /images/china_telecom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/china_telecom.png -------------------------------------------------------------------------------- /images/wechat_pay_coupon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FoKit/Scripts/HEAD/images/wechat_pay_coupon.png -------------------------------------------------------------------------------- /rewrite/get_10000_cookie.conf: -------------------------------------------------------------------------------- 1 | # 获取中国电信 Cookie 2 | # 登录入口:http://u3v.cn/5uwtIP 3 | 4 | ^https:\/\/(e\.189\.cn\/store\/user|open\.e\.189\.cn\/api\/logbox\/oauth2\/loginSubmit\.do) url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_10000_cookie.js 5 | 6 | hostname = e.189.cn, open.e.189.cn -------------------------------------------------------------------------------- /rewrite/get_10000_cookie.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=中国电信 Cookie 2 | #!desc=自动抓取中国电信 Cookie,登录入口:http://u3v.cn/5uwtIP 3 | 4 | [Script] 5 | 中国电信 Cookie = type=http-request,pattern=^https:\/\/(e\.189\.cn\/store\/user|open\.e\.189\.cn\/api\/logbox\/oauth2\/loginSubmit\.do),requires-body=0,max-size=0,timeout=1000,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_10000_cookie.js,script-update-interval=0 6 | 7 | [MITM] 8 | hostname = %APPEND% e.189.cn, open.e.189.cn 9 | -------------------------------------------------------------------------------- /rewrite/get_jd_wskey.conf: -------------------------------------------------------------------------------- 1 | #!name=京东 WSKEY 2 | #!desc=自动抓取京东WSKEY, 仅支持 Quantumult-X, 未经允许请勿使用。(自动上车) 3 | #!使用方法:划掉后台重新打开 京东APP 即可自动抓取 WSKEY。抓完 WSKEY 不能在京东 app 点退出登录(会导致 WSKEY 失效),切换账号的正确姿势是先断网(飞行模式)再点击退出登录,划掉后台重新打开 APP 再登录新的账号。 4 | 5 | #!Surge版本:https://raw.githubusercontent.com/FoKit/Scripts/main/rewrite/get_jd_wskey.sgmodule 6 | 7 | hostname = blackhole.m.jd.com, perf.m.jd.com 8 | 9 | https:\/\/blackhole\.m\.jd\.com\/getinfo url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js 10 | 11 | https:\/\/perf\.m\.jd\.com\/app_monitor\/\w{1,}\/getRule url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js 12 | -------------------------------------------------------------------------------- /rewrite/Cainiao_NoAD.conf: -------------------------------------------------------------------------------- 1 | # 菜鸟裹裹去广告@ddgksf2013 2 | 3 | # [filter_local] 4 | # host, amdc.m.taobao.com, reject 5 | 6 | ^https?:\/\/cn-acs\.m\.cainiao\.com\/gw\/mtop\.cainiao\.guoguo\.nbnetflow\.ads\.(show|mshow)\.cn url reject-200 7 | 8 | ^https?:\/\/cn-acs\.m\.cainiao\.com\/gw\/mtop\.cainiao\.nbpresentation\.protocol\.homepage\.get\.cn url script-response-body https://raw.githubusercontent.com/ddgksf2013/Scripts/master/cainiao_json.js 9 | 10 | ^https?:\/\/cn-acs\.m\.cainiao\.com\/gw\/mtop\.cainiao\.adkeyword url script-response-body https://raw.githubusercontent.com/ddgksf2013/Scripts/master/cainiao_json.js 11 | 12 | ^https?:\/\/cn-acs\.m\.cainiao\.com\/gw\/mtop\.cainiao\.guoguo\.nbnetflow\.ads\.index\.cn,requires url script-response-body https://raw.githubusercontent.com/ddgksf2013/Scripts/master/cainiao_json.js 13 | 14 | hostname = cn-acs.m.cainiao.com 15 | -------------------------------------------------------------------------------- /rewrite/get_jd_wskey.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=京东 WSKEY 2 | #!desc=自动抓取京东WSKEY, 仅支持 Surge, 未经允许请勿使用。(自动上车) 3 | #!使用方法:划掉后台重新打开 京东APP 即可自动抓取 WSKEY。抓完 WSKEY 不能在京东 app 点退出登录(会导致 WSKEY 失效),切换账号的正确姿势是先断网(飞行模式)再点击退出登录,划掉后台重新打开 APP 再登录新的账号。 4 | 5 | #!Quantumult X版本:https://raw.githubusercontent.com/FoKit/Scripts/main/rewrite/get_jd_wskey.conf 6 | 7 | [Script] 8 | 京东 WSKEY = type=http-request,pattern=https:\/\/blackhole\.m\.jd\.com\/getinfo,requires-body=0,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js,script-update-interval=0 9 | 10 | 京东 PIN = type=http-request,pattern=https:\/\/perf\.m\.jd\.com\/app_monitor\/\w{1,}\/getRule,requires-body=0,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js,script-update-interval=0 11 | 12 | [MITM] 13 | hostname = %APPEND% blackhole.m.jd.com, perf.m.jd.com 14 | -------------------------------------------------------------------------------- /scripts/jhsh_modifyTime.js: -------------------------------------------------------------------------------- 1 | let time = ''; 2 | let json = JSON.parse($response.body); 3 | 4 | if (json?.data?.SYSTEM_TIME) { 5 | if (json?.data?.MSPS_ENTITY?.EFFECT_PERIOD_START) { 6 | time = json.data.MSPS_ENTITY.EFFECT_PERIOD_START.replace(/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/, '$1-$2-$3 $4:$5:$6'); 7 | } else if (json?.data?.ACT_START_DTM) { 8 | time = json.data.ACT_START_DTM; 9 | } else if (json?.data?.KHHK_ENTITY?.DcCp_Avy_StTm) { 10 | time = json?.data?.SYSTEM_TIME.replace(/(\d{2}):(\d{2}):(\d{2})$/, json.data.KHHK_ENTITY.DcCp_Avy_StTm.replace(/^(\d{2})(\d{2})(\d{2})$/, '$1:$2:$3')); 11 | } 12 | 13 | if (time) { 14 | if (Date.now() < Date.parse(time)) { 15 | json['data']['SYSTEM_TIME'] = time; 16 | } 17 | console.log(`SYSTEM_TIME: ${json['data']['SYSTEM_TIME']}`); 18 | } 19 | } else { 20 | console.log($response.body); 21 | } 22 | 23 | $done({ body: JSON.stringify(json) }); 24 | -------------------------------------------------------------------------------- /scripts/zbt.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:找不同8000关+ 3 | 脚本说明:修改体力和道具数量为99999 4 | 使用方法:上传存档 -> 下载存档 5 | 6 | [rewrite_local] 7 | ^https:\/\/api\.gzgame99\.cn\/zc\/user\/setGameData url script-request-body https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/zbt.js 8 | ^https:\/\/api\.gzgame99\.cn\/zc\/user\/getGameData url script-response-body https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/zbt.js 9 | 10 | [MITM] 11 | hostname = api.gzgame99.cn 12 | */ 13 | 14 | 15 | let str = ''; 16 | 17 | if ($request.url.indexOf("setGameData") > -1) { 18 | str = $request.body; 19 | console.log('上传存档'); 20 | } else if ($request.url.indexOf("getGameData") > -1) { 21 | str = $response.body; 22 | console.log('下载存档'); 23 | } 24 | 25 | if (str) { 26 | let str2 = str.replace(/"curStrength":\d+,/g, `"curStrength":99999,`).replace(/"1":\d+,/g, `"1":99999,`); 27 | if (str != str2) { 28 | console.log('修改成功'); 29 | } else { 30 | console.log('修改失败') 31 | } 32 | 33 | $done({ body: str2 }); 34 | } else { 35 | console.log('空数据'); 36 | $done({}); 37 | } 38 | -------------------------------------------------------------------------------- /rewrite/geocaching_helper.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Geocaching 助手 2 | #!desc=用于修正 Geocaching 的 GPS 坐标、翻译 log / describe 3 | 4 | [MITM] 5 | hostname = %APPEND% api.groundspeak.com 6 | 7 | [Script] 8 | Geocaching logs = type=http-response,pattern=^https:\/\/api\.groundspeak\.com\/mobile\/v\d\/geocaches\/[A-Z0-9]{7}\/geocachelogs\?(onlyFriendLogs=\w+&)?skip=\d+&take=20,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/geocaching_helper.js 9 | Geocaching cache = type=http-response,pattern=^https:\/\/api\.groundspeak\.com\/mobile\/v1\/geocaches\/[A-Z0-9]{7}$,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/geocaching_helper.js 10 | Geocaching gps = type=http-response,pattern=^https:\/\/api\.groundspeak\.com\/mobile\/v1\/map\/search\?adventuresTake,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/geocaching_helper.js 11 | Geocaching unlock = type=http-response,pattern=^https:\/\/api\.groundspeak\.com\/mobile\/v1\/profileview,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/geocaching_helper.js 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 访问统计: 2 | 3 |  4 | 5 | --- 6 | 7 | ### 仓库脚本: 8 | 9 | | 序号 | 脚本名称 | 脚本类型 | 更新时间 | 运行环境 | 可用状态 | 10 | | ---- | --------------------------------------------------------------------------------------------------- | -------------- | ---------- | --------- | -------- | 11 | | 01 | [中国电信](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_10000_cookie.js) | rewrite | 2022-11-11 | NE | ✅ | 12 | | 02 | [京东 WSKEY](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js) | rewrite | 2024-07-27 | NE | ✅ | 13 | | 03 | [找不同 8000 关+](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/zbt.js) | rewrite | 2023-07-17 | NE | ✅ | 14 | | 04 | [Geocaching](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/geocaching_helper.js) | rewrite | 2024-05-24 | NE | ✅ | 15 | | 05 | [青龙变量同步](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ql_env_sync.js) | task | 2023-06-27 | Node | ✅ | 16 | | 06 | [京东账号同步](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ql_to_boxjs.js) | task | 2023-04-09 | NE | ✅ | 17 | | 07 | [海信爱家](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/Hisense.js) | task & rewrite | 2024-03-19 | NE & Node | ❌ | 18 | | 08 | [建行生活](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jhsh_checkIn.js) | task & rewrite | 2024-03-27 | NE & Node | ❌ | 19 | | 09 | [捷停车](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js) | task & rewrite | 2024-05-24 | NE & Node | ✅ | 20 | | 10 | [丽影云街](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/livingmall.js) | task & rewrite | 2024-03-27 | NE & Node | ✅ | 21 | | 11 | [PP 停车](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/pp_parking.js) | task & rewrite | 2024-03-12 | NE & Node | ✅ | 22 | | 12 | [复游会(托迈酷客)](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ThomasCook.js) | task & rewrite | 2024-04-10 | NE & Node | ✅ | 23 | | 13 | [新浪微博](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/weibo_sign.js) | task & rewrite | 2022-11-08 | NE & Node | ✅ | 24 | | 14 | [威锋论坛](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/weifeng.js) | task | 2022-11-15 | NE & Node | ✅ | 25 | | 15 | [伊利乳品](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/yiLi.js) | task & rewrite | 2024-04-08 | NE & Node | ✅ | 26 | | 16 | [悦然荟](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/yueran_sign.js) | task & rewrite | 2024-02-26 | NE & Node | ✅ | 27 | | 17 | [花城汇](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js) | task & rewrite | 2025-08-23 | NE & Node | ✅ | 28 | | 18 | [途虎养车](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/tuhu.js) | task & rewrite | 2023-03-20 | NE & Node | ✅ | 29 | | 19 | [谢瑞麟 · TSL](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/tsl_sign.js) | task & rewrite | 2024-04-12 | NE & Node | ✅ | 30 | | 20 | [微信支付有优惠](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/wechat_pay_coupon.js) | task & rewrite | 2024-05-18 | NE | ✅ | 31 | | 21 | [永旺 Aeon](https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/aeon_sign.js) | task & rewrite | 2024-04-11 | NE & Node | ❌ | 32 | 33 | --- 34 | 35 | ### 免责声明: 36 | 37 | 1. 本仓库发布的代码,包括其中涉及的任何解密分析代码,仅供学术研究和参考学习,禁止用于商业或非法用途。由于代码可能存在的法律性、准确性、有效性问题,用户应自行做出判断,并自行承担可能产生的所有风险,本人对此不承担任何保证责任。 38 | 2. 若本仓库代码涉及到的任何应用或平台与本人无关。本人不对因使用该代码可能导致的任何隐私泄漏或其他后果承担责任。此外,对于本仓库代码可能引起的任何问题,包括但不限于由代码错误导致的任何损失或损害,本人亦不承担任何责任。 39 | 3. 本仓库的所有内容仅供学习研究,用户下载后必须在 24 小时内将所有内容从设备上完全删除。若有任何违反此规定所引起的后果,本人不承担任何责任。 40 | 4. 如有任何单位或个人认为本仓库所提供的代码可能侵犯其权利,应及时提供身份证明及所有权证明并以书面形式通知我们。收到证明文件确认后,我们将尽快删除相关内容。 41 | 5. 任何直接或间接访问、使用或复制本仓库代码的人,必须仔细阅读并理解此声明。本人保留随时更新或补充此免责声明的权利,一旦您访问或使用了本仓库,即表示您已经阅读并接受此免责声明。 42 | -------------------------------------------------------------------------------- /scripts/ql_api.js: -------------------------------------------------------------------------------- 1 | 2 | $.ql = { 3 | type: 'api', 4 | headers: { 5 | 'Content-Type': `application/json;charset=UTF-8`, 6 | Authorization: '', 7 | }, 8 | disabled(ids) { 9 | if (!this.headers.Authorization) return; 10 | const opt = { 11 | url: `${$.ql_url}/${this.type}/envs/disable`, 12 | headers: this.headers, 13 | body: JSON.stringify(ids), 14 | }; 15 | return $.http.put(opt).then((response) => JSON.parse(response.body)); 16 | }, 17 | enabled(ids) { 18 | if (!this.headers.Authorization) return; 19 | const opt = { 20 | url: `${$.ql_url}/${this.type}/envs/enable`, 21 | headers: this.headers, 22 | body: JSON.stringify(ids), 23 | }; 24 | return $.http.put(opt).then((response) => JSON.parse(response.body)); 25 | }, 26 | delete(ids) { 27 | if (!this.headers.Authorization) return; 28 | const opt = { 29 | url: `${$.ql_url}/${this.type}/envs`, 30 | headers: this.headers, 31 | body: JSON.stringify(ids), 32 | }; 33 | return $.http.delete(opt).then((response) => JSON.parse(response.body)); 34 | }, 35 | add(records) { 36 | if (!this.headers.Authorization) return; 37 | const opt = { 38 | url: `${$.ql_url}/${this.type}/envs`, 39 | headers: this.headers, 40 | body: JSON.stringify(records), 41 | }; 42 | return $.http.post(opt).then((response) => JSON.parse(response.body)); 43 | }, 44 | edit(records) { 45 | if (!this.headers.Authorization) return; 46 | const opt = { 47 | url: `${$.ql_url}/${this.type}/envs`, 48 | headers: this.headers, 49 | body: JSON.stringify(records), 50 | }; 51 | return $.http.put(opt).then((response) => JSON.parse(response.body)); 52 | }, 53 | select(searchValue = 'JD_COOKIE') { 54 | if (!this.headers.Authorization) return; 55 | const opt = { 56 | url: `${$.ql_url}/${this.type}/envs?searchValue=${searchValue}`, 57 | headers: this.headers, 58 | }; 59 | return $.http.get(opt).then((response) => JSON.parse(response.body)); 60 | }, 61 | configs(fileName = 'config.sh') { 62 | if (!this.headers.Authorization) return; 63 | const opt = { 64 | url: `${$.ql_url}/${this.type}/configs/${fileName}?t=${Date.now()}`, 65 | headers: this.headers, 66 | }; 67 | return $.http.get(opt).then((response) => JSON.parse(response.body)); 68 | }, 69 | }; 70 | 71 | $.ql_config = JSON.parse($.read('#ql')); 72 | $.ql_url = $.ql_config.ip; 73 | 74 | if (!$.ql_url.match(/^(http|https)/)) $.ql_url = `http://${$.ql_url}`; 75 | 76 | $.application = { 77 | client_id: $.ql_config.client_id, 78 | client_secret: $.ql_config.client_secret, 79 | }; 80 | 81 | $.ql_account = { 82 | username: $.ql_config.username, 83 | password: $.ql_config.password, 84 | }; 85 | 86 | $.log(`地址:${$.ql_url}`); 87 | 88 | function noReady() { 89 | $.ql = false; 90 | $.log('请配置好相关信息'); 91 | } 92 | 93 | if ($.ql_config.is_pwd === 'true') { 94 | if ($.ql_account.username && $.ql_account.password) { 95 | $.ql.login = async () => { 96 | const options = { 97 | url: `${$.ql_url}/api/user/login`, 98 | body: JSON.stringify($.ql_account), 99 | headers: { 100 | 'Content-Type': `application/json;charset=UTF-8`, 101 | }, 102 | }; 103 | let response = await $.http.post(options); 104 | response = JSON.parse(response.body); 105 | if (response.code === 200) { 106 | $.ql.type = 'api'; 107 | $.ql.headers.Authorization = `Bearer ${response.data.token}`; 108 | $.log(`登陆成功:${response.data.lastaddr}`); 109 | $.log(`ip:${response.data.lastip}`); 110 | } else { 111 | $.log(response); 112 | $.log(`登陆失败:${response.message}`); 113 | } 114 | }; 115 | } else { 116 | noReady(); 117 | } 118 | } else { 119 | if ($.application.client_id && $.application.client_secret) { 120 | $.ql.login = async () => { 121 | const options = { 122 | url: `${$.ql_url}/open/auth/token?client_id=${$.application.client_id}&client_secret=${$.application.client_secret}`, 123 | headers: { 124 | 'Content-Type': `application/json;charset=UTF-8`, 125 | }, 126 | }; 127 | let response = await $.http.get(options); 128 | response = JSON.parse(response.body); 129 | if (response.code === 200) { 130 | $.ql.type = 'open'; 131 | $.ql.headers.Authorization = `Bearer ${response.data.token}`; 132 | $.log(`登陆成功`); 133 | } else { 134 | $.log(response); 135 | $.log(`登陆失败:${response.message}`); 136 | } 137 | }; 138 | } else { 139 | noReady(); 140 | } 141 | } -------------------------------------------------------------------------------- /rewrite/get_user_data.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=用户数据 2 | #!desc=获取用户数据模块(合集) 3 | #!category=FoKit 4 | 5 | [MITM] 6 | hostname = %APPEND% api.aeonbuy.com, app.moutai519.com.cn, member.mowgz.com, sweixin.hisense.com, yunbusiness.ccb.com, sytgate.jslife.com.cn, rest.zhimatech.com, api.660pp.com, apis.folidaymall.com, payapp.weixin.qq.com, api.tuhu.cn, club.yili.com, wox2019.woxshare.com, ccsp-egmas.sf-express.com, vip.heytea.com, www.kozbs.com, cdfmbrapi.cdfg.com.cn, tslmember-crm.tslj.com.cn, www.wandawic.com, qmwebapi.qmai.cn, webapi.qmai.cn, mxsa.mxbc.net, wechat.dairyqueen.com.cn, wxxcx.dairyqueen.com.cn, game.dominos.com.cn, developer.aliyun.com, auth.alipan.com, auth.aliyundrive.com, ulp.michelin.com.cn 7 | 8 | 9 | [Script] 10 | 永旺 Aeon = type=http-response,pattern=https:\/\/api\.aeonbuy\.com\/api\/access-auth-api\/auth\/third\/silentWechatMiniLogin,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/aeon_sign.js,script-update-interval=0 11 | 12 | i 茅台 = type=http-request,pattern=^https:\/\/app\.moutai519\.com\.cn\/xhr\/front\/mall\/message\/unRead\/query,requires-body=0,max-size=0,timeout=1000,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_maotai_token.js,script-update-interval=0 13 | 14 | 花城汇 = type=http-request,pattern=^https:\/\/member\.mowgz\.com\/bus\/getFunc\?busId=Member&methodId=getMember&getPram=gold%2Clevel&compId=\d+?&userId=\w+,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js 15 | 16 | 海信数据 = type=http-response,pattern=^https:\/\/sweixin\.hisense\.com\/ecrp\/member\/initMember,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/Hisense.js 17 | 18 | 建行数据 = type=http-request,pattern=^https:\/\/yunbusiness\.ccb\.com\/(clp_coupon|clp_service)\/txCtrl\?txcode=(A3341A038|autoLogin),requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jhsh_checkIn.js 19 | 20 | 捷停车 = type=http-request, pattern=^https:\/\/sytgate\.jslife\.com\.cn\/core-gateway\/order\/carno\/pay\/info, requires-body=1, max-size=0, script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js 21 | 22 | 丽影云街 = type=http-request,pattern=https:\/\/rest\.zhimatech\.com\/v3\/api,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/livingmall.js 23 | 24 | PP 停车 = type=http-request,pattern=^https:\/\/api\.660pp\.com\/rest\/[\d\.]+?\/user\/token,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/pp_parking.js 25 | 26 | 复游会 = type=http-request,pattern=^https:\/\/apis\.folidaymall\.com\/online\/capi\/uc\/getCount,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ThomasCook.js 27 | 28 | 途虎养车 = type=http-request,pattern=https:\/\/api\.tuhu\.cn\/User\/GetInternalCenterInfo,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/tuhu.js 29 | 30 | 微付金币 = type=http-response,pattern=https:\/\/payapp\.weixin\.qq\.com\/(coupon-center-user\/home\/login|coupon-center-award\/award\/detail),requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/wechat_pay_coupon.js,script-update-interval=0 31 | 32 | 伊利乳品 = type=http-request, requires-body=1, pattern=https:\/\/club\.yili\.com\/MALLIFChe\/MCSWSIAPI\.asmx\/Call,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/yiLi.js 33 | 34 | 悦然荟 = type=http-request,pattern=^https?:\/\/wox2019\.woxshare\.com\/clientApi\/userCenterDetail,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/yueran_sign.js 35 | 36 | 谢瑞麟 = type=http-response,pattern=https:\/\/tslmember-crm\.tslj\.com\.cn\/api\/auth\/login,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/tsl_sign.js,script-update-interval=0 37 | 38 | #################### 以下为非本人脚本部分 #################### 39 | 40 | 顺丰速运 = type=http-request,pattern=^https:\/\/ccsp-egmas.sf-express.com\/cx-app-member\/member\/app\/user\/universalSign,requires-body=1,max-size=0,timeout=1000,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/sfexpress/sfexpress.cookie.js,script-update-interval=0 41 | 42 | 喜茶 = type=http-response,pattern=^https:\/\/vip.heytea.com\/api\/service-member\/vip\/task\/member,requires-body=1,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/cc0928ca661cf3d89f55902e11b28432/raw/heytea.js,script-update-interval=0 43 | 44 | 植白说 = type=http-request,pattern=https:\/\/www\.kozbs\.com\/demo\/wx\/home\/signDay\?userId=.+,requires-body=0,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/3bd5650dab5194261d9426869ae8cd38/raw/zbs.js,script-update-interval=0 45 | 46 | 中免会员 = type=http-response,pattern=^https:\/\/cdfmbrapi\.cdfg\.com\.cn\/api\/user\/info,requires-body=1,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/2babe9ffd5dc6c7929e667bb2203421a/raw/zmhy.js,script-update-interval=0 47 | 48 | 万达商业 = type=http-response,pattern=^https?:\/\/www\.wandawic\.com\/api\/foreground\/loginregister\/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 49 | 50 | 霸王茶姬 = type=http-response,pattern=^https:\/\/(webapi|qmwebapi)\.qmai\.cn\/web\/(catering|catering2-apiserver)\/crm\/points-info,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://gist.githubusercontent.com/Sliverkiss/4984f7f34d6df8bcdd1e13ecac4bba51/raw/bwcj.js,script-update-interval=0 51 | 52 | 益禾堂 = type=http-request,pattern=^https:\/\/(webapi|qmwebapi)\.qmai\.cn\/web\/(catering|catering2-apiserver)\/crm\/points-info,requires-body=1,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/df5dbbf01bc3acc3bccaf7880acd242b/raw/yht.js,script-update-interval=0 53 | 54 | 蜜雪冰城 = type=http-response,pattern=^https:\/\/mxsa\.mxbc\.net\/api\/v1\/customer\/info,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://gist.githubusercontent.com/Sliverkiss/865c82e42a5730bb696f6700ebb94cee/raw/mxbc.js,script-update-interval=0 55 | 56 | CFB (DQ) = type=http-request,pattern=^https:\/\/(wechat|wxxcx)\.dairyqueen\.com\.cn\/(candaoAppLogin|UserXueLi\?_actionName=getXueLiSign),requires-body=1,max-size=0,timeout=1000,script-path=https://raw.githubusercontent.com/Sliverkiss/GoodNight/master/Script/cfb.js,script-update-interval=0 57 | 58 | 达美乐披萨 = type=http-request,pattern=^https:\/\/game\.dominos\.com\.cn\/.+\/game\/gameDone,requires-body=1,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/6b4da0d367d13790a9fd1d928c82bdf8/raw/dlm.js,script-update-interval=0 59 | 60 | 阿里云社区 = type=http-response,pattern=^https?:\/\/developer\.aliyun\.com\/developer\/api\/my\/user\/getUser,requires-body=1,max-size=0,binary-body-mode=0,timeout=30,script-path=https://gist.githubusercontent.com/Sliverkiss/ba14fe4fb20b640c17118c445461f7c6/raw/aliyunWeb.js,script-update-interval=0 61 | 62 | 阿里云盘 = type=http-request,pattern=^https:\/\/(auth|aliyundrive)\.alipan\.com\/v2\/account\/token,requires-body=1,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/33800a98dcd029ba09f8b6fc6f0f5162/raw/aliyun.js,script-update-interval=0 63 | 64 | 米其林俱乐部 = type=http-request,pattern=^https:\/\/ulp\.michelin\.com\.cn\/bff\/profile,requires-body=1,max-size=0,timeout=1000,script-path=https://gist.githubusercontent.com/Sliverkiss/49c5d5176cad6e47919ffe058606ed0d/raw/michelin.js,script-update-interval=0 65 | -------------------------------------------------------------------------------- /scripts/get_10000_cookie.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:中国电信 Cookie 3 | 更新时间:2022-11-11 4 | 登录入口:http://u3v.cn/5uwtIP 5 | 重写订阅(QX):https://raw.githubusercontent.com/FoKit/Scripts/main/rewrite/get_10000_cookie.conf 6 | 重写订阅(Surge):https://raw.githubusercontent.com/FoKit/Scripts/main/rewrite/get_10000_cookie.sgmodule 7 | BoxJs 订阅:https://raw.githubusercontent.com/FoKit/Scripts/main/boxjs/fokit.boxjs.json 8 | 9 | ================Quantumult X配置================= 10 | [rewrite_local] 11 | ^https:\/\/(e\.189\.cn\/store\/user|open\.e\.189\.cn\/api\/logbox\/oauth2\/loginSubmit\.do) url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_10000_cookie.js 12 | [MITM] 13 | hostname = e.189.cn, open.e.189.cn 14 | ====================Surge配置==================== 15 | [Script] 16 | 中国电信 Cookie = type=http-request,pattern=^https:\/\/(e\.189\.cn\/store\/user|open\.e\.189\.cn\/api\/logbox\/oauth2\/loginSubmit\.do),requires-body=0,max-size=0,timeout=1000,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_10000_cookie.js,script-update-interval=0 17 | [MITM] 18 | hostname = %APPEND% e.189.cn, open.e.189.cn 19 | ====================Loon配置===================== 20 | [Script] 21 | http-request ^https:\/\/(e\.189\.cn\/store\/user|open\.e\.189\.cn\/api\/logbox\/oauth2\/loginSubmit\.do) tag=中国电信 Cookie, script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_10000_cookie.js,requires-body=1 22 | [MITM] 23 | hostname = e.189.cn, open.e.189.cn 24 | */ 25 | 26 | const $ = new Env('中国电信 Cookie'); 27 | $.boxjs_key_1 = 'china_telecom_cookie'; 28 | $.boxjs_key_2 = 'china_telecom_login_url'; 29 | $.boxjs_data_1 = $.getdata($.boxjs_key_1); 30 | $.boxjs_data_2 = $.getdata($.boxjs_key_2); 31 | 32 | !(async () => { 33 | if (isGetCookie = typeof $request !== `undefined`) { 34 | GetCookie(); 35 | } 36 | 37 | // 获取 Cookie / Login_url 38 | function GetCookie() { 39 | if ($request && $request.url.indexOf("https://e.189.cn/store/user/") > -1 && $request.headers) { 40 | if ($request['headers']['Cookie'] || $request['headers']['cookie']) { 41 | $.cookie = $request['headers']['Cookie'] || $request['headers']['cookie']; 42 | $.china_telecom_cookie = $.cookie.match(/(LT=.+?;)/)[1]; 43 | $.china_telecom_cookie += $.cookie.match(/(CZSSON=.+?;)/)[1]; 44 | if ($.china_telecom_cookie && $.china_telecom_cookie !== $.boxjs_data_1) { 45 | $.setdata($.china_telecom_cookie, $.boxjs_key_1); 46 | $.msg(`🎉 Cookie 更新成功。\n${$.china_telecom_cookie}`); 47 | } else { 48 | console.log(`‼️ 无需更新 Cookie。\n${$.china_telecom_cookie}`); 49 | } 50 | } else { 51 | $.msg(`${$.name} 获取失败,未找到 Cookie。`); 52 | } 53 | } else if ($request && $request.url.indexOf("loginSubmit.do") > -1) { 54 | $.china_telecom_login_url = $request.url; 55 | if ($.china_telecom_login_url && $.china_telecom_login_url !== $.boxjs_data_2) { 56 | $.setdata($.china_telecom_login_url, $.boxjs_key_2); 57 | $.msg(`🎉 Login_url 更新成功。\n${$.china_telecom_login_url}`); 58 | } else { 59 | console.log(`‼️ 无需更新 Login_url。\n${$.china_telecom_login_url}`); 60 | } 61 | } 62 | } 63 | 64 | })() 65 | .catch((e) => $.logErr(e)) 66 | .finally(() => $.done()); 67 | 68 | // prettier-ignore 69 | 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}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}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,a]=i.split("@"),n={url:`http://${a}/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),a=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(a);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?s.status:s.statusCode,s.status=s.statusCode),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&&t.error||"UndefinedError"));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:a}=t,n=s.decode(a,this.encoding);e(null,{status:i,statusCode:r,headers:o,rawBody:a,body:n},n)},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?s.status:s.statusCode,s.status=s.statusCode),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&&t.error||"UndefinedError"));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:a}=t,n=i.decode(a,this.encoding);e(null,{status:s,statusCode:r,headers:o,rawBody:a,body:n},n)},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,i=t["update-pasteboard"]||t.updatePasteboard;return{"open-url":e,"media-url":s,"update-pasteboard":i}}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):this.isNode()&&process.exit(1)}}(t,e)} 70 | -------------------------------------------------------------------------------- /scripts/get_maotai_token.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:i茅台Token 3 | 更新时间:2023-02-07 4 | 5 | ==================================================================================================== 6 | 配置 (Quantumult X) 7 | [rewrite_local] 8 | ^https:\/\/app\.moutai519\.com\.cn\/xhr\/front\/mall\/message\/unRead\/query url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_maotai_token.js 9 | 10 | [MITM] 11 | hostname = app.moutai519.com.cn 12 | ==================================================================================================== 13 | 配置 (Surge) 14 | [Script] 15 | i茅台Token = type=http-request,pattern=^https:\/\/app\.moutai519\.com\.cn\/xhr\/front\/mall\/message\/unRead\/query,requires-body=0,max-size=0,timeout=1000,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_maotai_token.js,script-update-interval=0 16 | 17 | [MITM] 18 | hostname = %APPEND% app.moutai519.com.cn 19 | ==================================================================================================== 20 | */ 21 | 22 | const $ = new Env('i茅台'); 23 | $.MT_TOKENS_KEY = 'MT_TOKENS'; 24 | $.MT_TOKENS = $.getdata($.MT_TOKENS_KEY) || ''; 25 | 26 | !(async () => { 27 | if (isGetCookie = typeof $request !== `undefined`) { 28 | GetCookie(); 29 | } 30 | 31 | function GetCookie() { 32 | if ($request && $request.headers) { 33 | log($request.headers); 34 | if (($request.headers['MT-Token'] && $request.headers['MT-Device-ID']) || ($request.headers['mt-token'] && $request.headers['mt-device-id'])) { 35 | let new_MT_Token = $request.headers['MT-Token'] || $request.headers['mt-token']; 36 | let new_Device_ID = $request.headers['MT-Device-ID'] || $request.headers['mt-device-id']; 37 | let old_MT_Token = $.MT_TOKENS.split(',') ? $.MT_TOKENS.split(',')[1] : ''; 38 | if (old_MT_Token !== new_MT_Token) { 39 | $.setdata(new_Device_ID + ',' + new_MT_Token, $.MT_TOKENS_KEY); 40 | $.msg($.name, `🎉 Token获取成功`, `${new_Device_ID + ',' + new_MT_Token}`); 41 | } else { 42 | $.log(`无需更新 MT-Token:\n${new_Device_ID + ',' + new_MT_Token}\n`); 43 | } 44 | } 45 | if ($request.headers['MT-APP-Version'] || $request.headers['mt-app-version']) { 46 | $.MT_VERSION = $request.headers['MT-APP-Version'] || $request.headers['mt-app-version']; 47 | $.setdata($.MT_VERSION, `MT_VERSION`); 48 | $.log(`🎉 MT_VERSION 写入成功:\n${$.MT_VERSION}\n`); 49 | } 50 | if ($request.headers['User-Agent'] || $request.headers['user-agent']) { 51 | $.MT_USERAGENT = $request.headers['User-Agent'] || $request.headers['user-agent']; 52 | $.setdata($.MT_USERAGENT, `MT_USERAGENT`); 53 | $.log(`🎉 MT_USERAGENT 写入成功:\n${$.MT_USERAGENT}\n`); 54 | } 55 | if ($request.headers['MT-R'] || $request.headers['mt-r']) { 56 | $.MT_R = $request.headers['MT-R'] || $request.headers['mt-r']; 57 | $.setdata($.MT_R, `MT_R`); 58 | $.log(`🎉 MT_R 写入成功:\n${$.MT_R}\n`); 59 | } 60 | } 61 | } 62 | 63 | function log(text) { 64 | if ($.getdata('is_debug') === 'true') { 65 | if (typeof text == "string") { 66 | console.log(text); 67 | } else if (typeof text == "object") { 68 | console.log($.toStr(text)); 69 | } 70 | } 71 | } 72 | 73 | })() 74 | .catch((e) => $.logErr(e)) 75 | .finally(() => $.done()); 76 | 77 | // prettier-ignore 78 | 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}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}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,a]=i.split("@"),n={url:`http://${a}/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),a=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(a);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?s.status:s.statusCode,s.status=s.statusCode),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&&t.error||"UndefinedError"));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:a}=t,n=s.decode(a,this.encoding);e(null,{status:i,statusCode:r,headers:o,rawBody:a,body:n},n)},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?s.status:s.statusCode,s.status=s.statusCode),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&&t.error||"UndefinedError"));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:a}=t,n=i.decode(a,this.encoding);e(null,{status:s,statusCode:r,headers:o,rawBody:a,body:n},n)},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,i=t["update-pasteboard"]||t.updatePasteboard;return{"open-url":e,"media-url":s,"update-pasteboard":i}}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):this.isNode()&&process.exit(1)}}(t,e)} 79 | -------------------------------------------------------------------------------- /scripts/ql_to_boxjs.js: -------------------------------------------------------------------------------- 1 | /* 2 | 京东账号同步工具,QL ---> BOXJS 3 | 40 * * * https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ql_to_boxjs.js 4 | */ 5 | 6 | const $ = new API('ql', true); 7 | const title = '京东账号同步'; 8 | const cookiesKey = '#CookiesJD'; 9 | const jd_cookies = JSON.parse($.read(cookiesKey) || '[]'); 10 | 11 | // function getUsername(ck) { 12 | // if (!ck) return ''; 13 | // return decodeURIComponent(ck.match(/pt_pin=(.+?);/)[1]); 14 | // } 15 | 16 | async function getScriptUrl() { 17 | const response = await $.http.get({ 18 | url: 'https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ql_api.js', 19 | }) 20 | return response.body 21 | } 22 | 23 | (async () => { 24 | const ql_script = (await getScriptUrl()) || ''; 25 | eval(ql_script); 26 | await $.ql.login(); 27 | const configsRes = await $.ql.configs('cookies_user_info.json'); 28 | if (!configsRes) { 29 | $.log(`读取 cookies_user_info.json 失败 ❌`); 30 | $.done(); 31 | } 32 | const userInfos = JSON.parse(configsRes.data); 33 | const userInfosArr = Object.keys(userInfos); 34 | const cookies = []; 35 | for (const pin of userInfosArr) { 36 | if (userInfos[pin]?.cookie) { 37 | cookies.push({ 38 | "userName": decodeURIComponent(pin), 39 | "cookie": userInfos[pin]['cookie'] 40 | }); 41 | } 42 | } 43 | const saveCookie = jd_cookies.map((item) => { 44 | const qlCk = cookies.find((ql) => ql.userName === item.userName); 45 | if (qlCk) return { ...item, ...qlCk }; 46 | return item; 47 | }); 48 | const userNames = saveCookie.map((item) => item.userName); 49 | cookies.forEach((ql) => { 50 | if (userNames.indexOf(ql.userName) === -1) saveCookie.push(ql); 51 | }); 52 | $.write(JSON.stringify(saveCookie, null, `\t`), cookiesKey); 53 | if ($.read('mute') !== 'true') { 54 | console.log(`🎉 已获取到 ${cookies.length} 个Cookie。\n\n${cookies.map((item) => item.userName).join(`\n`)}`); 55 | return $.notify( 56 | title, 57 | `🎉 成功获取 ${cookies.length} 个Cookie`, 58 | ); 59 | } 60 | })() 61 | .catch((e) => { 62 | $.log(JSON.stringify(e)); 63 | }) 64 | .finally(() => { 65 | $.done(); 66 | }); 67 | 68 | function ENV() { 69 | const isQX = typeof $task !== 'undefined'; 70 | const isLoon = typeof $loon !== 'undefined'; 71 | const isSurge = typeof $httpClient !== 'undefined' && !isLoon; 72 | const isJSBox = typeof require == 'function' && typeof $jsbox != 'undefined'; 73 | const isNode = typeof require == 'function' && !isJSBox; 74 | const isRequest = typeof $request !== 'undefined'; 75 | const isScriptable = typeof importModule !== 'undefined'; 76 | return { isQX, isLoon, isSurge, isNode, isJSBox, isRequest, isScriptable }; 77 | } 78 | 79 | function HTTP(defaultOptions = { baseURL: '' }) { 80 | const { isQX, isLoon, isSurge, isScriptable, isNode } = ENV(); 81 | const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH']; 82 | const URL_REGEX = 83 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; 84 | 85 | function send(method, options) { 86 | options = typeof options === 'string' ? { url: options } : options; 87 | const baseURL = defaultOptions.baseURL; 88 | if (baseURL && !URL_REGEX.test(options.url || '')) { 89 | options.url = baseURL ? baseURL + options.url : options.url; 90 | } 91 | options = { ...defaultOptions, ...options }; 92 | const timeout = options.timeout; 93 | const events = { 94 | ...{ 95 | onRequest: () => { }, 96 | onResponse: (resp) => resp, 97 | onTimeout: () => { }, 98 | }, 99 | ...options.events, 100 | }; 101 | 102 | events.onRequest(method, options); 103 | 104 | let worker; 105 | if (isQX) { 106 | worker = $task.fetch({ method, ...options }); 107 | } else if (isLoon || isSurge || isNode) { 108 | worker = new Promise((resolve, reject) => { 109 | const request = isNode ? require('request') : $httpClient; 110 | request[method.toLowerCase()](options, (err, response, body) => { 111 | if (err) reject(err); 112 | else 113 | resolve({ 114 | statusCode: response.status || response.statusCode, 115 | headers: response.headers, 116 | body, 117 | }); 118 | }); 119 | }); 120 | } else if (isScriptable) { 121 | const request = new Request(options.url); 122 | request.method = method; 123 | request.headers = options.headers; 124 | request.body = options.body; 125 | worker = new Promise((resolve, reject) => { 126 | request 127 | .loadString() 128 | .then((body) => { 129 | resolve({ 130 | statusCode: request.response.statusCode, 131 | headers: request.response.headers, 132 | body, 133 | }); 134 | }) 135 | .catch((err) => reject(err)); 136 | }); 137 | } 138 | 139 | let timeoutid; 140 | const timer = timeout 141 | ? new Promise((_, reject) => { 142 | timeoutid = setTimeout(() => { 143 | events.onTimeout(); 144 | return reject( 145 | `${method} URL: ${options.url} exceeds the timeout ${timeout} ms`, 146 | ); 147 | }, timeout); 148 | }) 149 | : null; 150 | 151 | return ( 152 | timer 153 | ? Promise.race([timer, worker]).then((res) => { 154 | clearTimeout(timeoutid); 155 | return res; 156 | }) 157 | : worker 158 | ).then((resp) => events.onResponse(resp)); 159 | } 160 | 161 | const http = {}; 162 | methods.forEach( 163 | (method) => 164 | (http[method.toLowerCase()] = (options) => send(method, options)), 165 | ); 166 | return http; 167 | } 168 | 169 | function API(name = 'untitled', debug = false) { 170 | const { isQX, isLoon, isSurge, isNode, isJSBox, isScriptable } = ENV(); 171 | return new (class { 172 | constructor(name, debug) { 173 | this.name = name; 174 | this.debug = debug; 175 | 176 | this.http = HTTP(); 177 | this.env = ENV(); 178 | 179 | this.node = (() => { 180 | if (isNode) { 181 | const fs = require('fs'); 182 | 183 | return { 184 | fs, 185 | }; 186 | } else { 187 | return null; 188 | } 189 | })(); 190 | this.initCache(); 191 | 192 | const delay = (t, v) => 193 | new Promise(function (resolve) { 194 | setTimeout(resolve.bind(null, v), t); 195 | }); 196 | 197 | Promise.prototype.delay = function (t) { 198 | return this.then(function (v) { 199 | return delay(t, v); 200 | }); 201 | }; 202 | } 203 | 204 | // persistance 205 | 206 | // initialize cache 207 | initCache() { 208 | if (isQX) this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}'); 209 | if (isLoon || isSurge) 210 | this.cache = JSON.parse($persistentStore.read(this.name) || '{}'); 211 | 212 | if (isNode) { 213 | // create a json for root cache 214 | let fpath = 'root.json'; 215 | if (!this.node.fs.existsSync(fpath)) { 216 | this.node.fs.writeFileSync( 217 | fpath, 218 | JSON.stringify({}), 219 | { flag: 'wx' }, 220 | (err) => console.log(err), 221 | ); 222 | } 223 | this.root = {}; 224 | 225 | // create a json file with the given name if not exists 226 | fpath = `${this.name}.json`; 227 | if (!this.node.fs.existsSync(fpath)) { 228 | this.node.fs.writeFileSync( 229 | fpath, 230 | JSON.stringify({}), 231 | { flag: 'wx' }, 232 | (err) => console.log(err), 233 | ); 234 | this.cache = {}; 235 | } else { 236 | this.cache = JSON.parse( 237 | this.node.fs.readFileSync(`${this.name}.json`), 238 | ); 239 | } 240 | } 241 | } 242 | 243 | // store cache 244 | persistCache() { 245 | const data = JSON.stringify(this.cache); 246 | if (isQX) $prefs.setValueForKey(data, this.name); 247 | if (isLoon || isSurge) $persistentStore.write(data, this.name); 248 | if (isNode) { 249 | this.node.fs.writeFileSync( 250 | `${this.name}.json`, 251 | data, 252 | { flag: 'w' }, 253 | (err) => console.log(err), 254 | ); 255 | this.node.fs.writeFileSync( 256 | 'root.json', 257 | JSON.stringify(this.root), 258 | { flag: 'w' }, 259 | (err) => console.log(err), 260 | ); 261 | } 262 | } 263 | 264 | write(data, key) { 265 | this.log(`SET ${key}`); 266 | if (key.indexOf('#') !== -1) { 267 | key = key.substr(1); 268 | if (isSurge || isLoon) { 269 | return $persistentStore.write(data, key); 270 | } 271 | if (isQX) { 272 | return $prefs.setValueForKey(data, key); 273 | } 274 | if (isNode) { 275 | this.root[key] = data; 276 | } 277 | } else { 278 | this.cache[key] = data; 279 | } 280 | this.persistCache(); 281 | } 282 | 283 | read(key) { 284 | this.log(`READ ${key}`); 285 | if (key.indexOf('#') !== -1) { 286 | key = key.substr(1); 287 | if (isSurge || isLoon) { 288 | return $persistentStore.read(key); 289 | } 290 | if (isQX) { 291 | return $prefs.valueForKey(key); 292 | } 293 | if (isNode) { 294 | return this.root[key]; 295 | } 296 | } else { 297 | return this.cache[key]; 298 | } 299 | } 300 | 301 | delete(key) { 302 | this.log(`DELETE ${key}`); 303 | if (key.indexOf('#') !== -1) { 304 | key = key.substr(1); 305 | if (isSurge || isLoon) { 306 | return $persistentStore.write(null, key); 307 | } 308 | if (isQX) { 309 | return $prefs.removeValueForKey(key); 310 | } 311 | if (isNode) { 312 | delete this.root[key]; 313 | } 314 | } else { 315 | delete this.cache[key]; 316 | } 317 | this.persistCache(); 318 | } 319 | 320 | // notification 321 | notify(title, subtitle = '', content = '', options = {}) { 322 | const openURL = options['open-url']; 323 | const mediaURL = options['media-url']; 324 | 325 | if (isQX) $notify(title, subtitle, content, options); 326 | if (isSurge) { 327 | $notification.post( 328 | title, 329 | subtitle, 330 | content + `${mediaURL ? '\n多媒体:' + mediaURL : ''}`, 331 | { 332 | url: openURL, 333 | }, 334 | ); 335 | } 336 | if (isLoon) { 337 | let opts = {}; 338 | if (openURL) opts['openUrl'] = openURL; 339 | if (mediaURL) opts['mediaUrl'] = mediaURL; 340 | if (JSON.stringify(opts) == '{}') { 341 | $notification.post(title, subtitle, content); 342 | } else { 343 | $notification.post(title, subtitle, content, opts); 344 | } 345 | } 346 | if (isNode || isScriptable) { 347 | const content_ = 348 | content + 349 | (openURL ? `\n点击跳转: ${openURL}` : '') + 350 | (mediaURL ? `\n多媒体: ${mediaURL}` : ''); 351 | if (isJSBox) { 352 | const push = require('push'); 353 | push.schedule({ 354 | title: title, 355 | body: (subtitle ? subtitle + '\n' : '') + content_, 356 | }); 357 | } else { 358 | console.log(`${title}\n${subtitle}\n${content_}\n\n`); 359 | } 360 | } 361 | } 362 | 363 | // other helper functions 364 | log(msg) { 365 | if (this.debug) console.log(msg); 366 | } 367 | 368 | info(msg) { 369 | console.log(msg); 370 | } 371 | 372 | error(msg) { 373 | console.log('ERROR: ' + msg); 374 | } 375 | 376 | wait(millisec) { 377 | return new Promise((resolve) => setTimeout(resolve, millisec)); 378 | } 379 | 380 | done(value = {}) { 381 | if (isQX || isLoon || isSurge) { 382 | $done(value); 383 | } else if (isNode && !isJSBox) { 384 | if (typeof $context !== 'undefined') { 385 | $context.headers = value.headers; 386 | $context.statusCode = value.statusCode; 387 | $context.body = value.body; 388 | } 389 | } 390 | } 391 | })(name, debug); 392 | } 393 | -------------------------------------------------------------------------------- /scripts/weifeng.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 脚本名称:威锋论坛 4 | 更新时间:2022-11-15 5 | 脚本作者:@小白脸 6 | 使用方法:通过BoxJs配置威锋论坛账号和密码,脚本支持Quantumult X、Surge、Loon和青龙Node.js环境运行。环境变量:feng_username、feng_password 7 | BoxJs 订阅:https://raw.githubusercontent.com/FoKit/Scripts/main/boxjs/fokit.boxjs.json 8 | 9 | # Surge 10 | [Script] 11 | # > 威锋论坛签到+任务 12 | 威锋论坛 = type=cron,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/weifeng.js,cronexp=0 8 * * *,wake-system=1,timeout=15 13 | 14 | # Quantumult X 15 | [task_local] 16 | 0 8 * * * https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/weifeng.js, tag=威锋论坛, enabled=true 17 | 18 | */ 19 | 20 | const $ = new Env('威锋论坛'); 21 | const notify = $.isNode() ? require('./sendNotify') : ''; 22 | const userName = $.getdata('feng_username') || ($.isNode() ? process.env.feng_username : ''); 23 | const password = $.getdata('feng_password') || ($.isNode() ? process.env.feng_password : ''); 24 | 25 | //获取请求对象 26 | var person = new Person(); 27 | 28 | !(async () => { 29 | let singIn = await signin(); 30 | let Task = await task(); 31 | let inform = await infon(); 32 | let text = `${singIn},${Task}\n用户:${inform.userBaseInfo.userName} 等级:Lv${inform.userBaseInfo.level} 金币:${inform.weTicket} 注册:${inform.joinDays}天 连签:${inform.signInTotalCount}天`; 33 | $.msg($.name, ``, text) 34 | if ($.isNode()) await notify.sendNotify($.name, text + '\n'); 35 | })() 36 | .catch((e) => { 37 | $.log(`❌ ${$.name}, 出错了: ${e}`) 38 | }) 39 | .finally(() => { 40 | $.done(); 41 | }) 42 | 43 | function http_post(opt) { 44 | return new Promise(resolve => { 45 | $.post(opt, (err, resp, data) => { 46 | if (err || resp.status !== 200) { 47 | $.log(err) 48 | } else { 49 | resolve(data); 50 | } 51 | }) 52 | }) 53 | } 54 | 55 | function http_get(opt) { 56 | return new Promise(resolve => { 57 | $.get(opt, (err, resp, data) => { 58 | if (err || resp.status !== 200) { 59 | $.log(err) 60 | } else { 61 | resolve(data); 62 | } 63 | }) 64 | }) 65 | } 66 | 67 | async function ck() { 68 | if (!userName && !password) { 69 | $.msg($.name, '未配置账号密码,结束运行。') 70 | if ($.isNode()) await notify.sendNotify($.name, '未配置账号密码环境变量,结束运行。'); 71 | $.done(); 72 | } 73 | let k = person.pp('signins') 74 | let land = JSON.parse(await http_post(k)); 75 | if (land.status.code === 0) { 76 | let token = land.data.accessToken; 77 | // 存储token 78 | $.setdata(token, 'feng_token'); 79 | return token; 80 | } else { 81 | throw land.status.message 82 | } 83 | } 84 | 85 | async function signin() { 86 | // 获取token,如果没有则去请求,再存储token 87 | // Token开启全局访问 88 | Token = $.getdata('feng_token') || await ck(); 89 | let wf = person.pp('signin', Token); 90 | // 登录操作 91 | const data = await http_post(wf); 92 | let sig = JSON.parse(data); 93 | var code = `${sig.status.code}`.match(/\b(0|1021)\b/); 94 | if (code) { 95 | var _sign = sig.status.message; 96 | } else { 97 | $.msg(sig.status.message, '正在更新Token,请稍等。'); 98 | // token失效重新设置token为空,然后调用自己本身 99 | $.setdata('', 'feng_token') 100 | return signin(); 101 | } 102 | return _sign.replace(/.+/, 103 | (x) => { 104 | return x === 'success' ? '✅签到成功' : `⚠️${x}` 105 | } 106 | ) 107 | } 108 | 109 | async function infon() { 110 | let home = person.pp('homePageInfo', Token); 111 | let inform = JSON.parse(await http_get(home, 'get')).data; 112 | return inform 113 | } 114 | 115 | async function task() { 116 | let share = person.pp('share', Token); 117 | let publish = person.pp('publish', Token); 118 | for (var i = 0; i < 6; i++) { 119 | i === 5 ? await http_post(publish) 120 | : await http_post(share); 121 | } 122 | 123 | let Award = person.pp('award', Token); 124 | for (let i of ['13', '14']) { 125 | Award.body = `task=share&taskId=${i}`; 126 | console.log(JSON.parse(await http_post(Award)).status.message); 127 | } 128 | return '所有任务已完成,详情请查看日志。' 129 | } 130 | 131 | 132 | function Person() { 133 | 134 | this.running = `M0hhBBSkMGg71/hbUpHuOd4i4/1ZzT9LZbOzF+1dkKswn9Ib0qJkcOAnkXDwTcY4QJx6M5+lDT6y6+tQg6wGZoV/+zUcGczM3wEm0y0uB1naLlMjg+qumDkwYtey/XzovWzIs3eIwTcTTnlrMzlpB8oZ+kGBYiu9TOHfmLZUJ9jgW2FZ5c7W5ibg3uq606PxKmyVIqxJWAniJxdqEbf37601Ec031FSLZPN8TEPodEJkpkCbY6/QqD8LfQOtiipAAJi11HaK1yM78Wp+F31bPMK+YlwQS4NzibV0gA+SPp84ET23JxzgEELL/jZiAqeZMixKaHPp3clnAKf2CTYNnhQ1Y3PBcDbD4pZMVtUwRh9cMcWxFhct8T4/D+eO2/7IzJda8bwvy75AaDev3PtU2A==`; 135 | // 签到 136 | this.signin = { 137 | url: 'https://api.wfdata.club/v1/attendance/userSignIn', 138 | headers: { 'x-request-id': 'UUhIl4ogsHmoE6MZNCc99B1mIRrrNqjukn0zzekcN3un0vaaH7FNHvgXi3qDMO9D' } 139 | }; 140 | // 登陆 141 | this.signins = { 142 | url: 'https://api.wfdata.club/v1/auth/signin', 143 | headers: { 'x-request-id': ` /9ESQHOIeA8UQktLh6vDlMb+HHLyk+SJby4LdkK/iM0Pe68+gz9IvcQMwyk2MTDS` }, 144 | body: `account=${userName}&password=${password}` 145 | }; 146 | // 查看个人信息 147 | this.homePageInfo = { 148 | url: 'https://api.wfdata.club/v1/user/homePageInfo', 149 | headers: { 'x-request-id': 'ItdBL/7kPKkupGrKMKmKGedj/8Im0nDDPet4XuY92NjR83Ey/LnrfGe80vLHxfMV' } 150 | }; 151 | // 任务1 152 | this.share = { 153 | url: 'https://api.wfdata.club/v1/thread/share', 154 | headers: { 'x-request-id': `/R2ZHSmGTziyeazINmiY5VpSJVIoIHvZkBKufI5ujVht1gLM8W/Z3CGaxTVWEkYf` }, 155 | body: 'tid=1' 156 | }; 157 | // 任务2 158 | this.publish = { 159 | url: 'https://api.wfdata.club/v2/thread/publish', 160 | headers: { 161 | 'X-Request-id': 162 | 'D+6OXXW6hNqyUIwPjEYG6V3esfq6wfreTqLog2u0F1AYkzhRIM29erRFGwNsFA6q' 163 | }, 164 | body: `{"threadType":"Dynamic","content":"
开心<\/p>"}` 165 | }; 166 | // 交任务 167 | this.award = { 168 | url: 'https://api.wfdata.club/v1/task/award', 169 | headers: { 'x-request-id': 'LllYnOgUbzlLSTFSk9uUB2H7pL6/Jk3q7lSHWZKVWJqme6W0syRx7MFWGpkQN35f' } 170 | } 171 | this.pp = (x, y) => { 172 | this[x]['headers']['x-running-env'] = this.running; 173 | if (y) this[x]['headers']['x-access-token'] = y; 174 | return this[x]; 175 | } 176 | } 177 | 178 | 179 | // prettier-ignore 180 | 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}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}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,a]=i.split("@"),n={url:`http://${a}/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),a=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(a);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?s.status:s.statusCode,s.status=s.statusCode),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&&t.error||"UndefinedError"));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:a}=t,n=s.decode(a,this.encoding);e(null,{status:i,statusCode:r,headers:o,rawBody:a,body:n},n)},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?s.status:s.statusCode,s.status=s.statusCode),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&&t.error||"UndefinedError"));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:a}=t,n=i.decode(a,this.encoding);e(null,{status:s,statusCode:r,headers:o,rawBody:a,body:n},n)},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,i=t["update-pasteboard"]||t.updatePasteboard;return{"open-url":e,"media-url":s,"update-pasteboard":i}}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):this.isNode()&&process.exit(1)}}(t,e)} 181 | -------------------------------------------------------------------------------- /scripts/ql_env_sync.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 脚本名称:青龙变量同步 3 | * 脚本说明:用于青龙多容器同步环境变量,执行后会清空本地所有JD_COOKIE变量,并获取远程容器所有JD_COOKIE变量写入本地 4 | * 环境变量:env_sync_ip / env_sync_id / env_sync_key / env_sync_username / env_sync_password / AUTH_CONFIG (账号密码/密钥登录方式二选一) 5 | * 更新时间:2023/06/27 09:54 6 | * 脚本作者:@Fokit_Orz 7 | */ 8 | 9 | const $ = new Env('青龙变量同步') 10 | const fs = require('fs') 11 | const got = require('got') 12 | const path = require('path') 13 | const api = got.extend() 14 | const ql_host = 'http://localhost:5700' 15 | const authConfig = process.env.AUTH_CONFIG || 'data/config/auth.json' 16 | const authFile = path.join(path.resolve(__dirname, '/ql/'), authConfig) 17 | 18 | const env_sync_ip = process.env.env_sync_ip 19 | const env_sync_id = process.env.env_sync_id 20 | const env_sync_key = process.env.env_sync_key 21 | const env_sync_username = process.env.env_sync_username 22 | const env_sync_password = process.env.env_sync_password 23 | 24 | !(async () => { 25 | // 获取Token 26 | console.log(`\n获取本地Token...`) 27 | const ql_token = await getToken() // 本地token 28 | const remote_token = await get_ql_token() // 远程token 29 | if (!$.api_type) console.log(`❌ 结束运行。`) && process.exit(0); 30 | 31 | // 获取本地变量 32 | console.log(`\n开始获取本地变量...`) 33 | const ql_cookies = await get_ql_JDCookie(ql_host, ql_token) 34 | console.log(`本地共有 ${ql_cookies.length} 个 JD_COOKIE 环境变量\n`) 35 | 36 | // 清空本地变量 37 | const del_arr = ql_cookies.map(cookies => cookies.id) 38 | if (del_arr.length > 0) { 39 | console.log(`开始清空本地变量...`) 40 | await ql_delEnv(ql_token, del_arr) 41 | console.log(`已清空本地环境变量\n`) 42 | } 43 | 44 | // 获取远程变量 45 | console.log(`开始获取远程变量...`) 46 | const remote_cookies = await get_ql_JDCookie(env_sync_ip, remote_token, $.api_type) 47 | const cookies = remote_cookies.map((item) => { 48 | if (item.status == 0) { // 只获取启用变量 49 | return { name: "JD_COOKIE", value: item.value } 50 | } 51 | }) 52 | const jd_cookies = cookies.filter(Boolean); // 过滤 undefined 53 | console.log(`成功获取到 ${cookies.length} 个 JD_COOKIE 变量,有效 ${jd_cookies.length} 个,无效 ${cookies.length - jd_cookies.length} 个\n`) 54 | 55 | // 写入本地环境变量 56 | console.log(`开始写入环境变量...`) 57 | await ql_addEnv(ql_token, jd_cookies) 58 | console.log(`成功写入 ${jd_cookies.length} 个环境变量。`) 59 | })().catch((e) => { 60 | console.log('', `❌ 失败! 原因: ${e}!`, '') 61 | }) 62 | 63 | 64 | /** 65 | * 删除本地环境变量 66 | * @param {*} json [123, 456] 67 | * @returns {object} 68 | */ 69 | async function ql_delEnv(token, json) { 70 | try { 71 | return await api({ 72 | method: 'DELETE', 73 | url: `${ql_host}/api/envs`, 74 | params: { t: Date.now() }, 75 | body: JSON.stringify(json), 76 | headers: { 77 | Accept: 'application/json', 78 | authorization: `Bearer ${token}`, 79 | 'Content-Type': 'application/json;charset=UTF-8', 80 | }, 81 | }).json() 82 | } catch (e) { 83 | console.log(e) 84 | } 85 | } 86 | 87 | /** 88 | * 获取环境变量 89 | * @param {*} searchValue 90 | * @returns {object} 91 | */ 92 | async function get_ql_JDCookie(host, token, apiType = 'api', searchValue = 'JD_COOKIE') { 93 | try { 94 | const body = await api({ 95 | url: `${host}/${apiType}/envs`, 96 | searchParams: { 97 | searchValue, 98 | t: Date.now(), 99 | }, 100 | headers: { 101 | Accept: 'application/json', 102 | authorization: `Bearer ${token}`, 103 | }, 104 | }).json() 105 | return body.data 106 | } catch (e) { 107 | console.log(e) 108 | } 109 | } 110 | 111 | /** 112 | * 新增本地环境变量 113 | * @param {*} json 114 | * @returns {object} 115 | */ 116 | async function ql_addEnv(token, json) { 117 | try { 118 | return await api({ 119 | method: 'post', 120 | url: `${ql_host}/api/envs`, 121 | params: { t: Date.now() }, 122 | body: JSON.stringify(json), 123 | headers: { 124 | Accept: 'application/json', 125 | authorization: `Bearer ${token}`, 126 | 'Content-Type': 'application/json;charset=UTF-8', 127 | }, 128 | }).json() 129 | } catch (e) { 130 | console.log(e) 131 | } 132 | } 133 | 134 | /** 135 | * 获取本地token 136 | * @returns {string} 137 | */ 138 | async function getToken() { 139 | const res = getFileContentByName(authFile) 140 | if (res) { 141 | const authConfig = JSON.parse(res) 142 | console.log(`获取成功\n`) 143 | return authConfig.token 144 | } else { 145 | return '' 146 | } 147 | } 148 | 149 | /** 150 | * 获取文件内容 151 | * @param fileName 文件路径 152 | * @returns {string} 153 | */ 154 | function getFileContentByName(fileName) { 155 | if (fs.existsSync(fileName)) { 156 | return fs.readFileSync(fileName, 'utf8') 157 | } 158 | return '' 159 | } 160 | 161 | /** 162 | * 获取远程Token 163 | */ 164 | async function get_ql_token() { 165 | console.log(`获取远程Token...\n${env_sync_ip}`) 166 | if (env_sync_username && env_sync_password) { 167 | let response = await api({ 168 | method: 'post', 169 | url: `${env_sync_ip}/api/user/login`, 170 | // params: { t: Date.now() }, 171 | body: `username: ${env_sync_username}, password: ${env_sync_password}`, 172 | headers: { 173 | Accept: `application/json;charset=UTF-8`, 174 | }, 175 | }).json() 176 | // console.log(response) 177 | if (response.code === 200) { 178 | $.api_type = 'api'; 179 | // $.remote_token = `Bearer ${response.data.token} `; 180 | $.log(`登陆成功:${response.data.lastaddr} `); 181 | $.log(`ip:${response.data.lastip} `); 182 | return response.data.token 183 | } else { 184 | $.log(response); 185 | $.log(`登陆失败:${response.message} `); 186 | return '' 187 | } 188 | } else if (env_sync_id && env_sync_key) { 189 | let response = await api({ 190 | method: 'get', 191 | url: `${env_sync_ip}/open/auth/token?client_id=${env_sync_id}&client_secret=${env_sync_key}`, 192 | headers: { 193 | Accept: `application/json;charset=UTF-8`, 194 | }, 195 | }).json() 196 | // console.log(response) 197 | if (response.code === 200) { 198 | $.api_type = 'open'; 199 | // $.remote_token = `Bearer ${ response.data.token } `; 200 | $.log(`登陆成功`); 201 | return response.data.token 202 | } else { 203 | $.log(response); 204 | $.log(`登陆失败:${response.message} `); 205 | return '' 206 | } 207 | } 208 | } 209 | 210 | 211 | // prettier-ignore 212 | 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 } isStash() { return "undefined" != typeof $environment && $environment["stash-version"] } 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, a] = i.split("@"), n = { url: `http://${a}/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), a = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(a); 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 ? s.status : s.statusCode, s.status = s.statusCode), 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 && t.error || "UndefinedError")); 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: a } = t, n = s.decode(a, this.encoding); e(null, { status: i, statusCode: r, headers: o, rawBody: a, body: n }, n) }, 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 ? s.status : s.statusCode, s.status = s.statusCode), 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 && t.error || "UndefinedError")); 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: a } = t, n = i.decode(a, this.encoding); e(null, { status: s, statusCode: r, headers: o, rawBody: a, body: n }, n) }, 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, i = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": i } } 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) : this.isNode() && process.exit(1) } }(t, e) } 213 | -------------------------------------------------------------------------------- /scripts/reRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 脚本名称:请求重放 3 | * 脚本说明:此脚本用于调试 Api 接口服务 4 | * 更新时间:2024-05-08 5 | * BoxJs地址:https://raw.githubusercontent.com/FoKit/Scripts/main/boxjs/fokit.boxjs.json 6 | 7 | -------------- Quantumult X 配置 -------------- 8 | 9 | [MITM] 10 | hostname = m.360.cn 11 | 12 | [rewrite_local] 13 | ^https:\/\/m\.360\.cn url script-response-body https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/reRequest.js 14 | 15 | [task_local] 16 | 7 7 7 7 7 https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/reRequest.js, tag=请求重放, img-url=https://github.com/FoKit/Scripts/blob/main/images/debug.png?raw=true, enabled=true 17 | 18 | */ 19 | 20 | const $ = new Env('请求重放'); 21 | const reRequest = $.getjson('reRequest') || {}; 22 | const _options = $.toObj(reRequest['options']) || {}; 23 | const _count = reRequest['count'] || 200; // 重试次数 24 | const _wait = reRequest['wait'] || 50; // 间隔时间 ms 25 | const _timeout = reRequest['timeout'] || 5e3; // 超时时间 ms 26 | $.is_debug = $.getdata('is_debug') || 'false'; // 调试模式 27 | 28 | if (typeof $request !== `undefined`) { 29 | // 获取用户数据 30 | getRequest(); 31 | $.done(); 32 | } else { 33 | // 脚本执行入口 34 | !(async () => { 35 | await main(); // 主函数 36 | })() 37 | .catch((e) => $.messages.push(e.message || e) && $.logErr(e)) 38 | .finally(async () => { 39 | $.done(); 40 | }) 41 | } 42 | 43 | // 主函数 44 | async function main() { 45 | if (!_options['url']) { 46 | _options['url'] = /url\s?=\s?`(.+)?`/.exec(reRequest['options'])?.[1]; 47 | _options['method'] = /method\s?=\s?`(.+)?`/.exec(reRequest['options'])?.[1]; 48 | _options['headers'] = /headers\s?=\s?(\{[\S\s]+?\})/.exec(reRequest['options'])?.[1] || {}; 49 | _options['body'] = /body\s?=\s?`(.+)?`/.exec(reRequest['options'])?.[1] || ''; 50 | } 51 | if (!_options['url']) throw new Error('请求 url 不存在, 结束运行'); 52 | if (_options['method'].toLowerCase() !== 'post') delete _options['body']; 53 | _options['timeout'] = _timeout; 54 | for (let i = 0; i < _count; i++) { 55 | try { 56 | $.log(`\n ▸ 第[${i + 1}/${_count}]次请求 - ${_options['method']}:\n`); 57 | let result = await Request(_options, _options['method']); 58 | $.log($.toStr(result)); 59 | } catch (err) { 60 | $.logErr(err); 61 | } 62 | await $.wait(_wait); 63 | $.log(`等待 ${_wait} ms\n`); 64 | } 65 | } 66 | 67 | // 获取请求数据 68 | function getRequest() { 69 | try { 70 | debug($request); 71 | _options['url'] = $request.url; 72 | _options['method'] = $request.method; 73 | _options['headers'] = ObjectKeys2LowerCase($request.headers); 74 | if ($request?.body) { 75 | _options['body'] = $request.body; 76 | } 77 | $.setdata($.toStr(_options), '@reRequest.options'); 78 | $.msg($.name, ``, `请求重放信息获取成功 🎉`); 79 | } catch (err) { 80 | $.logErr(err); 81 | } 82 | } 83 | 84 | 85 | /** 86 | * 请求函数二次封装 87 | * @param {(object|string)} options - 构造请求内容,可传入对象或 Url * 88 | * @param {string} method - 请求方式 get / post 等,默认自动判断 89 | * @param {boolean} onlyBody 仅返回 body 内容,默认为 true 90 | * @returns {(object|string)} 自动根据内容返回 JSON 对象或字符串 91 | */ 92 | async function Request(options, method, onlyBody = true) { 93 | try { 94 | options = options.url ? options : { url: options }; 95 | method = method || ('body' in options ? method = 'post' : method = 'get'); 96 | const _timeout = options?.timeout || 15e3; 97 | const _http = [ 98 | new Promise((_, reject) => setTimeout(() => reject(new Error(`❌ 请求超时: ${options['url']}`)), _timeout)), 99 | new Promise((resolve, reject) => { 100 | debug(options, '[Request]'); 101 | $.http[method.toLowerCase()](options) 102 | .then((response) => { 103 | debug(response, '[Response]'); 104 | let res = onlyBody ? response.body : response; 105 | res = $.toObj(res) || res; 106 | resolve(res); 107 | }) 108 | .catch((err) => reject(new Error(err))); 109 | }) 110 | ]; 111 | return await Promise.race(_http); 112 | } catch (err) { 113 | $.logErr(err); 114 | } 115 | } 116 | 117 | /** 118 | * 对象属性转小写 119 | * @param {object} obj - 传入 $request.headers 120 | * @returns {object} 返回转换后的对象 121 | */ 122 | function ObjectKeys2LowerCase(obj) { 123 | const _lower = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])) 124 | return new Proxy(_lower, { 125 | get: function (target, propKey, receiver) { 126 | return Reflect.get(target, propKey.toLowerCase(), receiver) 127 | }, 128 | set: function (target, propKey, value, receiver) { 129 | return Reflect.set(target, propKey.toLowerCase(), value, receiver) 130 | } 131 | }) 132 | } 133 | 134 | /** 135 | * DEBUG 136 | * @param {*} content - 传入内容 137 | * @param {*} title - 标题 138 | */ 139 | function debug(content, title = "debug") { 140 | let start = `\n----- ${title} -----\n`; 141 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 142 | if ($.is_debug === 'true') { 143 | if (typeof content == "string") { 144 | $.log(start + content + end); 145 | } else if (typeof content == "object") { 146 | $.log(start + $.toStr(content) + end); 147 | } 148 | } 149 | } 150 | 151 | // prettier-ignore 152 | 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, o) => { 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.logLevels = { debug: 0, info: 1, warn: 2, error: 3 }, this.logLevelPrefixs = { debug: "[DEBUG] ", info: "[INFO] ", warn: "[WARN] ", error: "[ERROR] " }, this.logLevel = "info", 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; 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, 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 o = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); o = o ? 1 * o : 20, o = e && e.timeout ? e.timeout : o; const [r, a] = i.split("@"), n = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: o }, headers: { "X-Key": r, Accept: "*/*" }, timeout: o }; 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), o = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, o) : i ? this.fs.writeFileSync(e, o) : this.fs.writeFileSync(t, o) } } lodash_get(t, e, s) { const i = e.replace(/\[(\d+)\]/g, ".$1").split("."); let o = t; for (const t of i) if (o = Object(o)[t], void 0 === o) return s; return o } lodash_set(t, e, s) { return Object(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), o = s ? this.getval(s) : ""; if (o) try { const t = JSON.parse(o); e = t ? this.lodash_get(t, i, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, i, o] = /^@(.*?)\.(.*?)$/.exec(e), r = this.getval(i), a = i ? "null" === r ? null : r || "{}" : "{}"; try { const e = JSON.parse(a); this.lodash_set(e, o, t), s = this.setval(JSON.stringify(e), i) } catch (e) { const r = {}; this.lodash_set(r, o, t), s = this.setval(JSON.stringify(r), i) } } 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 : {}, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.cookie && 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, i) => { !t && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, i) })); 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: i, headers: o, body: r, bodyBytes: a } = t; e(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (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: i, statusCode: o, headers: r, rawBody: a } = t, n = s.decode(a, this.encoding); e(null, { status: i, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (t => { const { message: i, response: o } = t; e(i, o, o && s.decode(o.rawBody, this.encoding)) })); break } } 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, i) => { !t && s && (s.body = i, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, i) })); 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: i, headers: o, body: r, bodyBytes: a } = t; e(null, { status: s, statusCode: i, headers: o, body: r, bodyBytes: a }, r, a) }), (t => e(t && t.error || "UndefinedError"))); break; case "Node.js": let i = require("iconv-lite"); this.initGotEnv(t); const { url: o, ...r } = t; this.got[s](o, r).then((t => { const { statusCode: s, statusCode: o, headers: r, rawBody: a } = t, n = i.decode(a, this.encoding); e(null, { status: s, statusCode: o, headers: r, rawBody: a, body: n }, n) }), (t => { const { message: s, response: o } = t; e(s, o, o && i.decode(o.rawBody, this.encoding)) })); break } } 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 } queryStr(t) { let e = ""; for (const s in t) { let i = t[s]; null != i && "" !== i && ("object" == typeof i && (i = JSON.stringify(i)), e += `${s}=${i}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", i = "", o = {}) { const r = t => { const { $open: e, $copy: s, $media: i, $mediaMime: o } = 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: { const r = {}; let a = t.openUrl || t.url || t["open-url"] || e; a && Object.assign(r, { action: "open-url", url: a }); let n = t["update-pasteboard"] || t.updatePasteboard || s; if (n && Object.assign(r, { action: "clipboard", text: n }), i) { let t, e, s; if (i.startsWith("http")) t = i; else if (i.startsWith("data:")) { const [t] = i.split(";"), [, o] = i.split(","); e = o, s = t.replace("data:", "") } else { e = i, s = (t => { const e = { JVBERi0: "application/pdf", R0lGODdh: "image/gif", R0lGODlh: "image/gif", iVBORw0KGgo: "image/png", "/9j/": "image/jpg" }; for (var s in e) if (0 === t.indexOf(s)) return e[s]; return null })(i) } Object.assign(r, { "media-url": t, "media-base64": e, "media-base64-mime": o ?? s }) } return Object.assign(r, { "auto-dismiss": t["auto-dismiss"], sound: t.sound }), r } case "Loon": { const s = {}; let o = t.openUrl || t.url || t["open-url"] || e; o && Object.assign(s, { openUrl: o }); let r = t.mediaUrl || t["media-url"]; return i?.startsWith("http") && (r = i), r && Object.assign(s, { mediaUrl: r }), console.log(JSON.stringify(s)), s } case "Quantumult X": { const o = {}; let r = t["open-url"] || t.url || t.openUrl || e; r && Object.assign(o, { "open-url": r }); let a = t["media-url"] || t.mediaUrl; i?.startsWith("http") && (a = i), a && Object.assign(o, { "media-url": a }); let n = t["update-pasteboard"] || t.updatePasteboard || s; return n && Object.assign(o, { "update-pasteboard": n }), console.log(JSON.stringify(o)), o } 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, i, r(o)); break; case "Quantumult X": $notify(e, s, i, r(o)); break; case "Node.js": break }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), i && t.push(i), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } debug(...t) { this.logLevels[this.logLevel] <= this.logLevels.debug && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.debug}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } info(...t) { this.logLevels[this.logLevel] <= this.logLevels.info && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.info}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } warn(...t) { this.logLevels[this.logLevel] <= this.logLevels.warn && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.warn}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } error(...t) { this.logLevels[this.logLevel] <= this.logLevels.error && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.error}${t.map((t => t ?? String(t))).join(this.logSeparator)}`)) } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.map((t => t ?? String(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); break } } 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) } 153 | -------------------------------------------------------------------------------- /scripts/jd_cookie_search.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Author: 2Ya 4 | Github: https://github.com/domping 5 | ScriptName:京东 ck 多账号备注 + 搜索 6 | ================================== 7 | 给京东账号添加一个备注吧 O(∩_∩)O哈哈~ (适合账号多账号昵称混乱的用户) 8 | ================================== 9 | 使用方法: 10 | 1.添加 boxjs 订阅:https://raw.githubusercontent.com/dompling/Script/master/dompling.boxjs.json 11 | 2.在应用中找到 dompling -> 京东账号 ck 检索 12 | 3.点击右上角运行按钮初始化京东 ck 数据 13 | 4.初始完成之后,给各个账号添加备注就能愉快的搜索你的京东 ck 了。 14 | 5.搜索方式:设置关键字 下标(数组下标从 0 开始)、username(京东 ck 的 pin)、nickname(给京东账号设置的备注昵称), status(正常|未登录) 15 | 搜索示例:0,2Y,正常 16 | 返回结果:返回下标为 0 的,返回 2Y (username|nickname),返回正常状态的 17 | task : 每日九点检查 ck 过期状态 18 | 0 9 * * * https://raw.githubusercontent.com/dompling/Script/master/jd/jd_cookie_search.js 19 | 20 | */ 21 | const noTitle = '登陆助手初始化' 22 | const $ = new API('jd_ck_remark') 23 | $.msg = '' 24 | const APIKey = 'CookiesJD' 25 | const CacheKey = `#${APIKey}` 26 | const remark_key = `remark` 27 | const searchKey = 'keyword' 28 | const keyword = ($.read(searchKey) || '').split(',') 29 | const cookiesRemark = JSON.parse($.read(remark_key) || '[]') 30 | const CookiesJD = JSON.parse($.read(CacheKey) || '[]') 31 | const CookieJD = $.read('#CookieJD') 32 | const CookieJD2 = $.read('#CookieJD2') 33 | const ckData = CookiesJD.map((item) => item.cookie) 34 | if (CookieJD) ckData.unshift(CookieJD) 35 | if (CookieJD2) ckData.unshift(CookieJD2) 36 | 37 | console.log('初始化备注开始') 38 | console.log(`=========== 检测到京东账号:【${ckData.length}】个 ===========`) 39 | 40 | const ckRemarkFormat = {} 41 | cookiesRemark.forEach((item) => { 42 | ckRemarkFormat[item.username] = item 43 | }) 44 | ;(async () => { 45 | const ckFormat = [] 46 | const notLogin = [] 47 | let ckIndex = 1 48 | for (const cookie of ckData) { 49 | let username = cookie.match(/pt_pin=(.+?);/)[1] 50 | username = decodeURIComponent(username) 51 | console.log('===================================') 52 | console.log(`检查开始:账号 ${username} 【登陆状态】`) 53 | const response = await isLogin(cookie) 54 | const status = response.retcode === '0' ? '正常' : '未登录' 55 | 56 | let avatar = '', 57 | nickname = '', 58 | isPlusVip = 0, 59 | beanNum = 0, 60 | fruit = '', 61 | jdPet = '', 62 | mobile = ckRemarkFormat[username] ? ckRemarkFormat[username].mobile : '' 63 | if (response.retcode === '0') { 64 | isPlusVip = response.data.userInfo.isPlusVip 65 | beanNum = response.data.assetInfo.beanNum 66 | avatar = response.data.userInfo.baseInfo.headImageUrl 67 | nickname = response.data.userInfo.baseInfo.nickname 68 | console.log('帐号昵称:' + nickname) 69 | // if (!mobile) mobile = await getPhoneNumber(cookie) // 手机号码 70 | // fruit = await getFruit(cookie) // 农场进度 71 | // jdPet = await PetRequest('energyCollect', cookie) // 萌宠进度 72 | } 73 | 74 | console.log(`检查结束:账号【${ckIndex}】 ${username}【${status}】`) 75 | console.log('===================================') 76 | let newRemark = 77 | nickname || 78 | (ckRemarkFormat[username] ? ckRemarkFormat[username].remark : '') 79 | 80 | const item = { 81 | index: ckIndex, 82 | username, 83 | nickname, 84 | qywxUserId: '', 85 | cardId: '', 86 | paymentCode: '', 87 | avatar, 88 | ...ckRemarkFormat[username], 89 | jdPet, 90 | fruit, 91 | beanNum, 92 | mobile, 93 | isPlusVip, 94 | status, 95 | remark: newRemark, 96 | } 97 | if (status === '未登录') notLogin.push(item) 98 | ckFormat.push(item) 99 | ckIndex++ 100 | } 101 | $.msg = '检索完成,所有账号状态正常!' 102 | console.log($.msg) 103 | if (notLogin.length) { 104 | console.log( 105 | `----------------未登录账号【${notLogin.length}】----------------` 106 | ) 107 | console.log( 108 | notLogin.map((item) => `${item.username}【${item.nickname}】`).join(`\n`) 109 | ) 110 | $.msg = `未登录账号:\n ${notLogin 111 | .map((item) => `账号【${item.index}】:${item.nickname || item.username}`) 112 | .join(`\n`)}` 113 | } 114 | $.write(JSON.stringify(ckFormat, null, `\t`), remark_key) 115 | console.log(`检测到${keyword.length - 1}个搜索条件:${keyword.join(',')}`) 116 | if (keyword && keyword[0]) { 117 | console.log('开始搜索中') 118 | const searchValue = ckFormat.filter((item, index) => { 119 | return ( 120 | keyword.indexOf(`${index}`) > -1 || 121 | keyword.indexOf(item.username) > -1 || 122 | keyword.indexOf(item.nickname) > -1 || 123 | keyword.indexOf(item.status) > -1 124 | ) 125 | }) 126 | if (searchValue.length) { 127 | $.msg = `已找到搜索结果:\n` 128 | searchValue.forEach((item) => { 129 | $.msg += `${item.nickname || item.username}:${item.mobile} 【${ 130 | item.status 131 | }】\n` 132 | }) 133 | } else { 134 | $.msg = '未找到相关 ck' 135 | } 136 | console.log($.msg) 137 | if ($.read('mute') !== 'true') { 138 | $.notify(noTitle, `关键字:${keyword}`, $.msg) 139 | } 140 | } else { 141 | if ($.read('mute') !== 'true') { 142 | $.notify(noTitle, ``, $.msg) 143 | } 144 | } 145 | })() 146 | .catch((e) => { 147 | console.log(e) 148 | }) 149 | .finally(() => { 150 | $.done() 151 | }) 152 | 153 | async function isLogin(Cookie) { 154 | const opt = { 155 | url: 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?sceneval=2&sceneval=2&g_login_type=1&g_ty=ls', 156 | headers: { 157 | cookie: Cookie, 158 | Referer: 'https://home.m.jd.com/', 159 | }, 160 | } 161 | return $.http.get(opt).then((response) => { 162 | try { 163 | return JSON.parse(response.body) 164 | } catch (e) { 165 | return {} 166 | } 167 | }) 168 | } 169 | 170 | async function getPhoneNumber(cookie) { 171 | const opt = { 172 | url: `https://crmsam.jd.com/union/bindingPhoneNumber`, 173 | headers: { 174 | cookie: cookie, 175 | }, 176 | } 177 | 178 | return $.http.get(opt).then((response) => { 179 | try { 180 | const data = JSON.parse(response.body) 181 | if (data.code === 200) return data.data 182 | return '' 183 | } catch (e) { 184 | return '' 185 | } 186 | }) 187 | } 188 | 189 | function taskPetUrl(function_id, body = {}, cookie) { 190 | body['version'] = 2 191 | body['channel'] = 'app' 192 | return { 193 | url: `https://api.m.jd.com/client.action?functionId=${function_id}`, 194 | body: `body=${escape( 195 | JSON.stringify(body) 196 | )}&appid=wh5&loginWQBiz=pet-town&clientVersion=9.0.4`, 197 | headers: { 198 | cookie: cookie, 199 | host: 'api.m.jd.com', 200 | 'content-type': 'application/x-www-form-urlencoded', 201 | }, 202 | } 203 | } 204 | 205 | async function PetRequest(function_id, cookie, body = {}) { 206 | return $.http.post(taskPetUrl(function_id, body, cookie)).then((response) => { 207 | try { 208 | const data = JSON.parse(response.body) 209 | if (data.code === '0') return data.result.medalPercent.toFixed(1) 210 | return '' 211 | } catch (e) { 212 | return '' 213 | } 214 | }) 215 | } 216 | 217 | async function getFruit(cookie) { 218 | const option = { 219 | url: `https://api.m.jd.com/client.action?functionId=initForFarm`, 220 | body: `body=${escape( 221 | JSON.stringify({ version: 4 }) 222 | )}&appid=wh5&clientVersion=9.1.0`, 223 | headers: { 224 | accept: '*/*', 225 | cookie: cookie, 226 | origin: 'https://home.m.jd.com', 227 | referer: 'https://home.m.jd.com/myJd/newhome.action', 228 | 229 | 'content-type': 'application/x-www-form-urlencoded', 230 | }, 231 | } 232 | 233 | return $.http.post(option).then((response) => { 234 | try { 235 | const data = JSON.parse(response.body) 236 | if (data.code === '0') 237 | return ( 238 | (data.farmUserPro.treeEnergy / data.farmUserPro.treeTotalEnergy) * 239 | 100 240 | ).toFixed(2) 241 | return '' 242 | } catch (e) { 243 | return '' 244 | } 245 | }) 246 | } 247 | 248 | function ENV() { 249 | const isQX = typeof $task !== 'undefined' 250 | const isLoon = typeof $loon !== 'undefined' 251 | const isSurge = typeof $httpClient !== 'undefined' && !isLoon 252 | const isJSBox = typeof require == 'function' && typeof $jsbox != 'undefined' 253 | const isNode = typeof require == 'function' && !isJSBox 254 | const isRequest = typeof $request !== 'undefined' 255 | const isScriptable = typeof importModule !== 'undefined' 256 | return { 257 | isQX, 258 | isLoon, 259 | isSurge, 260 | isNode, 261 | isJSBox, 262 | isRequest, 263 | isScriptable, 264 | } 265 | } 266 | 267 | function HTTP( 268 | defaultOptions = { 269 | baseURL: '', 270 | } 271 | ) { 272 | const { isQX, isLoon, isSurge, isScriptable, isNode } = ENV() 273 | const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'] 274 | const URL_REGEX = 275 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/ 276 | 277 | function send(method, options) { 278 | options = 279 | typeof options === 'string' 280 | ? { 281 | url: options, 282 | } 283 | : options 284 | const baseURL = defaultOptions.baseURL 285 | if (baseURL && !URL_REGEX.test(options.url || '')) { 286 | options.url = baseURL ? baseURL + options.url : options.url 287 | } 288 | if (options.body && options.headers && !options.headers['Content-Type']) { 289 | options.headers['Content-Type'] = 'application/x-www-form-urlencoded' 290 | } 291 | options = { 292 | ...defaultOptions, 293 | ...options, 294 | } 295 | const timeout = options.timeout 296 | const events = { 297 | ...{ 298 | onRequest: () => {}, 299 | onResponse: (resp) => resp, 300 | onTimeout: () => {}, 301 | }, 302 | ...options.events, 303 | } 304 | 305 | events.onRequest(method, options) 306 | 307 | let worker 308 | if (isQX) { 309 | worker = $task.fetch({ 310 | method, 311 | ...options, 312 | }) 313 | } else if (isLoon || isSurge || isNode) { 314 | worker = new Promise((resolve, reject) => { 315 | const request = isNode ? require('request') : $httpClient 316 | request[method.toLowerCase()](options, (err, response, body) => { 317 | if (err) reject(err) 318 | else 319 | resolve({ 320 | statusCode: response.status || response.statusCode, 321 | headers: response.headers, 322 | body, 323 | }) 324 | }) 325 | }) 326 | } else if (isScriptable) { 327 | const request = new Request(options.url) 328 | request.method = method 329 | request.headers = options.headers 330 | request.body = options.body 331 | worker = new Promise((resolve, reject) => { 332 | request 333 | .loadString() 334 | .then((body) => { 335 | resolve({ 336 | statusCode: request.response.statusCode, 337 | headers: request.response.headers, 338 | body, 339 | }) 340 | }) 341 | .catch((err) => reject(err)) 342 | }) 343 | } 344 | 345 | let timeoutid 346 | const timer = timeout 347 | ? new Promise((_, reject) => { 348 | timeoutid = setTimeout(() => { 349 | events.onTimeout() 350 | return reject( 351 | `${method} URL: ${options.url} exceeds the timeout ${timeout} ms` 352 | ) 353 | }, timeout) 354 | }) 355 | : null 356 | 357 | return ( 358 | timer 359 | ? Promise.race([timer, worker]).then((res) => { 360 | clearTimeout(timeoutid) 361 | return res 362 | }) 363 | : worker 364 | ).then((resp) => events.onResponse(resp)) 365 | } 366 | 367 | const http = {} 368 | methods.forEach( 369 | (method) => 370 | (http[method.toLowerCase()] = (options) => send(method, options)) 371 | ) 372 | return http 373 | } 374 | 375 | function API(name = 'untitled', debug = false) { 376 | const { isQX, isLoon, isSurge, isNode, isJSBox, isScriptable } = ENV() 377 | return new (class { 378 | constructor(name, debug) { 379 | this.name = name 380 | this.debug = debug 381 | 382 | this.http = HTTP() 383 | this.env = ENV() 384 | 385 | this.node = (() => { 386 | if (isNode) { 387 | const fs = require('fs') 388 | 389 | return { 390 | fs, 391 | } 392 | } else { 393 | return null 394 | } 395 | })() 396 | this.initCache() 397 | 398 | const delay = (t, v) => 399 | new Promise(function (resolve) { 400 | setTimeout(resolve.bind(null, v), t) 401 | }) 402 | 403 | Promise.prototype.delay = function (t) { 404 | return this.then(function (v) { 405 | return delay(t, v) 406 | }) 407 | } 408 | } 409 | 410 | // persistence 411 | // initialize cache 412 | initCache() { 413 | if (isQX) this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}') 414 | if (isLoon || isSurge) 415 | this.cache = JSON.parse($persistentStore.read(this.name) || '{}') 416 | 417 | if (isNode) { 418 | // create a json for root cache 419 | let fpath = 'root.json' 420 | if (!this.node.fs.existsSync(fpath)) { 421 | this.node.fs.writeFileSync( 422 | fpath, 423 | JSON.stringify({}), 424 | { 425 | flag: 'wx', 426 | }, 427 | (err) => console.log(err) 428 | ) 429 | } 430 | this.root = {} 431 | 432 | // create a json file with the given name if not exists 433 | fpath = `${this.name}.json` 434 | if (!this.node.fs.existsSync(fpath)) { 435 | this.node.fs.writeFileSync( 436 | fpath, 437 | JSON.stringify({}), 438 | { 439 | flag: 'wx', 440 | }, 441 | (err) => console.log(err) 442 | ) 443 | this.cache = {} 444 | } else { 445 | this.cache = JSON.parse( 446 | this.node.fs.readFileSync(`${this.name}.json`) 447 | ) 448 | } 449 | } 450 | } 451 | 452 | // store cache 453 | persistCache() { 454 | const data = JSON.stringify(this.cache, null, 2) 455 | if (isQX) $prefs.setValueForKey(data, this.name) 456 | if (isLoon || isSurge) $persistentStore.write(data, this.name) 457 | if (isNode) { 458 | this.node.fs.writeFileSync( 459 | `${this.name}.json`, 460 | data, 461 | { 462 | flag: 'w', 463 | }, 464 | (err) => console.log(err) 465 | ) 466 | this.node.fs.writeFileSync( 467 | 'root.json', 468 | JSON.stringify(this.root, null, 2), 469 | { 470 | flag: 'w', 471 | }, 472 | (err) => console.log(err) 473 | ) 474 | } 475 | } 476 | 477 | write(data, key) { 478 | this.log(`SET ${key}`) 479 | if (key.indexOf('#') !== -1) { 480 | key = key.substr(1) 481 | if (isSurge || isLoon) { 482 | return $persistentStore.write(data, key) 483 | } 484 | if (isQX) { 485 | return $prefs.setValueForKey(data, key) 486 | } 487 | if (isNode) { 488 | this.root[key] = data 489 | } 490 | } else { 491 | this.cache[key] = data 492 | } 493 | this.persistCache() 494 | } 495 | 496 | read(key) { 497 | this.log(`READ ${key}`) 498 | if (key.indexOf('#') !== -1) { 499 | key = key.substr(1) 500 | if (isSurge || isLoon) { 501 | return $persistentStore.read(key) 502 | } 503 | if (isQX) { 504 | return $prefs.valueForKey(key) 505 | } 506 | if (isNode) { 507 | return this.root[key] 508 | } 509 | } else { 510 | return this.cache[key] 511 | } 512 | } 513 | 514 | delete(key) { 515 | this.log(`DELETE ${key}`) 516 | if (key.indexOf('#') !== -1) { 517 | key = key.substr(1) 518 | if (isSurge || isLoon) { 519 | return $persistentStore.write(null, key) 520 | } 521 | if (isQX) { 522 | return $prefs.removeValueForKey(key) 523 | } 524 | if (isNode) { 525 | delete this.root[key] 526 | } 527 | } else { 528 | delete this.cache[key] 529 | } 530 | this.persistCache() 531 | } 532 | 533 | // notification 534 | notify(title, subtitle = '', content = '', options = {}) { 535 | const openURL = options['open-url'] 536 | const mediaURL = options['media-url'] 537 | 538 | if (isQX) $notify(title, subtitle, content, options) 539 | if (isSurge) { 540 | $notification.post( 541 | title, 542 | subtitle, 543 | content + `${mediaURL ? '\n多媒体:' + mediaURL : ''}`, 544 | { 545 | url: openURL, 546 | } 547 | ) 548 | } 549 | if (isLoon) { 550 | let opts = {} 551 | if (openURL) opts['openUrl'] = openURL 552 | if (mediaURL) opts['mediaUrl'] = mediaURL 553 | if (JSON.stringify(opts) === '{}') { 554 | $notification.post(title, subtitle, content) 555 | } else { 556 | $notification.post(title, subtitle, content, opts) 557 | } 558 | } 559 | if (isNode || isScriptable) { 560 | const content_ = 561 | content + 562 | (openURL ? `\n点击跳转: ${openURL}` : '') + 563 | (mediaURL ? `\n多媒体: ${mediaURL}` : '') 564 | if (isJSBox) { 565 | const push = require('push') 566 | push.schedule({ 567 | title: title, 568 | body: (subtitle ? subtitle + '\n' : '') + content_, 569 | }) 570 | } else { 571 | console.log(`${title}\n${subtitle}\n${content_}\n\n`) 572 | } 573 | } 574 | } 575 | 576 | // other helper functions 577 | log(msg) { 578 | if (this.debug) console.log(`[${this.name}] LOG: ${this.stringify(msg)}`) 579 | } 580 | 581 | info(msg) { 582 | console.log(`[${this.name}] INFO: ${this.stringify(msg)}`) 583 | } 584 | 585 | error(msg) { 586 | console.log(`[${this.name}] ERROR: ${this.stringify(msg)}`) 587 | } 588 | 589 | wait(millisec) { 590 | return new Promise((resolve) => setTimeout(resolve, millisec)) 591 | } 592 | 593 | done(value = {}) { 594 | if (isQX || isLoon || isSurge) { 595 | $done(value) 596 | } else if (isNode && !isJSBox) { 597 | if (typeof $context !== 'undefined') { 598 | $context.headers = value.headers 599 | $context.statusCode = value.statusCode 600 | $context.body = value.body 601 | } 602 | } 603 | } 604 | 605 | stringify(obj_or_str) { 606 | if (typeof obj_or_str === 'string' || obj_or_str instanceof String) 607 | return obj_or_str 608 | else 609 | try { 610 | return JSON.stringify(obj_or_str, null, 2) 611 | } catch (err) { 612 | return '[object Object]' 613 | } 614 | } 615 | })(name, debug) 616 | } 617 | -------------------------------------------------------------------------------- /scripts/hch_sign.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 脚本名称:花城汇小程序(广州) 3 | * 活动规则:每日签到可获得 10-30 积分,兼容 NE 和 Node.js 环境。 4 | * 更新时间:2025-08-23 5 | * 环境变量:HCH_USERID 6 | * BoxJs订阅:https://raw.githubusercontent.com/FoKit/Scripts/main/boxjs/fokit.boxjs.json 7 | 8 | ------------------ Surge 配置 ----------------- 9 | 10 | [MITM] 11 | hostname = member.mowgz.com 12 | 13 | [Script] 14 | 花城汇# = type=http-request,pattern=^https:\/\/member\.mowgz\.com\/bus\/getFunc\?busId=Member&methodId=getMember&getPram=gold%2Clevel&compId=\d+?&userId=\w+,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js 15 | 16 | 花城汇 = type=cron,cronexp=17 7 * * *,timeout=60,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js,script-update-interval=0 17 | 18 | ------------------ Loon 配置 ------------------ 19 | 20 | [MITM] 21 | hostname = member.mowgz.com 22 | 23 | [Script] 24 | http-request ^https:\/\/member\.mowgz\.com\/bus\/getFunc\?busId=Member&methodId=getMember&getPram=gold%2Clevel&compId=\d+?&userId=\w+ tag=花城汇#, script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js,requires-body=0 25 | 26 | cron "17 7 * * *" script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js,tag = 花城汇,enable=true 27 | 28 | -------------- Quantumult X 配置 -------------- 29 | 30 | [MITM] 31 | hostname = member.mowgz.com 32 | 33 | [rewrite_local] 34 | ^https:\/\/member\.mowgz\.com\/bus\/getFunc\?busId=Member&methodId=getMember&getPram=gold%2Clevel&compId=\d+?&userId=\w+ url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js 35 | 36 | [task_local] 37 | 17 7 * * * https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js, tag=花城汇, img-url=https://raw.githubusercontent.com/FoKit/Scripts/main/images/hch.png, enabled=true 38 | 39 | ------------------ Stash 配置 ----------------- 40 | 41 | cron: 42 | script: 43 | - name: 花城汇 44 | cron: '17 7 * * *' 45 | timeout: 60 46 | 47 | http: 48 | mitm: 49 | - "member.mowgz.com" 50 | script: 51 | - match: ^https:\/\/member\.mowgz\.com\/bus\/getFunc\?busId=Member&methodId=getMember&getPram=gold%2Clevel&compId=\d+?&userId=\w+ 52 | name: 花城汇 53 | type: request 54 | require-body: false 55 | 56 | script-providers: 57 | 花城汇: 58 | url: https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/hch_sign.js 59 | interval: 86400 60 | 61 | */ 62 | 63 | const $ = new Env('花城汇'); 64 | $.is_debug = ($.isNode() ? process.env['IS_DEDUG'] : $.getdata('is_debug')) || 'false'; // 调试模式 65 | $.userList = ($.isNode() ? process.env['HCH_USERID'] : $.getdata('@hch.userid')) || ""; // userIdArr 66 | $.messages = []; 67 | 68 | // 主函数 69 | async function main() { 70 | // 检查变量 71 | checkEnv(); 72 | for (let i = 0; i < $.userList.length; i++) { 73 | // 初始化 74 | $.user_id = $.userList[i]; 75 | $.log(`账号 ${i+1} 开始执行 \n`); 76 | 77 | // 查询信息 78 | await userCenter(); 79 | 80 | // 每日签到 81 | if ($.user_id) await sign(); 82 | } 83 | } 84 | 85 | // 每日签到 86 | async function sign() { 87 | let msg = ''; 88 | 89 | // 构造请求 90 | let opt = `https://member.mowgz.com/json/getData?appName=IAppUser&funcName=userCheck&data=%7B%22user_id%22%3A%22${$.user_id}%22%2C%22com_id%22%3A%2210018%22%2C%22check_id%22%3A%22%22%2C%22isCheckAuth%22%3Atrue%7D`; 91 | 92 | // 发起请求 93 | const result = await Request(opt); 94 | if (result?.status === '1') { 95 | msg = `🎉签到成功, ${result.error}\n`; 96 | } else if (result?.status === '0') { 97 | msg = `❌签到失败, ${result.error}\n`; 98 | } else { 99 | msg = `❌每日签到任务失败\n`; 100 | } 101 | $.messages.push(msg) && $.log(msg); 102 | } 103 | 104 | // 查询信息 105 | async function userCenter() { 106 | let msg = ''; 107 | 108 | // 构造请求 109 | let opt = `https://member.mowgz.com/bus/getFunc?busId=Member&methodId=getMember&getPram=gold%2Clevel&compId=10018&userId=${$.user_id}`; 110 | 111 | // 发起请求 112 | const result = await Request(opt); 113 | if (result?.errcode === 0 && result?.data?.lvInfo) { 114 | const { nickname, mobile, gold, lvInfo } = result.data; 115 | var user = hideSensitiveData(mobile, 3, 4) || $.user_id; 116 | msg = `账号: ${user} 昵称: ${nickname}\n积分: ${gold} 等级: ${lvInfo['lv_now']['name']}`; 117 | } else { 118 | msg = `账号: ${$.user_id}\n❌会员信息查询失败\n`; 119 | $.user_id = null; 120 | $.log($.toStr(result)); 121 | } 122 | $.messages.push(msg) && $.log(msg); 123 | } 124 | 125 | // 获取小程序数据 126 | function getUserId() { 127 | try { 128 | // 检查变量 129 | checkEnv(); 130 | if ($request.url.indexOf("methodId=getMember") > -1) { 131 | debug($request.url); 132 | // 从 $request.url 提取 userId 133 | $.user_id = $request.url.match(/userId=([\w]+)/)?.[1]; 134 | $.log(`获取到 userId: ${$.user_id}`); 135 | if (!$.userList.includes($.user_id)) { 136 | $.userList.push($.user_id); 137 | // 把获取到的数据写入代理缓存 138 | $.setdata($.toStr($.userList), '@hch.userid'); 139 | $.msg($.name, ``, `用户 userId 获取成功。 🎉`); 140 | } 141 | } 142 | } catch (err) { 143 | $.logErr(err); 144 | } 145 | } 146 | 147 | if (typeof $request !== `undefined`) { 148 | // 获取用户数据 149 | getUserId(); 150 | $.done(); 151 | } else { 152 | // 脚本执行入口 153 | !(async () => { 154 | await main(); // 主函数 155 | })() 156 | .catch((e) => $.messages.push(e.message || e) && $.logErr(e)) 157 | .finally(async () => { 158 | await sendMsg($.messages.join('\n')); // 推送通知 159 | $.done(); 160 | }) 161 | } 162 | 163 | // 检查变量 164 | function checkEnv() { 165 | // 把字符串转换为数组 166 | try { 167 | $.userList = JSON.parse($.userList); 168 | } catch (e) { 169 | $.userList = []; 170 | } 171 | if ($.userList.length) { 172 | $.log(`\n检测到 ${$.userList.length} 个账号变量\n`); 173 | } else { 174 | $.log(`\n找不到 userId, 请检查变量配置。❌\n`); 175 | } 176 | } 177 | 178 | /** 179 | * 数据脱敏 180 | * @param {string} string - 传入字符串 181 | * @param {number} head_length - 前缀展示字符数,默认为 2 182 | * @param {number} foot_length - 后缀展示字符数,默认为 2 183 | * @returns {string} - 返回字符串 184 | */ 185 | function hideSensitiveData(string, head_length = 2, foot_length = 2) { 186 | try { 187 | let star = ''; 188 | for (var i = 0; i < string.length - head_length - foot_length; i++) { 189 | star += '*'; 190 | } 191 | return string.substring(0, head_length) + star + string.substring(string.length - foot_length); 192 | } catch (e) { 193 | return string; 194 | } 195 | } 196 | 197 | /** 198 | * 请求函数二次封装 199 | * @param {(object|string)} options - 构造请求内容,可传入对象或 Url * 200 | * @param {string} method - 请求方式 get / post 等,默认自动判断 201 | * @param {boolean} onlyBody 仅返回 body 内容,默认为 true 202 | * @returns {(object|string)} 自动根据内容返回 JSON 对象或字符串 203 | */ 204 | async function Request(options, method, onlyBody = true) { 205 | try { 206 | options = options.url ? options : { url: options }; 207 | method = method || ('body' in options ? method = 'post' : method = 'get'); 208 | const _timeout = options?.timeout || 15e3; 209 | const _http = [ 210 | new Promise((_, reject) => setTimeout(() => reject(new Error(`❌ 请求超时: ${options['url']}`)), _timeout)), 211 | new Promise((resolve, reject) => { 212 | debug(options, '[Request]'); 213 | $.http[method.toLowerCase()](options) 214 | .then((response) => { 215 | debug(response, '[Response]'); 216 | let res = onlyBody ? response.body : response; 217 | res = $.toObj(res) || res; 218 | resolve(res); 219 | }) 220 | .catch((err) => reject(new Error(err))); 221 | }) 222 | ]; 223 | return await Promise.race(_http); 224 | } catch (err) { 225 | $.logErr(err); 226 | } 227 | } 228 | 229 | // 发送消息 230 | async function sendMsg(message) { 231 | if (!message) return; 232 | message = message.replace(/\n+$/, ''); // 清除末尾换行 233 | try { 234 | if ($.isNode()) { 235 | try { 236 | var notify = require('./sendNotify'); 237 | } catch (e) { 238 | var notify = require('./utils/sendNotify'); 239 | } 240 | await notify.sendNotify($.name, message); 241 | } else { 242 | $.msg($.name, '', message); 243 | } 244 | } catch (e) { 245 | $.log(`\n\n----- ${$.name} -----\n${message}`); 246 | } 247 | } 248 | 249 | /** 250 | * DEBUG 251 | * @param {*} content - 传入内容 252 | * @param {*} title - 标题 253 | */ 254 | function debug(content, title = "debug") { 255 | let start = `\n----- ${title} -----\n`; 256 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 257 | if ($.is_debug === 'true') { 258 | if (typeof content == "string") { 259 | $.log(start + content + end); 260 | } else if (typeof content == "object") { 261 | $.log(start + $.toStr(content) + end); 262 | } 263 | } 264 | } 265 | 266 | // prettier-ignore 267 | 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) } 268 | 269 | -------------------------------------------------------------------------------- /scripts/ThomasCook.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:复游会(托迈酷客) 3 | 活动规则:每日签到可获得积分 4 | 环境变量:ThomasCook_Cookie 5 | 使用说明:添加重写规则进入"复游会"小程序即可获取Cookie 6 | 更新记录:2023-11-10 新增每日浏览任务 7 | 2023-11-12 代码优化 8 | 2023-11-16 手机号脱敏 9 | 2024-04-09 修复变量作用域 10 | ==================================================================================================== 11 | 配置 (Surge) 12 | [MITM] 13 | hostname = apis.folidaymall.com 14 | 15 | [Script] 16 | 获取托迈酷客Cookie = type=http-request,pattern=^https:\/\/apis\.folidaymall\.com\/online\/capi\/uc\/getCount,requires-body=0,max-size=0,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ThomasCook.js 17 | 18 | 19 | 托迈酷客 = type=cron,cronexp=15 10 * * *,timeout=60,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ThomasCook.js,script-update-interval=0 20 | ---------------------------------------------------------------------------------------------------- 21 | 配置 (QuanX) 22 | [MITM] 23 | hostname = apis.folidaymall.com 24 | 25 | [rewrite_local] 26 | ^https:\/\/apis\.folidaymall\.com\/online\/capi\/uc\/getCount url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ThomasCook.js 27 | 28 | [task_local] 29 | 15 10 * * * https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/ThomasCook.js, tag=托迈酷客, enabled=true 30 | ==================================================================================================== 31 | */ 32 | 33 | const $ = new Env('复游会'); 34 | const ck_key = 'ThomasCook_Cookie'; 35 | const origin = 'https://apis.folidaymall.com'; 36 | 37 | // ---------------------- 一般不动变量区域 ---------------------- 38 | const Notify = 1; // 0 为关闭通知, 1 为打开通知, 默认为 1 39 | let cookie = '', cookiesArr = [], userIdx = 0; // Cookie 数据 40 | $.notifyMsg = []; // 为通知准备的空数组 41 | $.is_debug = ($.isNode() ? process.env.IS_DEDUG : $.getdata('is_debug')) || 'false'; // 调试模式 42 | 43 | // ---------------------- 自定义变量区域 ---------------------- 44 | 45 | 46 | // 统一管理 api 接口 47 | const Api = { 48 | "sign": { 49 | "name": "每日签到", 50 | "url": "/online/cms-api/sign/userSign", 51 | }, 52 | "relationList": { 53 | "name": "获取任务列表", 54 | "url": "/online/cms-api/activity/queryActivityTaskRelationList", 55 | }, 56 | "task": { 57 | "name": "领取任务", 58 | "url": "/online/cms-api/activity/receiveActivityTask", 59 | "body": `{"activityTaskId":"${$.activityTaskId}"}` 60 | }, 61 | "submit": { 62 | "name": "提交任务", 63 | "url": "/online/cms-api/activity/submitCompleteActivityTask", 64 | "body": `{"activityTaskId":"${$.activityTaskId}"}` 65 | }, 66 | "rewards": { 67 | "name": "领取奖励", 68 | "url": "/online/cms-api/activity/receiveActivityTaskRewards", 69 | "body": `{"activityTaskId":"${$.activityTaskId}","activityTaskRelationId":"${$.activityTaskRelationId}"}` 70 | } 71 | } 72 | 73 | // 获取 Cookie 74 | function GetCookie() { 75 | if ($request && $request.url.indexOf("getCount") > -1 && $request.headers.Authorization) { 76 | cookie = $request.headers.Authorization; 77 | $.setdata(cookie, ck_key); 78 | $.msg($.name, ``, `🎉 Cookie 获取成功`); 79 | } 80 | } 81 | 82 | // 脚本入口函数 83 | async function main() { 84 | for (let cookieItem of cookiesArr) { 85 | cookie = cookieItem; 86 | $.index = ++userIdx; 87 | $.activityTaskId = ''; 88 | $.activityTaskRelationId = ''; 89 | $.taskContentNum = 0; 90 | $.notCompleted = true; 91 | console.log(`\n账号 ${$.index} 开始执行\n`); 92 | // 每日签到 93 | await signin(); 94 | // 获取任务列表 95 | await relationList(); 96 | // 如果任务id不存在或已完成,则跳过该用户 97 | if (!$.activityTaskId || !$.notCompleted) continue; 98 | // 领取任务 99 | await toTask(Api.task); 100 | // 等待任务 101 | await $.wait(1000 * $.taskContentNum); 102 | // 提交任务 103 | await toTask(Api.submit); 104 | // 再次获取任务列表 105 | await relationList(); 106 | // 领取奖励 107 | await toTask(Api.rewards); 108 | } 109 | } 110 | 111 | // 每日签到 112 | async function signin() { 113 | try { 114 | let result = await httpRequest(options(Api.sign.url)); 115 | debug(result); 116 | let text = ''; 117 | if (result?.responseCode === '0') { 118 | $.mobile = result.data.signInfo.mobile; // 手机号 119 | // $.accountId = result.data.signInfo.accountId; // 用户ID 120 | $.signInStatus = result.data.signInfo.signInStatus === 1 ? '🎉 签到成功' : "❌ 签到失败"; // 签到状态:1=是 0=否 121 | $.changeIntegeral = result.data.signInfo.changeIntegeral; // 积分变动 122 | $.continousSignDays = result.data.signInfo.continousSignDays; // 连续签到天数 123 | $.currentIntegral = result.data.signInfo.currentIntegral + $.changeIntegeral; // 当前积分 124 | text = `账号 ${hideSensitiveData($.mobile, 3, 4)}\n${$.signInStatus}, ${$.changeIntegeral > 0 ? `积分 +${$.changeIntegeral}, ` : ''}连续签到 ${$.continousSignDays} 天, 积分余额 ${$.currentIntegral}\n`; 125 | } else if (result?.responseCode === '402') { 126 | $.signInStatus = result.message; 127 | text = $.signInStatus; 128 | } else { 129 | $.signInStatus = "❌ 签到失败"; 130 | text = $.signInStatus; 131 | console.log(data); 132 | } 133 | $.notifyMsg.push(text); 134 | console.log(`每日签到: ${$.signInStatus}`); 135 | } catch (e) { 136 | console.log(e); 137 | } 138 | } 139 | 140 | // 获取任务列表 141 | async function relationList() { 142 | try { 143 | let result = await httpRequest(options(Api.relationList.url)); 144 | debug(result); 145 | let taskList = result.data.activityTaskRelations; 146 | for (const item of taskList) { 147 | const { activityTaskId, activityTaskRelationId, activityTaskName, activityTaskType, activityTaskDesc, taskProcessStatus, activityTaskSort, taskContentNum, taskRewardType, taskRewardTypeName, taskRewardValue, taskJumpAddressType, taskJumpAddressDesc, taskEventButton, taskFinishNum, successRewardDesc } = item; 148 | if (taskRewardTypeName == "积分") { 149 | $.activityTaskId = activityTaskId; 150 | $.taskName = activityTaskName; 151 | if (taskProcessStatus == "NOT_COMPLETED") { 152 | $.taskContentNum = taskContentNum; 153 | console.log(`活动名称: ${activityTaskName}\n活动说明: ${activityTaskDesc}\n活动奖励: ${taskRewardValue} ${taskRewardTypeName}`); 154 | } else { 155 | $.notCompleted = false; 156 | $.activityTaskRelationId = activityTaskRelationId; 157 | console.log(`完成任务: ${$.activityTaskRelationId}`); 158 | } 159 | break; 160 | } 161 | // console.log(item); 162 | } 163 | } catch (e) { 164 | console.log(e); 165 | } 166 | 167 | } 168 | 169 | // 执行任务 170 | async function toTask(obj) { 171 | try { 172 | let result = await httpRequest(options(obj.url, obj.body)); 173 | debug(result); 174 | if (result?.responseCode == "0") { 175 | console.log(`${$.taskName}: ${result['message']}`); 176 | } else { 177 | console.log(`${$.taskName}失败: ${$.toStr(result)}`); 178 | } 179 | } catch (e) { 180 | console.log(e); 181 | } 182 | } 183 | 184 | // 主执行程序 185 | !(async () => { 186 | // 获取 Cookie 187 | if (isGetCookie = typeof $request !== `undefined`) { 188 | GetCookie(); 189 | return; 190 | } 191 | // 未检测到 Cookie,退出 192 | if (!(await checkEnv())) { throw new Error(`❌未检测到ck,请添加环境变量`) }; 193 | // 执行任务 194 | if (cookiesArr.length > 0) await main(); 195 | })() 196 | .catch((e) => $.notifyMsg.push(e.message || e)) // 捕获登录函数等抛出的异常, 并把原因添加到全局变量(通知) 197 | .finally(async () => { 198 | await sendMsg($.notifyMsg.join('\n')); // 推送通知 199 | $.done(); 200 | }) 201 | 202 | 203 | // ---------------------- 辅助函数区域 ---------------------- 204 | // 封装请求参数 205 | function options(url, body = '') { 206 | let opt = { 207 | url: `${origin}${url}`, 208 | headers: { 209 | 'Accept': `*/*`, 210 | 'Origin': `https://hotels.folidaymall.com`, 211 | 'Accept-Encoding': `gzip, deflate, br`, 212 | 'Content-Type': `application/json;charset=utf-8`, 213 | 'Connection': `keep-alive`, 214 | 'Host': `apis.folidaymall.com`, 215 | 'User-Agent': `Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.32(0x1800202c) NetType/WIFI Language/zh_CN miniProgram/wx1fa4da2889526a37`, 216 | 'Authorization': cookie, 217 | 'Accept-Language': `zh-CN,zh-Hans;q=0.9`, 218 | 'Referer': `https://hotels.folidaymall.com/` 219 | }, 220 | body, 221 | timeout: 10000 222 | } 223 | if (body == '') delete opt.body; 224 | return opt; 225 | } 226 | 227 | // 检查变量 228 | async function checkEnv() { 229 | // 多账号分割 230 | cookie = ($.isNode() ? process.env.ThomasCook_Cookie : $.getdata(ck_key)).split('@'); 231 | if (cookie) { 232 | // 获取 Cookie 数组 233 | Object.keys(cookie).forEach((item) => item && cookiesArr.push(cookie[item])); 234 | // 检测账号数量 235 | return console.log(`共找到${cookiesArr.length}个账号`), true; // true == !0 236 | } 237 | return; 238 | } 239 | 240 | // 发送消息 241 | async function sendMsg(message) { 242 | if (!message) return; 243 | message = message.replace(/\n+$/, ''); // 清除末尾换行 244 | if (Notify > 0) { 245 | if ($.isNode()) { 246 | try { 247 | var notify = require('./sendNotify'); 248 | } catch (e) { 249 | var notify = require('./utils/sendNotify'); 250 | } 251 | await notify.sendNotify($.name, message); 252 | } else { 253 | $.msg($.name, '', message); 254 | } 255 | } else { 256 | console.log(message); 257 | } 258 | } 259 | 260 | // 数据脱敏 261 | function hideSensitiveData(string, head_length = 2, foot_length = 2) { 262 | let star = ''; 263 | try { 264 | for (var i = 0; i < string.length - head_length - foot_length; i++) { 265 | star += '*'; 266 | } 267 | return string.substring(0, head_length) + star + string.substring(string.length - foot_length); 268 | } catch (e) { 269 | console.log(e); 270 | return string; 271 | } 272 | } 273 | 274 | // DEBUG 275 | function debug(content, title = "debug") { 276 | let start = `\n----- ${title} -----\n`; 277 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 278 | if ($.is_debug === 'true') { 279 | if (typeof content == "string") { 280 | console.log(start + content + end); 281 | } else if (typeof content == "object") { 282 | console.log(start + $.toStr(content) + end); 283 | } 284 | } 285 | } 286 | 287 | 288 | // 请求函数二次封装 289 | function httpRequest(options, method = 'get') { if ('body' in options) { method = 'post' }; return new Promise((resolve) => { $[method](options, (err, resp, data) => { try { if (err) { console.log(`❌ ${options['url']} 请求失败`); $.logErr(err); } else { if (data) { try { typeof JSON.parse(data) == 'object' ? (data = JSON.parse(data)) : ''; } catch (e) { } } else { console.log(`服务器返回空数据`); } } } catch (e) { $.logErr(e, resp); } finally { resolve(data); } }) }) } 290 | 291 | // prettier-ignore 292 | 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 } isStash() { return "undefined" != typeof $environment && $environment["stash-version"] } 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, a] = i.split("@"), n = { url: `http://${a}/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), a = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(a); 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 ? s.status : s.statusCode, s.status = s.statusCode), 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 && t.error || "UndefinedError")); 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: a } = t, n = s.decode(a, this.encoding); e(null, { status: i, statusCode: r, headers: o, rawBody: a, body: n }, n) }, 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 ? s.status : s.statusCode, s.status = s.statusCode), 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 && t.error || "UndefinedError")); 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: a } = t, n = i.decode(a, this.encoding); e(null, { status: s, statusCode: r, headers: o, rawBody: a, body: n }, n) }, 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, i = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": i } } 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) : this.isNode() && process.exit(1) } }(t, e) } 293 | -------------------------------------------------------------------------------- /scripts/jparking_sign.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:捷停车签到 3 | 活动入口:捷停车APP-停车币签到 4 | 签到规则:连签奖励,首日 6 停车币、次日 7 停车币,以此类推7天封顶 5 | 活动奖励:停车币可用于兑换停车券,比例 1000:1 6 | 环境变量:jtc_userId(Node环境,多账号以@隔开) 7 | 使用说明:添加重写规则并打开捷停车 APP 即可获取 userId 和 Token 8 | 更新时间:2024-05-24 9 | 10 | ================ Surge 配置 ================ 11 | [MITM] 12 | hostname = %APPEND% sytgate.jslife.com.cn 13 | 14 | [Script] 15 | 获取捷停车userId = type=http-request, pattern=^https:\/\/sytgate\.jslife\.com\.cn\/core-gateway\/order\/carno\/pay\/info, requires-body=1, max-size=0, script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js 16 | 17 | 捷停车签到 = type=cron, cronexp=15 9 * * *, timeout=60, script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js, script-update-interval=0 18 | 19 | ============ Quantumult X 配置 ============= 20 | [MITM] 21 | hostname = sytgate.jslife.com.cn 22 | 23 | [rewrite_local] 24 | ^https:\/\/sytgate\.jslife\.com\.cn\/core-gateway\/order\/carno\/pay\/info url script-request-body https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js 25 | 26 | [task_local] 27 | 15 9 * * * https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js, tag=捷停车签到, enabled=true 28 | 29 | ================ Loon 配置 ================ 30 | [MITM] 31 | hostname = sytgate.jslife.com.cn 32 | 33 | cron "15 9 * * *" script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js, tag=捷停车签到 34 | 35 | http-request ^https:\/\/sytgate\.jslife\.com\.cn\/core-gateway\/order\/carno\/pay\/info script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js, requires-body=true, timeout=10, enabled=false, tag=获取捷停车userId 36 | 37 | ================ Boxjs订阅 ================ 38 | 订阅地址:https://raw.githubusercontent.com/FoKit/Scripts/main/boxjs/fokit.boxjs.json 39 | 40 | */ 41 | 42 | // ---------------------- 一般不动变量区域 ---------------------- 43 | const $ = new Env('捷停车签到'); 44 | const origin = 'https://sytgate.jslife.com.cn'; 45 | const jtc_userId_key = 'jtc_userId'; 46 | const Notify = 1; // 0 为关闭通知, 1 为打开通知, 默认为 1 47 | $.messages = []; // 为通知准备的空数组 48 | 49 | // ---------------------- 自定义变量区域 ---------------------- 50 | $.is_debug = ($.isNode() ? process.env.IS_DEDUG : $.getdata('is_debug')) || 'false'; // 调试模式 51 | let userId = ($.isNode() ? process.env.jtc_userId : $.getdata(jtc_userId_key)) || '', userIdArr = []; 52 | let watchVideo = ($.isNode() ? process.env.jtc_video : $.getdata('jtc_video')) || 'false'; // 此功能有封号风险,默认禁用 53 | 54 | // 统一管理 api 接口 55 | const Api = { 56 | // 领取奖励 57 | "receive": { 58 | "url": "/base-gateway/integral/v2/task/receive", 59 | }, 60 | // 执行任务 61 | "complete": { 62 | "url": "/base-gateway/integral/v2/task/complete", 63 | }, 64 | // 个人信息 65 | "query": { 66 | "url": "/base-gateway/member/queryMbrCityBaseInfo", 67 | }, 68 | // adToken 69 | "adToken": { 70 | "url": "/base-gateway/integral/v2/task/token", 71 | } 72 | } 73 | 74 | // 主执行程序 75 | !(async () => { 76 | // 检查变量 77 | await checkEnv(); 78 | // 获取 Cookie 79 | if (isGetCookie = typeof $request !== 'undefined') { 80 | GetCookie(); 81 | return; 82 | } 83 | // 未检测到账号变量, 退出 84 | if (!userIdArr[0]) { 85 | throw new Error(`❌ 未获取到 userId, 请添加环境变量`); 86 | } else { 87 | // 执行任务 88 | await main(); 89 | } 90 | })() 91 | .catch((e) => $.messages.push(e.message || e) && console.log(e)) // 捕获登录函数等抛出的异常, 并把原因添加到全局变量(通知) 92 | .finally(async () => { 93 | await sendMsg($.messages.join('\n')); // 推送通知 94 | $.done(); 95 | }) 96 | 97 | 98 | // 脚本入口函数 99 | async function main() { 100 | for (let i = 0; i < userIdArr.length; i++) { 101 | console.log(`账号[${i + 1}]开始执行`); 102 | 103 | // 变量初始化 104 | $.message = ''; 105 | $.result = ''; 106 | $.mobile = '未知'; 107 | $.integralValue = 0; 108 | $.userId = userIdArr[i].split(',')[0]; 109 | $.token = userIdArr[i].split(',')[1]; 110 | $.taskMap = { "T00": "签到", "T01": "浏览", "T02": "看视频" }; 111 | 112 | // 领取浏览任务 113 | await browse(); 114 | 115 | // 看视频任务 116 | // watchVideo == 'true' && $.token && await videos(); 117 | 118 | // 领取签到奖励 119 | await receive("T00"); 120 | 121 | // 领取浏览奖励 122 | $.taskMap['T01'] && await receive("T01"); 123 | 124 | // 打印结果 125 | console.log($.result); 126 | 127 | // 等待 1 秒 128 | await $.wait(1000 * 1); 129 | 130 | // 获取账号信息, 带 2 次重试机制 131 | let trys = 0; 132 | while ($.integralValue == 0 && trys <= 3) { 133 | trys++; 134 | if (trys > 1) console.log(`⚠️ 第 ${trys}/3 次重试...\n`) 135 | await getUserInfo(); 136 | } 137 | 138 | // 拼接通知消息 139 | $.messages.push(`${$.result.replace(/\n$/, '')}`); // 签到 & 浏览 任务结果 140 | $.messages.push(`停车币余额: ${$.integralValue} 可抵扣: ${($.integralValue / 1000).toFixed(2)} 元\n`); 141 | 142 | // 每个账号间隔 3 秒 143 | await $.wait(1000 * 3); 144 | } 145 | } 146 | 147 | 148 | // 获取数据 149 | function GetCookie() { 150 | if ($request && $request.body) { 151 | let body = JSON.parse($request.body); 152 | if (body?.userId) { 153 | if (!new RegExp(body.userId).test(userId)) { 154 | userId ? userId += `@${body.userId},${body.token}` : userId += `${body.userId},${body.token}`; 155 | $.setdata(userId, jtc_userId_key); 156 | console.log(`userId: ${body.userId} \n`); 157 | $.messages.push(`🎉 userId 写入成功\n${hideSensitiveData(body.userId, 4, 4)} `); 158 | } else { 159 | console.log(`❌ ${body.userId} 已存在\n`); 160 | } 161 | } 162 | } 163 | } 164 | 165 | 166 | // 提交任务(浏览 & 签到) 167 | async function receive(taskNo) { 168 | let result = await httpRequest(options(Api.receive.url, `{"userId":"${$.userId}","reqSource":"APP_JTC","taskNo":"${taskNo}","token":"${$.token}"}`)); 169 | debug(result, "receive"); 170 | if (result.success) { 171 | $.result += `${$.taskMap[taskNo]} 任务完成, 获得 ${result.data} 停车币\n`; 172 | } else { 173 | $.result += `${result.message} \n`; 174 | } 175 | } 176 | 177 | // 浏览 178 | async function browse() { 179 | let result = await httpRequest(options(Api.complete.url, `{"userId":"${$.userId}","reqSource":"APP_JTC","taskNo":"T01","token":"${$.token}"}`)); 180 | debug(result, "browse"); 181 | if (!result.success) { 182 | console.log(`❌ 领取${$.taskMap['T01']}任务失败: ${result.message}`); 183 | delete $.taskMap['T01']; 184 | } 185 | } 186 | 187 | // 看视频 188 | async function videos() { 189 | // 获取 adToken 190 | let res = await httpRequest(options(Api.adToken.url, `{"adTime":"600","userId":"${$.userId}","taskNo":"T02","token":"${$.token}","timestamp":"${Date.now()}"}`)); 191 | debug(res, "getAdToken"); 192 | if (res.success) { 193 | let = adToken = res['data']['token']; 194 | let videosCoins = 0; // 看视频奖励数 195 | // 领取奖励(每日50次) 196 | for (let i = 1; i <= 50; i++) { 197 | let result = await httpRequest(options(Api.complete.url, `{"timestamp":"${Date.now()}","taskNo":"T02","reqSource":"APP_JTC","receiveTag":"true","userId":"${$.userId}","token":"${$.token}","adToken":"${adToken}"}`)); 198 | debug(result, "videos"); 199 | if (result.success) { 200 | videosCoins += result['data']['integralValue']; 201 | console.log(`✅ 完成看视频任务,获得 ${result['data']['integralValue']} 停车币`); 202 | } else { 203 | console.log(`❌ 看视频任务失败: `, result); 204 | break; 205 | } 206 | } 207 | videosCoins && ($.result += `${$.taskMap['T02']} 任务完成, 获得 ${videosCoins} 停车币\n`); 208 | } else { 209 | console.log(`❌ 领取${$.taskMap['T02']}任务失败: ${res.message}`); 210 | } 211 | } 212 | 213 | 214 | // 用户信息 215 | async function getUserInfo() { 216 | let result = await httpRequest(options(Api.query.url, `{"userId":"${$.userId}","reqSource":"APP_JTC"}`)); 217 | debug(result, "getUserInfo"); 218 | if (result.code == '0') { 219 | $.mobile = result.data.mobile; 220 | $.integralValue = result.data.integralValue; 221 | $.messages.push(`账号: ${hideSensitiveData($.mobile, 3, 4)} `); 222 | console.log(`账号 ${$.mobile} 停车币余额 ${$.integralValue} \n`); 223 | } else { 224 | console.log(`❌ 用户信息查询失败\n${result} \n`); 225 | } 226 | } 227 | 228 | 229 | // 检查变量 230 | async function checkEnv() { 231 | // 多账号分割 232 | userIdArr = userId.split('@'); 233 | // 当下标0为空字符串也会占用长度,所以需判断是否为空字符串 234 | if (userIdArr[0]) { 235 | console.log(`\n检测到 ${userIdArr.length} 个账号变量\n`); 236 | return userIdArr.length; 237 | } else { 238 | console.log(`\n检测到 0 个账号变量\n`); 239 | return 0; 240 | } 241 | } 242 | 243 | 244 | // 发送消息 245 | async function sendMsg(message) { 246 | if (!message) return; 247 | message = message.replace(/\n+$/, ''); // 清除末尾换行 248 | if (Notify > 0) { 249 | if ($.isNode()) { 250 | try { 251 | var notify = require('./sendNotify'); 252 | } catch (e) { 253 | var notify = require('./utils/sendNotify'); 254 | } 255 | await notify.sendNotify($.name, message); 256 | } else { 257 | $.msg($.name, '', message); 258 | } 259 | } else { 260 | console.log(message); 261 | } 262 | } 263 | 264 | 265 | // 封装请求参数 266 | function options(url, body = '') { 267 | let opt = { 268 | url: `${origin}${url}`, 269 | headers: { 270 | "Host": "sytgate.jslife.com.cn", 271 | "Content-Type": "application/json;charset=utf-8", 272 | "Accept-Encoding": "gzip, deflate, br", 273 | "Connection": "keep-alive", 274 | "Accept": "*/*", 275 | "User-Agent": "JTC/6.2.0 (iPhone; iOS 16.6.1; Scale/3.00)", 276 | "Accept-Language": "zh-Hans-CN;q=1, zh-Hant-HK;q=0.9, en-CN;q=0.8, de-DE;q=0.7, ja-CN;q=0.6", 277 | "content-type": "application/json" 278 | }, 279 | body, 280 | timeout: 10000 281 | } 282 | if (body == '') delete opt.body; 283 | debug(opt); 284 | return opt; 285 | } 286 | 287 | 288 | // DEBUG 289 | function debug(content, title = "debug") { 290 | let start = `\n----- ${title} -----\n`; 291 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 292 | if ($.is_debug === 'true') { 293 | if (typeof content == "string") { 294 | console.log(start + content + end); 295 | } else if (typeof content == "object") { 296 | console.log(start + $.toStr(content) + end); 297 | } 298 | } 299 | } 300 | 301 | 302 | // 数据脱敏 303 | function hideSensitiveData(string, head_length = 2, foot_length = 2) { 304 | let star = ''; 305 | for (var i = 0; i < string.length - head_length - foot_length; i++) { 306 | star += '*'; 307 | } 308 | return string.substring(0, head_length) + star + string.substring(string.length - foot_length); 309 | } 310 | 311 | 312 | // 请求函数二次封装 313 | function httpRequest(options, method = 'get') { if ('body' in options) { method = 'post' }; return new Promise((resolve) => { $[method](options, (err, resp, data) => { try { if (err) { console.log(`❌ ${options['url']} 请求失败`); $.logErr(err); } else { if (data) { try { typeof JSON.parse(data) == 'object' ? (data = JSON.parse(data)) : ''; } catch (e) { } } else { console.log(`服务器返回空数据`); } } } catch (e) { $.logErr(e, resp); } finally { resolve(data); } }) }) } 314 | 315 | // prettier-ignore 316 | 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 } isStash() { return "undefined" != typeof $environment && $environment["stash-version"] } 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, a] = i.split("@"), n = { url: `http://${a}/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), a = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(a); 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 ? s.status : s.statusCode, s.status = s.statusCode), 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 && t.error || "UndefinedError")); 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: a } = t, n = s.decode(a, this.encoding); e(null, { status: i, statusCode: r, headers: o, rawBody: a, body: n }, n) }, 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 ? s.status : s.statusCode, s.status = s.statusCode), 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 && t.error || "UndefinedError")); 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: a } = t, n = i.decode(a, this.encoding); e(null, { status: s, statusCode: r, headers: o, rawBody: a, body: n }, n) }, 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, i = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": i } } 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) : this.isNode() && process.exit(1) } }(t, e) } 317 | -------------------------------------------------------------------------------- /scripts/get_jd_wskey.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:京东 WSKEY 3 | 更新时间:2024/07/27 4 | 使用方法:划掉后台重新打开 京东APP 即可自动抓取 WSKEY。抓完 WSKEY 不能在京东 app 点退出登录(会导致 WSKEY 失效),切换账号的正确姿势是先断网(飞行模式)再点击退出登录,划掉后台重新打开 APP 再登录新的账号。 5 | 注意事项:脚本抓取的 WSKEY 默认自动提交到服务器(自动上车),可通过 BoxJs 设置关闭自动提交功能。 6 | 重写订阅:https://raw.githubusercontent.com/FoKit/Scripts/main/rewrite/get_jd_wskey.sgmodule 7 | BoxJs订阅:https://raw.githubusercontent.com/FoKit/Scripts/main/boxjs/fokit.boxjs.json 8 | 9 | ------------------ Surge 配置 ------------------ 10 | 11 | [Script] 12 | 京东 WSKEY = type=http-request,pattern=https:\/\/blackhole\.m\.jd\.com\/getinfo,requires-body=0,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js,script-update-interval=0 13 | 14 | 京东 PIN = type=http-request,pattern=https:\/\/perf\.m\.jd\.com\/app_monitor\/\w{1,}\/getRule,requires-body=0,max-size=0,binary-body-mode=0,timeout=30,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js,script-update-interval=0 15 | 16 | [MITM] 17 | hostname = %APPEND% blackhole.m.jd.com, perf.m.jd.com 18 | 19 | ------------------- Loon 配置 ------------------- 20 | 21 | [MITM] 22 | hostname = blackhole.m.jd.com, perf.m.jd.com 23 | 24 | [Script] 25 | http-request https:\/\/blackhole\.m\.jd\.com\/getinfo tag=京东 WSKEY,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js,requires-body=0 26 | 27 | http-request https:\/\/perf\.m\.jd\.com\/app_monitor\/\w{1,}\/getRule tag=京东 PIN,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js,requires-body=0 28 | 29 | --------------- Quantumult X 配置 --------------- 30 | 31 | [MITM] 32 | hostname = blackhole.m.jd.com, perf.m.jd.com 33 | 34 | [rewrite_local] 35 | 36 | https:\/\/blackhole\.m\.jd\.com\/getinfo url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js 37 | 38 | https:\/\/perf\.m\.jd\.com\/app_monitor\/\w{1,}\/getRule url script-request-header https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/get_jd_wskey.js 39 | 40 | ------------------------------------------------ 41 | */ 42 | 43 | const $ = new Env('京东 WSKEY'); 44 | $.jd_tempKey = 'jd_temp', $.wskeyKey = 'wskeyList'; // 缓存键名 45 | $.is_debug = $.getdata('is_debug') || 'false'; // 调试模式 46 | $.chat_id = $.getdata('WSKEY_TG_USER_ID') || ''; // TG CHAT ID 47 | $.bot_token = $.getdata('WSKEY_TG_BOT_TOKEN') || ''; // TG Robot Token 48 | $.autoSubmit = $.getdata('WSKEY_AUTO_UPLOAD') || 'true'; // 是否自动提交 49 | $.Messages = [], $.cookie = ''; // 初始化数据 50 | 51 | // 脚本执行入口 52 | !(async () => { 53 | if (typeof $request !== `undefined`) { 54 | await GetCookie(); 55 | if ($.cookie && $.autoSubmit != 'false') { 56 | await SubmitCK(); 57 | } else if ($.cookie) { 58 | $.Messages.push(`🎉 WSKEY 获取成功\n${$.cookie}`); 59 | $.setjson($.wskeyList, $.wskeyKey); // 写入数据持久化 60 | } 61 | } 62 | })() 63 | .catch((e) => $.Messages.push(e.message || e) && $.logErr(e)) 64 | .finally(async () => { 65 | await sendMsg($.Messages.join('\n').trimStart().trimEnd()); // 推送通知 66 | $.done(); 67 | }) 68 | 69 | // 获取用户数据 70 | async function GetCookie() { 71 | try { 72 | debug($request.headers); 73 | const headers = ObjectKeys2LowerCase($request.headers); 74 | const [, wskey] = headers?.cookie.match(/wskey=([^=;]+?);/) || ''; 75 | const [, pin] = headers?.cookie.match(/pin=([^=;]+?);/) || ''; 76 | 77 | // 延迟读取缓存 78 | if ($request.url.includes('/getRule')) await $.wait(3e3); 79 | 80 | // 读取缓存数据 81 | $.jd_temp = $.getjson($.jd_tempKey) || {}; // 临时缓存 82 | $.wskeyList = $.getjson($.wskeyKey) || []; // WSKEY 缓存 83 | 84 | // 清理过期缓存数据 85 | if ($.jd_temp?.['ts'] && Date.now() - $.jd_temp['ts'] >= 15e3) { 86 | $.log(`🆑 清理过期缓存数据`); 87 | $.jd_temp = {}; 88 | } 89 | 90 | // 写入缓存 91 | if (wskey) { 92 | $.log(`wskey: ${wskey}`); 93 | $.jd_temp['wskey'] = wskey; 94 | $.jd_temp['ts'] = Date.now(); 95 | $.setjson($.jd_temp, $.jd_tempKey); // 写入新的 wskey 96 | } else if (pin) { 97 | $.log(`pin: ${pin}`); 98 | $.jd_temp['pin'] = pin; 99 | $.jd_temp['ts'] = Date.now(); 100 | $.setjson($.jd_temp, $.jd_tempKey); // 写入新的 pin 101 | } 102 | 103 | // 拼接 wskey 104 | if ($.jd_temp?.['wskey'] && $.jd_temp?.['pin']) { 105 | $.cookie = `wskey=${$.jd_temp['wskey']}; pin=${$.jd_temp['pin']};`; 106 | 107 | // 使用 find() 方法找到与 pin 匹配的对象,以新增或更新用户 WSKEY 108 | const user = $.wskeyList.find(user => user.userName === $.jd_temp['pin']); 109 | if (user) { 110 | if (user.cookie == $.cookie) { 111 | $.log(`⚠️ 当前 WSKEY 与缓存一致, 结束运行。`); 112 | $.done(); // WSKEY 无变化结束运行 113 | } 114 | $.log(`♻️ 更新用户 WSKEY: ${$.cookie}`); 115 | user.cookie = $.cookie; 116 | } else { 117 | $.log(`🆕 新增用户 WSKEY: ${$.cookie}`); 118 | $.wskeyList.push({ "userName": $.jd_temp?.['pin'], "cookie": $.cookie }); 119 | } 120 | } 121 | } catch (e) { 122 | $.log("❌ 用户数据获取失败"), $.log(e); 123 | } 124 | } 125 | 126 | // 提交 WSKEY 127 | async function SubmitCK() { 128 | let msg = ''; 129 | // 构造请求 130 | let options = { 131 | url: "https://api.fokit.cn/submit", 132 | body: `text=${$.cookie}` 133 | }; 134 | if ($.bot_token && $.chat_id) { 135 | options['url'] += '?' + $.queryStr({ 136 | bot_token: $.bot_token, 137 | chat_id: $.chat_id, 138 | }); 139 | } 140 | 141 | // 发起请求 142 | var result = await Request(options); 143 | if (result?.ok) { 144 | msg += `🎉 WSKEY 提交成功。\n${$.cookie}`; 145 | $.setjson($.wskeyList, $.wskeyKey); // 写入数据持久化 146 | } else if (result?.error_code === 400) { 147 | msg += `⚠️ Telegram bot 无发送消息权限。\n${$.cookie}`; 148 | } else if (result?.error_code === 401) { 149 | msg += `⚠️ Telegram bot token 填写错误。\n${$.cookie}`; 150 | } else { 151 | msg += `❌ WSKEY 提交失败, 请稍后重试。\n${$.cookie}`; 152 | $.log($.toStr(result)); 153 | } 154 | 155 | $.Messages.push(msg), $.log(msg); 156 | } 157 | 158 | /** 159 | * 对象属性转小写 160 | * @param {object} obj - 传入 $request.headers 161 | * @returns {object} 返回转换后的对象 162 | */ 163 | function ObjectKeys2LowerCase(obj) { 164 | const _lower = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])) 165 | return new Proxy(_lower, { 166 | get: function (target, propKey, receiver) { 167 | return Reflect.get(target, propKey.toLowerCase(), receiver) 168 | }, 169 | set: function (target, propKey, value, receiver) { 170 | return Reflect.set(target, propKey.toLowerCase(), value, receiver) 171 | } 172 | }) 173 | } 174 | 175 | /** 176 | * 请求函数二次封装 177 | * @param {(object|string)} options - 构造请求内容,可传入对象或 Url 178 | * @returns {(object|string)} - 根据 options['respType'] 传入的 {status|headers|rawBody} 返回对象或字符串,默认为 body 179 | */ 180 | async function Request(options) { 181 | try { 182 | options = options.url ? options : { url: options }; 183 | const _method = options?._method || ('body' in options ? 'post' : 'get'); 184 | const _respType = options?._respType || 'body'; 185 | const _timeout = options?._timeout || 15e3; 186 | const _http = [ 187 | new Promise((_, reject) => setTimeout(() => reject(`❌ 请求超时: ${options['url']}`), _timeout)), 188 | new Promise((resolve, reject) => { 189 | debug(options, '[Request]'); 190 | $[_method.toLowerCase()](options, (error, response, data) => { 191 | debug(response, '[response]'); 192 | error && $.log($.toStr(error)); 193 | if (_respType !== 'all') { 194 | resolve($.toObj(response?.[_respType], response?.[_respType])); 195 | } else { 196 | resolve(response); 197 | } 198 | }) 199 | }) 200 | ]; 201 | return await Promise.race(_http); 202 | } catch (err) { 203 | $.logErr(err); 204 | } 205 | } 206 | 207 | // 发送消息 208 | async function sendMsg(message) { 209 | if (!message) return; 210 | try { 211 | if ($.isNode()) { 212 | try { 213 | var notify = require('./sendNotify'); 214 | } catch (e) { 215 | var notify = require('./utils/sendNotify'); 216 | } 217 | await notify.sendNotify($.name, message); 218 | } else { 219 | $.msg($.name, '', message); 220 | } 221 | } catch (e) { 222 | $.log(`\n\n----- ${$.name} -----\n${message}`); 223 | } 224 | } 225 | 226 | /** 227 | * DEBUG 228 | * @param {*} content - 传入内容 229 | * @param {*} title - 标题 230 | */ 231 | function debug(content, title = "debug") { 232 | let start = `\n----- ${title} -----\n`; 233 | let end = `\n----- ${$.time('HH:mm:ss')} -----\n`; 234 | if ($.is_debug === 'true') { 235 | if (typeof content == "string") { 236 | $.log(start + content + end); 237 | } else if (typeof content == "object") { 238 | $.log(start + $.toStr(content) + end); 239 | } 240 | } 241 | } 242 | 243 | // prettier-ignore 244 | 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, o) => { s.call(this, t, (t, s, r) => { t ? o(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.logLevels = { debug: 0, info: 1, warn: 2, error: 3 }, this.logLevelPrefixs = { debug: "[DEBUG] ", info: "[INFO] ", warn: "[WARN] ", error: "[ERROR] " }, this.logLevel = "info", 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 o = this.getdata(t); if (o) 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, o) => e(o)) }) } runScript(t, e) { return new Promise(s => { let o = this.getdata("@chavy_boxjs_userCfgs.httpapi"); o = o ? o.replace(/\n/g, "").trim() : o; let r = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); r = r ? 1 * r : 20, r = e && e.timeout ? e.timeout : r; const [i, a] = o.split("@"), n = { url: `http://${a}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": i, Accept: "*/*" }, timeout: r }; this.post(n, (t, e, o) => s(o)) }).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), o = !s && this.fs.existsSync(e); if (!s && !o) return {}; { const o = s ? t : e; try { return JSON.parse(this.fs.readFileSync(o)) } 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), o = !s && this.fs.existsSync(e), r = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, r) : o ? this.fs.writeFileSync(e, r) : this.fs.writeFileSync(t, r) } } lodash_get(t, e, s) { const o = e.replace(/\[(\d+)\]/g, ".$1").split("."); let r = t; for (const t of o) 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, o) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[o + 1]) >> 0 == +e[o + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, o] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, o, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, o, r] = /^@(.*?)\.(.*?)$/.exec(e), i = this.getval(o), a = o ? "null" === i ? null : i || "{}" : "{}"; try { const e = JSON.parse(a); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), o) } catch (e) { const i = {}; this.lodash_set(i, r, t), s = this.setval(JSON.stringify(i), o) } } 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, o) => { !t && s && (s.body = o, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, o) }); 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: o, headers: r, body: i, bodyBytes: a } = t; e(null, { status: s, statusCode: o, headers: r, body: i, bodyBytes: a }, i, a) }, 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: o, statusCode: r, headers: i, rawBody: a } = t, n = s.decode(a, this.encoding); e(null, { status: o, statusCode: r, headers: i, rawBody: a, body: n }, n) }, t => { const { message: o, response: r } = t; e(o, 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"]), 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, o) => { !t && s && (s.body = o, s.statusCode = s.status ? s.status : s.statusCode, s.status = s.statusCode), e(t, s, o) }); 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: o, headers: r, body: i, bodyBytes: a } = t; e(null, { status: s, statusCode: o, headers: r, body: i, bodyBytes: a }, i, a) }, t => e(t && t.error || "UndefinedError")); break; case "Node.js": let o = 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: a } = t, n = o.decode(a, this.encoding); e(null, { status: s, statusCode: r, headers: i, rawBody: a, body: n }, n) }, t => { const { message: s, response: r } = t; e(s, r, r && o.decode(r.rawBody, this.encoding)) }) } } time(t, e = null) { const s = e ? new Date(e) : new Date; let o = { "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 o) new RegExp("(" + e + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? o[e] : ("00" + o[e]).substr(("" + o[e]).length))); return t } queryStr(t) { let e = ""; for (const s in t) { let o = t[s]; null != o && "" !== o && ("object" == typeof o && (o = JSON.stringify(o)), e += `${s}=${o}&`) } return e = e.substring(0, e.length - 1), e } msg(e = t, s = "", o = "", 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, o = t["update-pasteboard"] || t.updatePasteboard; return { "open-url": e, "media-url": s, "update-pasteboard": o } } 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, o, i(r)); break; case "Quantumult X": $notify(e, s, o, i(r)); break; case "Node.js": }if (!this.isMuteLog) { let t = ["", "==============📣系统通知📣=============="]; t.push(e), s && t.push(s), o && t.push(o), console.log(t.join("\n")), this.logs = this.logs.concat(t) } } debug(...t) { this.logLevels[this.logLevel] <= this.logLevels.debug && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.debug}${t.join(this.logSeparator)}`)) } info(...t) { this.logLevels[this.logLevel] <= this.logLevels.info && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.info}${t.join(this.logSeparator)}`)) } warn(...t) { this.logLevels[this.logLevel] <= this.logLevels.warn && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.warn}${t.join(this.logSeparator)}`)) } error(...t) { this.logLevels[this.logLevel] <= this.logLevels.error && (t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(`${this.logLevelPrefixs.error}${t.join(this.logSeparator)}`)) } 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) } 245 | --------------------------------------------------------------------------------