├── Module ├── Cookie.sgmodule └── Script.sgmodule └── Script ├── AutoSwitchJDUserAgent.js ├── Oil.js ├── Restart Docker.js ├── Stream-All.js ├── WageEarner.boxjs.json ├── daka.js ├── erke.js ├── http_meta_availability.js ├── huke.js ├── jredrain.sh ├── qcej.js ├── sub_info_panel.js ├── tjdd.js ├── tls.js ├── xinian.boxjs.json └── xmSports.js /Module/Cookie.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Cookie 2 | #!desc=获取Cookie 3 | #!category=xinian 4 | 5 | [Script] 6 | # > 高德打车 7 | 高德打车Cookie = type=http-response, pattern=^https:\/\/m5(-zb)?\.amap\.com\/ws\/yuece\/(act|openapi)\/(activity\/current\/)?query,requires-body=1,script-path=https://raw.githubusercontent.com/wf021325/qx/master/task/ampDache.js 8 | 9 | # > 爱奇艺 10 | 爱奇艺Cookie = type=http-request,pattern=^https:\/\/passport\.iqiyi\.com\/apis\/user\/,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/iQIYI-DailyBonus/iQIYI.js 11 | 12 | # > 伊利乳品 13 | 伊利乳品Cookie = type=http-request,pattern=https:\/\/club\.yili\.com\/MALLIFChe\/MCSWSIAPI\.asmx\/Call,requires-body=1,script-path=https://raw.githubusercontent.com/id77/QuantumultX/master/task/yiLi.cookie.js 14 | 15 | # > IT之家 16 | IT之家Cookie = type=http-request,pattern=^https:\/\/my\.ruanmei\.com\/api\/usersign\/getsigninfo?,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/ithome/ithome.cookie.js 17 | 18 | # > 百度贴吧 19 | 百度贴吧Cookie = type=http-request,pattern=https?:\/\/(c\.tieba\.baidu\.com|180\.97\.\d+\.\d+)\/c\/s\/login,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/BDTieBa-DailyBonus/TieBa.js 20 | 21 | # > 顺丰速运 22 | 顺丰速运Cookie = type=http-response,pattern=^https:\/\/mcs-mimp-web\.sf-express\.com\/mcs-mimp\/share\/weChat\/shareGiftReceiveRedirect.+,requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/f4bbb9911b3d8fde9ff9ce077073f0ac/raw/sfsyV2.js 23 | 24 | # > 美团买菜 25 | 美团买菜Cookie = type=http-request,pattern=^https?:\/\/mall\.meituan\.com\/api\/c\/mallcoin\/checkIn\/queryTaskListInfoV.\?,script-path=https://raw.githubusercontent.com/JoJoJotarou/myScript/master/script/meituan/mall.meituan.cookie.js 26 | 27 | # > 阿里云盘 28 | 阿里云盘Cookie = type=http-request,pattern=^https:\/\/(auth|aliyundrive)\.alipan\.com\/v2\/account\/token,requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/33800a98dcd029ba09f8b6fc6f0f5162/raw/aliyun.js 29 | 30 | # > 捷停车 31 | 捷停车Cookie = type=http-request,pattern=^https:\/\/sytgate\.jslife\.com\.cn\/core-gateway\/order\/carno\/pay\/info,requires-body=1,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/jparking_sign.js 32 | 33 | # > PP停车 34 | PP停车Cookie = type=http-request,pattern=^https:\/\/api\.660pp\.com\/rest\/[\d\.]+?\/user\/token,script-path=https://raw.githubusercontent.com/FoKit/Scripts/main/scripts/pp_parking.js 35 | 36 | # > 携程旅行 37 | 携程旅行Cookie = type=http-response,pattern=^https:\/\/m\.ctrip\.com\/restapi\/soa2\/\d+\/[a-zA-Z]+Login(?:$|\?),requires-body=1,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Ctrip-DailyBonus/Ctrip.js 38 | 39 | # > 奶茶合一 40 | 奶茶合一Cookie1 = type=http-response,pattern=^https:\/\/(webapi|webapi2)\.qmai\.cn\/web\/seller\/(oauth\/flash-sale-login|account\/login-minp),requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/8b4f5487e0f28786c7dec9c7484dcd5e/raw/teaMilk.js 41 | 奶茶合一Cookie2 = type=http-request,pattern=^https:\/\/(webapi|webapi2|qmwebapi)\.qmai\.cn\/web\/(catering\/integral|cmk-center)\/sign\/(signIn|takePartInSign),requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/8b4f5487e0f28786c7dec9c7484dcd5e/raw/teaMilk.js 42 | 43 | # > 柠季 44 | 柠季Cookie = type=http-response,pattern=^https?:\/\/pos\.meituan\.com\/api\/v1\/crm\/frontend\/campaign\/display,requires-body=1,script-path=https://raw.githubusercontent.com/leiyiyan/resource/main/script/ningji/ningji.js 45 | 46 | # > 蜜雪冰城 47 | 蜜雪冰城Cookie = type=http-response,pattern=^https:\/\/mxsa\.mxbc\.net\/api\/v1\/customer\/info,requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/865c82e42a5730bb696f6700ebb94cee/raw/mxbc.js 48 | 49 | # > 中国移动 50 | 中国移动Cookie = type=http-request,pattern=^https?:\/\/client\.app\.coc\.10086\.cn\/biz-orange\/[LD]N\/uam(onekey|randcode)login\/autoLogin,requires-body=1,script-path=https://github.120399.xyz/Script/10086.js 51 | 52 | # > 奈雪点单 53 | 奈雪点单Cookie = type=http-request,pattern=^https:\/\/tm-web\.pin-dao\.cn\/user\/base-userinfo,requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/4d0e9572b99530b7cb0e7298622aa2a9/raw/naixue.js 54 | 55 | # > 去重二剪 56 | 去重二剪Cookie = type=http-request,pattern=^https:\/\/apis\.ddfans\.com\/bian\/public\/index\.php\/water,requires-body=1,script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js 57 | 58 | # > WPS_PC 59 | WPS_PC_Cookie = type=http-request,pattern=^https:\/\/(vip|account)(userinfo|\.wps\.cn\/p\/auth\/check)$,script-path=https://raw.githubusercontent.com/wf021325/qx/master/task/wps.js 60 | 61 | # > 回收猿旧衣回收 62 | 回收猿回收Cookie = type=http-response,pattern=^https:\/\/www\.52bjy\.com\/api\/app\/hsy\.php\?action=user&appkey=1079fb245839e765&merchant_id=2&method=center&username=.+,requires-body=1,script-path=https://gist.githubusercontent.com/Sliverkiss/86757c4beac9d48ebe76cc436900a021/raw/hsyhs.js 63 | 64 | # > 鸿星尔克 65 | 鸿星尔克Cookie = type=http-request,pattern=^https:\/\/hope\.demogic\.com\/gic-wx-app\/get-member-asset\.json,script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js 66 | 67 | # > 唐机豆豆 68 | 唐机豆豆Cookie = type=http-request,pattern=^https:\/\/h5\.youzan\.com\/wscshop\/weappp\/shop_business_hour\.json,script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js 69 | 70 | # > 途虎养车 71 | 途虎养车Cookie = type=http-request,pattern=https:\/\/api\.tuhu\.cn\/User\/GetInternalCenterInfo,script-path=https://raw.githubusercontent.com/Sliverkiss/GoodNight/master/Script/tuhu.js 72 | 73 | [MITM] 74 | hostname = %APPEND% mall.telunsu.net, *.amap.com, passport.iqiyi.com, club.yili.com, my.ruanmei.com, c.tieba.baidu.com, mcs-mimp-web.sf-express.com, mall.meituan.com, auth.alipan.com, auth.aliyundrive.com, sytgate.jslife.com.cn, api.660pp.com, m.ctrip.com, webapi2.qmai.cn,webapi.qmai.cn,qmwebapi.qmai.cn, pos.meituan.com, mxsa.mxbc.net, client.app.coc.10086.cn, tm-web.pin-dao.cn, apis.ddfans.com, *.wps.cn, www.52bjy.com, www.nodeseek.com, hope.demogic.com, h5.youzan.com, api.tuhu.cn 75 | -------------------------------------------------------------------------------- /Module/Script.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Remove ads and unlock 2 | #!desc=去广告与解锁 3 | #!category=xinian 4 | 5 | [Header Rewrite] 6 | # > Bing Ai 7 | ^https:\/\/www\.bing\.com\/search header-replace User-Agent "Mozilla/5.0 (iphone; Intel Mac OS X 10_12_6) AppleWebKit/537.36 Chrome/110.0 Safari/537.36 Edg/110.0" 8 | 9 | [Body Rewrite] 10 | # > TestFlight 限制 11 | http-request ^https?:\/\/testflight\.apple\.com\/v\d\/accounts\/.+?\/install$ "storefrontId"\x20:\x20"\d{6}-\d{2},\d{2}" "storefrontId":"143441-19,29" 12 | # > WxPusher消息推送平台 底部广告 13 | http-response ^https?:\/\/wxpusher.zjiecode.com\/api\/message '

[\s\S]*?<\/body>' 14 | 15 | [General] 16 | # > 代理检测 17 | skip-proxy = %APPEND% www.baidu.com,yunbusiness.ccb.com,wxh.wo.cn,gate.lagou.com,www.abchina.com.cn,www.shanbay.com,login-service.mobile-bank.psbc.com,mobile-bank.psbc.com,id6.me,iosapps.itunes.apple.com 18 | always-real-ip = %APPEND% easy-login.10099.com.cn,*-update.xoyocdn.com,id6.me,open.e.189.cn 19 | # > HomeKit Accessories Quirk 20 | always-raw-tcp-keywords = %INSERT% "Content-Type: application/pairing+tlv8" 21 | 22 | [Rule] 23 | # > 阻止所有UDP流量到端口443,使HTTP3请求退回到TCP流量 24 | AND,((PROTOCOL,UDP), (DEST-PORT,443)),REJECT-NO-DROP 25 | # 防止TG加载转圈 26 | IP-CIDR,95.161.76.100/31,REJECT,no-resolve,pre-matching 27 | # > Safari 防跳转 28 | DOMAIN,app-site-association.cdn-apple.com,REJECT,pre-matching 29 | # > 京东 去广告 30 | URL-REGEX,^https:\/\/m\.360buyimg\.com\/mobilecms\/s(?:1125x2436|1080x1920),REJECT 31 | # > 交管12123 去广告 32 | URL-REGEX,^https:\/\/gab\.122\.gov\.cn\/eapp\/m\/sysquery\/adver$,REJECT 33 | # > 京东金融 去广告 34 | URL-REGEX,^https?:\/\/ms\.jr\.jd\.com\/gw\/generic\/aladdin\/(new)?na\/m\/getLoadingPicture,REJECT 35 | # > 滴滴出行 去广告 36 | //01 安全提示横幅 37 | URL-REGEX,^https:\/\/guard\.sec\.xiaojukeji\.com\/api\/guard\/psg\/v2\/getShieldStatus,REJECT 38 | //02 底部打车&代价推荐 39 | URL-REGEX,^https:\/\/conf\.diditaxi\.com\.cn\/one,REJECT 40 | //03 福利专区-金融服务-公交-骑车-滴滴乐园 41 | URL-REGEX,^https:\/\/conf\.diditaxi\.com\.cn\/nav\/widget,REJECT 42 | //04 主页 领任务 43 | URL-REGEX,^https:\/\/conf\.diditaxi\.com\.cn\/api\/(component|fusion|dynamicmodule|usercenter),REJECT 44 | URL-REGEX,^https:\/\/conf\.diditaxi\.com\.cn\/dynamic,REJECT 45 | //05 主页 非主流式悬浮挂件 46 | URL-REGEX,^https:\/\/res\.xiaojukeji\.com\/resapi\/activity\/mget,REJECT 47 | //06 商城 48 | URL-REGEX,^https:\/\/shop-gw\.chengxinyouxuan\.com\/(route|indexConfig|apolloConfig|getShopTuanInfos),REJECT 49 | //07 开屏广告 50 | URL-REGEX,^https:\/\/img-ys011\.didistatic\.com\/static,REJECT 51 | //08 骑行 52 | URL-REGEX,^https:\/\/pt-starimg\.didistatic\.com\/static,REJECT 53 | # > 云闪付 去广告 54 | URL-REGEX,^https:\/\/wallet\.95516\.com\/s\/wl\/icon\/long,REJECT 55 | # > 威锋 去广告 56 | URL-REGEX,^https:\/\/api\.wfdata\.club\/v2\/yesfeng\/(infoCenterAd|yesList),REJECT 57 | # > 美团 去广告 58 | URL-REGEX,^https:\/\/flowplus\.meituan\.net\/v1\/mss_\w+\/linglong\/\d+\.jpg$,REJECT 59 | URL-REGEX,^https:\/\/s3plus\.meituan\.net\/v1\/mss_\w+\/(brandcpt-vedio|waimai-alita)\/\w+\.zip$,REJECT 60 | # > 中国联通 去开屏 61 | DOMAIN-SUFFIX,m1.ad.10010.com,REJECT,pre-matching 62 | # > 中国电信 去开屏 63 | DOMAIN,ad.21cn.com,REJECT,pre-matching 64 | DOMAIN,ad.k.21cn.com,REJECT,pre-matching 65 | DOMAIN,admarket.21cn.com,REJECT,pre-matching 66 | DOMAIN,adshows.21cn.com,REJECT,pre-matching 67 | # > 顺丰速运 去开屏 68 | URL-REGEX,^https?:\/\/ccsp-egmas\.sf-express\.com\/cx-app-base\/base\/app\/appVersion\/detectionUpgrade,REJECT 69 | URL-REGEX,^https?:\/\/ccsp-egmas\.sf-express\.com\/cx-app-base\/base\/app\/ad\/,REJECT 70 | URL-REGEX,^https?:\/\/shopic\.sf-express\.com\/crm\/mobile\/common\/flashscreen,REJECT 71 | # > 拼多多 去开屏 72 | URL-REGEX,^https?:\/\/api\.(pinduoduo|yangkeduo)\.com\/api\/cappuccino\/splash,REJECT 73 | # > 建行生活 去开屏 74 | URL-REGEX,^https?:\/\/yunbusiness\.ccb\.com\/clp_service\/txCtrl\?txcode=A3341A00(6|9),REJECT 75 | # > 平安好车主 去开屏 76 | URL-REGEX,^https?:\/\/iobs\.pingan\.com\.cn\/download\/icore-aops-base-dmz-prd\/(YourSystemName|icore-apps-ad),REJECT 77 | # 网易云音乐 去开屏 78 | DOMAIN,img1.360buyimg.com,REJECT,pre-matching 79 | DOMAIN,g.cn.miaozhen.com,REJECT,pre-matching 80 | DOMAIN,ef-dongfeng.tanx.com,REJECT,pre-matching 81 | DOMAIN,ossgw.alicdn.com,REJECT,pre-matching 82 | DOMAIN,iadmusicmat.music.126.net,REJECT,pre-matching 83 | 84 | [Script] 85 | # > 扫描全能王 pro 86 | 全能扫描 = type=http-response,pattern=https:\/\/(api|api-cs)*\.intsig\.net\/purchase\/cs\/query_property\?,requires-body=1,script-path=https://raw.githubusercontent.com/id77/QuantumultX/master/Script/camscanner.js 87 | # > Google 人机验证 88 | 谷歌验证 = type=http-response,pattern=^https:\/\/www\.google\.com(?:\.[a-z]+|)\/(?:search\?(?:|.+?&)q=|$),requires-body=1,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Surge/JS/Google_CAPTCHA.js,ability=http-client-policy,timeout=10 89 | # > Unidream 90 | Unidream = type=http-response,pattern=^https:\/\/buy\.itunes\.apple\.com\/verifyReceipt,requires-body=1,script-path=https://raw.githubusercontent.com/Yu9191/Rewrite/main/itunes/Unidream.js 91 | 92 | [MITM] 93 | hostname = %APPEND% www.bing.com, testflight.apple.com, wxpusher.zjiecode.com, m.360buyimg.com, gab.122.gov.cn, guard.sec.xiaojukeji.com, conf.diditaxi.com.cn, res.xiaojukeji.com, shop-gw.chengxinyouxuan.com, img-ys011.didistatic.com, pt-starimg.didistatic.com, wallet.95516.com, api.wfdata.club, flowplus.meituan.net, s3plus.meituan.net, api.pinduoduo.com, yunbusiness.ccb.com, iobs.pingan.com.cn, ap*.intsig.net, www.google.com*, buy.itunes.apple.com 94 | -------------------------------------------------------------------------------- /Script/AutoSwitchJDUserAgent.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Auto Switch JD UserAgent 3 | // @name:zh-CN JD_UA自动切换 4 | // @namespace 魔改:https://greasyfork.org/zh-CN/scripts/490764 5 | // @version 1.0 6 | // @description Use different User Agents based on different URLs or domains. 7 | // @description:zh-CN 根据不同的网址或域名,使用不同的 User Agent。 8 | // @author xinian 9 | // @license none 10 | // @icon data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDEwIiBoZWlnaHQ9IjQwNCIgdmlld0JveD0iMCAwIDQxMCA0MDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zOTkuNjQxIDU5LjUyNDZMMjE1LjY0MyAzODguNTQ1QzIxMS44NDQgMzk1LjMzOCAyMDIuMDg0IDM5NS4zNzggMTk4LjIyOCAzODguNjE4TDEwLjU4MTcgNTkuNTU2M0M2LjM4MDg3IDUyLjE4OTYgMTIuNjgwMiA0My4yNjY1IDIxLjAyODEgNDQuNzU4NkwyMDUuMjIzIDc3LjY4MjRDMjA2LjM5OCA3Ny44OTI0IDIwNy42MDEgNzcuODkwNCAyMDguNzc2IDc3LjY3NjNMMzg5LjExOSA0NC44MDU4QzM5Ny40MzkgNDMuMjg5NCA0MDMuNzY4IDUyLjE0MzQgMzk5LjY0MSA1OS41MjQ2WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyKSIvPgo8cGF0aCBkPSJNMjkyLjk2NSAxLjU3NDRMMTU2LjgwMSAyOC4yNTUyQzE1NC41NjMgMjguNjkzNyAxNTIuOTA2IDMwLjU5MDMgMTUyLjc3MSAzMi44NjY0TDE0NC4zOTUgMTc0LjMzQzE0NC4xOTggMTc3LjY2MiAxNDcuMjU4IDE4MC4yNDggMTUwLjUxIDE3OS40OThMMTg4LjQyIDE3MC43NDlDMTkxLjk2NyAxNjkuOTMxIDE5NS4xNzIgMTczLjA1NSAxOTQuNDQzIDE3Ni42MjJMMTgzLjE4IDIzMS43NzVDMTgyLjQyMiAyMzUuNDg3IDE4NS45MDcgMjM4LjY2MSAxODkuNTMyIDIzNy41NkwyMTIuOTQ3IDIzMC40NDZDMjE2LjU3NyAyMjkuMzQ0IDIyMC4wNjUgMjMyLjUyNyAyMTkuMjk3IDIzNi4yNDJMMjAxLjM5OCAzMjIuODc1QzIwMC4yNzggMzI4LjI5NCAyMDcuNDg2IDMzMS4yNDkgMjEwLjQ5MiAzMjYuNjAzTDIxMi41IDMyMy41TDMyMy40NTQgMTAyLjA3MkMzMjUuMzEyIDk4LjM2NDUgMzIyLjEwOCA5NC4xMzcgMzE4LjAzNiA5NC45MjI4TDI3OS4wMTQgMTAyLjQ1NEMyNzUuMzQ3IDEwMy4xNjEgMjcyLjIyNyA5OS43NDYgMjczLjI2MiA5Ni4xNTgzTDI5OC43MzEgNy44NjY4OUMyOTkuNzY3IDQuMjczMTQgMjk2LjYzNiAwLjg1NTE4MSAyOTIuOTY1IDEuNTc0NFoiIGZpbGw9InVybCgjcGFpbnQxX2xpbmVhcikiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhciIgeDE9IjYuMDAwMTciIHkxPSIzMi45OTk5IiB4Mj0iMjM1IiB5Mj0iMzQ0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM0MUQxRkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjQkQzNEZFIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhciIgeDE9IjE5NC42NTEiIHkxPSI4LjgxODE4IiB4Mj0iMjM2LjA3NiIgeTI9IjI5Mi45ODkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRUE4MyIvPgo8c3RvcCBvZmZzZXQ9IjAuMDgzMzMzMyIgc3RvcC1jb2xvcj0iI0ZGREQzNSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkE4MDAiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K 11 | // @match *://*/* 12 | // @grant none 13 | // ==/UserScript== 14 | 15 | (function () { 16 | 'use strict'; 17 | 18 | // 语言包 19 | const languagePack = { 20 | 'en': { 21 | name: 'Auto Switch UserAgent', 22 | description: 'Automatically switch User Agent based on different URLs or domains, and support multilingual display.', 23 | status: 'Status', 24 | ua: 'Current UA:', 25 | key: 'Key in uaList:', 26 | close: 'Close', 27 | noMatch: 'No matching UA found in the list' 28 | }, 29 | 'zh-CN': { 30 | name: 'UA自由切', 31 | description: '根据不同的网址或域自动切换 User Agent,支持多语言显示。', 32 | status: '状态', 33 | ua: '当前 UA:', 34 | key: 'uaList 中的 Key:', 35 | close: '关闭', 36 | noMatch: '在清单中未匹配到 UA' 37 | } 38 | }; 39 | 40 | // 内置JD UA 列表 41 | const builtInUaList = { 42 | 'jd_iphone': 'jdapp;iPhone;11.0.0;15.1;JD4iPhone/11.0.0 CFNetwork/1331.0.7 Darwin/21.4.0', 43 | 'jdjsb_iphone': 'jdltapp;iPhone;3.3.8;14.5;;network/wifi;hasUPPay/0;pushNoticeIsOpen/0;lang/zh_CN;model/iPhone11,8;addressid/1577930106;hasOCPay/0;appBuild/1063;supportBestPay/0;pv/2.9;apprpd/Allowance_Registered;ref/JDLTTaskCenterViewController;psq/8;ads/;psn/|4;jdv/0|;adk/;app_device/IOS;pap/JA2020_3112531|3.3.8|IOS 14.5;Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148;supportJDSHWK/1', 44 | 'jdjr_iphone': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148/application=JDJR-App&deviceId=4354534334832364d234532423d243145343-d293432414d2433383832363544444643424&eufv=1&clientType=ios&iosType=iphone&clientVersion=6.3.50&HiClVersion=6.3.50&isUpdate=0&osVersion=15.5&osName=iOS&screen=896414&src=App Store&netWork=2&netWorkType=4&CpayJS=UnionPay/1.0 JDJR&stockSDK=stocksdk-iphone_4.1.0&sPoint=&jdPay=(#@jdPaySDK*#@jdPayChannel=jdfinance&jdPayChannelVersion=6.3.50&jdPaySdkVersion=4.00.37.00&jdPayClientName=iOS*#@jdPaySDK*#@)', 45 | 'jx_iphone': 'jdpingou;iPhone;4.7.0;14.5;fec5b553097ab5ad978a892b9e9bd7ef61294408;network/2g,3g;model/iPhone11,6;appBuild/100518;ADID/00000000-0000-0000-0000-000000000000;supportApplePay/1;hasUPPay/0;pushNoticeIsOpen/0;hasOCPay/0;supportBestPay/0;session/91;pap/JA2019_3111789;brand/apple;supportJDSHWK/1;Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148', 46 | }; 47 | 48 | // 内置替换规则 49 | const builtInUrlUaMap = { 50 | '*.m.jd.com': 'jd_iphone', // 京东 51 | 'gold.jd.com': 'jdjsb_iphone', // 京东极速版 52 | '*.jr.jd.com': 'jdjr_iphone', // 京东金融 53 | '*.m.jingxi.com': 'jx_iphone', // 京喜 54 | }; 55 | 56 | // 自定义 UA 列表 57 | const customUaList = { 58 | }; 59 | 60 | // 自定义替换规则 61 | const customUrlUaMap = { 62 | }; 63 | 64 | // 黑名单和白名单 65 | const blackList = ['*']; 66 | const whiteList = ['*.m.jd.com', 'gold.jd.com', '*.jr.jd.com', '*.m.jingxi.com']; 67 | 68 | // 合并 UA 列表和替换规则 69 | const uaList = Object.assign({}, builtInUaList, customUaList); 70 | const urlUaMap = Object.assign({}, builtInUrlUaMap, customUrlUaMap); 71 | 72 | // 获取当前网址或域 73 | const currentUrl = window.location.href; 74 | const currentDomain = window.location.hostname; 75 | 76 | // 检查是否在白名单中 77 | function isInWhiteList(url, domain) { 78 | return whiteList.some(subdomain => { 79 | const regex = new RegExp(subdomain.replace(/\./g, '\\.').replace(/\*/g, '.*')); 80 | return regex.test(url) || regex.test(domain); 81 | }); 82 | } 83 | 84 | // 检查是否在黑名单中 85 | function isInBlackList(url, domain) { 86 | return blackList.some(subdomain => { 87 | const regex = new RegExp(subdomain.replace(/\./g, '\\.').replace(/\*/g, '.*')); 88 | return regex.test(url) || regex.test(domain); 89 | }); 90 | } 91 | 92 | // 匹配替换规则 93 | let matchedUaKey = null; 94 | if (isInWhiteList(currentUrl, currentDomain) || !isInBlackList(currentUrl, currentDomain)) { 95 | for (const subdomain in urlUaMap) { 96 | const regex = new RegExp(subdomain.replace(/\./g, '\\.').replace(/\*/g, '.*')); 97 | if (regex.test(currentUrl) || regex.test(currentDomain)) { 98 | matchedUaKey = urlUaMap[subdomain]; 99 | break; 100 | } 101 | } 102 | } 103 | 104 | // 设置 User Agent 105 | if (matchedUaKey) { 106 | Object.defineProperty(navigator, 'userAgent', { 107 | get: function () { 108 | return uaList[matchedUaKey]; 109 | } 110 | }); 111 | } 112 | 113 | // 添加“状态”按钮和面板(仅在白名单中显示) 114 | if (isInWhiteList(currentUrl, currentDomain)) { 115 | const statusButton = document.createElement('button'); 116 | statusButton.textContent = languagePack[getLanguage()].status; 117 | statusButton.style.position = 'fixed'; 118 | statusButton.style.top = '0'; 119 | statusButton.style.right = '0'; 120 | statusButton.style.zIndex = '9999'; 121 | statusButton.addEventListener('click', function () { 122 | showStatusPanel(); 123 | }); 124 | document.body.appendChild(statusButton); 125 | 126 | const statusPanel = document.createElement('div'); 127 | statusPanel.style.position = 'fixed'; 128 | statusPanel.style.top = '0'; 129 | statusPanel.style.right = '0'; 130 | statusPanel.style.zIndex = '99999'; 131 | statusPanel.style.padding = '10px'; 132 | statusPanel.style.backgroundColor = '#fff'; 133 | statusPanel.style.border = '1px solid #000'; 134 | statusPanel.style.display = 'none'; 135 | document.body.appendChild(statusPanel); 136 | 137 | function showStatusPanel() { 138 | statusPanel.style.display = 'block'; 139 | statusPanel.innerHTML = ` 140 |

${languagePack[getLanguage()].name}: ${languagePack[getLanguage()].description}

141 |

${languagePack[getLanguage()].ua}: ${matchedUaKey ? uaList[matchedUaKey] : languagePack[getLanguage()].noMatch}

142 |

${languagePack[getLanguage()].key}: ${matchedUaKey ? matchedUaKey : languagePack[getLanguage()].noMatch}

143 | 144 | `; 145 | } 146 | 147 | function hideStatusPanel() { 148 | statusPanel.style.display = 'none'; 149 | } 150 | } 151 | 152 | // 获取语言 153 | function getLanguage() { 154 | const browserLanguage = navigator.language || navigator.userLanguage; 155 | return languagePack[browserLanguage] ? browserLanguage : 'en'; 156 | } 157 | })(); 158 | -------------------------------------------------------------------------------- /Script/Oil.js: -------------------------------------------------------------------------------- 1 | const params = getParams($argument); 2 | const provinceName = params.provname || "广东"; 3 | const apiUrls = [ 4 | `https://apis.tianapi.com/oilprice/index?key=0502a67aa1632678f596891c4af219a8&prov=${encodeURIComponent(provinceName)}`, 5 | `https://apis.tianapi.com/oilprice/index?key=231de491563c35731436829ac52aad43&prov=${encodeURIComponent(provinceName)}`, 6 | `https://apis.tianapi.com/oilprice/index?key=a2bc7a0e01be908881ff752677cf94b7&prov=${encodeURIComponent(provinceName)}`, 7 | `https://apis.tianapi.com/oilprice/index?key=1bcc67c0114bc39a8818c8be12c2c9ac&prov=${encodeURIComponent(provinceName)}`, 8 | `https://apis.tianapi.com/oilprice/index?key=3c5ee42145c852de4147264f25b858dc&prov=${encodeURIComponent(provinceName)}`, 9 | `https://apis.tianapi.com/oilprice/index?key=d718b0f7c2b6d71cb3a9814e90bf847f&prov=${encodeURIComponent(provinceName)}` 10 | ]; 11 | let currentIndex = 0; 12 | 13 | function testNextUrl() { 14 | if (currentIndex >= apiUrls.length) { 15 | console.log("所有URL都失败了"); 16 | $done(); 17 | return; 18 | } 19 | 20 | const apiUrl = apiUrls[currentIndex]; 21 | 22 | $httpClient.get(apiUrl, (error, response, data) => { 23 | if (error) { 24 | console.log(`URL ${currentIndex + 1} 出错: ${error}`); 25 | currentIndex++; 26 | testNextUrl(); 27 | } else { 28 | handleResponse(data); 29 | } 30 | }); 31 | } 32 | 33 | function handleResponse(data) { 34 | const oilPriceData = JSON.parse(data); 35 | if (oilPriceData.code === 200) { 36 | const oilPriceInfo = oilPriceData.result; 37 | const message = `0#柴油: ${oilPriceInfo.p0}元 | 92汽油: ${oilPriceInfo.p92}元\n95汽油: ${oilPriceInfo.p95}元 | 98汽油: ${oilPriceInfo.p98}元`; 38 | 39 | // 获取 http://m.qiyoujiage.com 网页 HTML 内容并提取 tishiContent 40 | $httpClient.get('http://m.qiyoujiage.com/', (error, response, data) => { 41 | if (error) { 42 | console.log(`获取HTML内容出错: ${error}`); 43 | } else { 44 | // 使用正则表达式从HTML中提取 var tishiContent 的内容 45 | const tishiMatch = data.match(/var\s+tishiContent\s*=\s*"(.*?)"/); 46 | if (tishiMatch) { 47 | let tishiContent = tishiMatch[1]; 48 | 49 | // 1. 动态匹配日期并加1天 50 | const dateMatch = tishiContent.match(/(\d{1,2})月(\d{1,2})日/); 51 | let formattedDate = "未知日期"; 52 | if (dateMatch) { 53 | let [month, day] = [parseInt(dateMatch[1]), parseInt(dateMatch[2]) + 1]; 54 | formattedDate = `${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`; 55 | } 56 | 57 | // 2. 动态匹配“下跌”或“下调”变为“降”,“上涨”或“上调”变为“升” 58 | let adjustmentType = "-"; 59 | const adjustmentMatch = tishiContent.match(/(下调|下跌|上调|上涨)/); 60 | if (adjustmentMatch) { 61 | adjustmentType = (adjustmentMatch[1].includes("下")) ? "⤵︎" : "⤴︎"; 62 | } 63 | 64 | // 3. 动态匹配价格区间 65 | const priceRangeMatch = tishiContent.match(/(\d+\.\d+)元\/升-(\d+\.\d+)元\/升/); 66 | let priceAdjustment = "0.00-0.00元"; 67 | if (priceRangeMatch) { 68 | priceAdjustment = `${priceRangeMatch[1]}-${priceRangeMatch[2]}`; 69 | } 70 | 71 | // 在标题中加入从 tishiContent 提取的动态信息 72 | const body = { 73 | title: `今日油价 | ${formattedDate} ${adjustmentType} ${priceAdjustment}`, 74 | content: `${message}`, 75 | provname: params.provname, 76 | icon: params.icon, 77 | "icon-color": params.color 78 | }; 79 | $done(body); 80 | } else { 81 | console.log("提取`tishiContent`失败"); 82 | } 83 | } 84 | }); 85 | } else { 86 | console.log(`请求失败,错误信息:${oilPriceData.msg}`); 87 | currentIndex++; 88 | testNextUrl(); 89 | } 90 | } 91 | 92 | function getParams(param) { 93 | return Object.fromEntries( 94 | param 95 | .split("&") 96 | .map((item) => item.split("=")) 97 | .map(([k, v]) => [k, decodeURIComponent(v)]) 98 | ); 99 | } 100 | 101 | testNextUrl(); 102 | -------------------------------------------------------------------------------- /Script/Restart Docker.js: -------------------------------------------------------------------------------- 1 | const $ = new Env('Docker 容器重启'); 2 | 3 | // Docker 容器名字 4 | const containerName = 'pagermaid_pyro'; 5 | 6 | // 处理成功的函数 7 | function handleSuccess() { 8 | const successMsg = `容器 ${containerName} 重启成功`; 9 | $.log(successMsg); 10 | $.msg($.name, "", successMsg); 11 | $.done(); 12 | } 13 | 14 | // 使用 $httpClient 模块发出 HTTP 请求来重启 Docker 容器 15 | $httpClient.post({ 16 | url: `http://localhost:2375/containers/${containerName}/restart`, 17 | headers: { 18 | 'Content-Type': 'application/json' 19 | } 20 | }, (error, response, data) => { 21 | if (error) { 22 | // 将超时错误信息处理为成功信息 23 | handleSuccess(); 24 | return; 25 | } 26 | 27 | if (response.status !== 204) { 28 | const errorMsg = `容器 ${containerName} 重启失败,响应数据:${data}`; 29 | $.logErr(errorMsg); 30 | $.msg($.name, "", errorMsg); 31 | $.done({ 32 | error: errorMsg 33 | }); 34 | return; 35 | } 36 | 37 | handleSuccess(); 38 | }); 39 | 40 | 41 | // prettier-ignore 42 | 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;"POST"===e&&(s=this.post);const i=new Promise(((e,i)=>{s.call(this,t,((t,s,o)=>{t?i(t):e(s)}))}));return t.timeout?((t,e=1e3)=>Promise.race([t,new Promise(((t,s)=>{setTimeout((()=>{s(new Error("请求超时"))}),e)}))]))(i,t.timeout):i}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:"*/*"},policy:"DIRECT",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)} 43 | -------------------------------------------------------------------------------- /Script/Stream-All.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 脚本参考 @Helge_0x00 4 | 修改日期:2024.08.20 5 | Surge配置参考注释 6 | 7 | ---------------------------------------- 8 | 9 | [Panel] 10 | 策略面板 = script-name=解锁检测,update-interval=7200 11 | 12 | [Script] 13 | 解锁检测 = type=generic,timeout=30,script-path=https://raw.githubusercontent.com/githubdulong/Script/master/Stream-All.js,script-update-interval=0,argument=title=解锁检测&icon=headphones.circle&color=#FF2121 14 | 15 | ---------------------------------------- 16 | 17 | 支持使用脚本使用 argument 参数自定义配置,如:argument=title=解锁检测&icon=headphones.circle&color=#FF2121,具体参数如下所示, 18 | * title: 面板标题 19 | * icon: SFSymbols 图标 20 | * color:图标颜色 21 | 22 | */ 23 | 24 | const STATUS_COMING = 2; 25 | const STATUS_AVAILABLE = 1; 26 | const STATUS_NOT_AVAILABLE = 0; 27 | const STATUS_TIMEOUT = -1; 28 | const STATUS_ERROR = -2; 29 | const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36'; 30 | const REQUEST_HEADERS = { 31 | 'User-Agent': UA, 32 | 'Accept-Language': 'en', 33 | }; 34 | const SUPPORTED_LOCATIONS = ["T1","XX","AL","DZ","AD","AO","AG","AR","AM","AU","AT","AZ","BS","BD","BB","BE","BZ","BJ","BT","BA","BW","BR","BG","BF","CV","CA","CL","CO","KM","CR","HR","CY","DK","DJ","DM","DO","EC","SV","EE","FJ","FI","FR","GA","GM","GE","DE","GH","GR","GD","GT","GN","GW","GY","HT","HN","HU","IS","IN","ID","IQ","IE","IL","IT","JM","JP","JO","KZ","KE","KI","KW","KG","LV","LB","LS","LR","LI","LT","LU","MG","MW","MY","MV","ML","MT","MH","MR","MU","MX","MC","MN","ME","MA","MZ","MM","NA","NR","NP","NL","NZ","NI","NE","NG","MK","NO","OM","PK","PW","PA","PG","PE","PH","PL","PT","QA","RO","RW","KN","LC","VC","WS","SM","ST","SN","RS","SC","SL","SG","SK","SI","SB","ZA","ES","LK","SR","SE","CH","TH","TG","TO","TT","TN","TR","TV","UG","AE","US","UY","VU","ZM","BO","BN","CG","CZ","VA","FM","MD","PS","KR","TW","TZ","TL","GB"]; 35 | const WARP_FEATURES = ["plus", "on"]; 36 | 37 | let args = getArgs(); 38 | 39 | (async () => { 40 | let now = new Date(); 41 | let hour = now.getHours(); 42 | let minutes = now.getMinutes(); 43 | hour = hour > 9 ? hour : "0" + hour; 44 | minutes = minutes > 9 ? minutes : "0" + minutes; 45 | 46 | let panel_result = { 47 | title: `${args.title} | ${hour}:${minutes}` || `解锁检测 | ${hour}:${minutes}`, 48 | content: '', 49 | icon: args.icon || "eye.slash.circle.fill", 50 | "icon-color": args.color || "#ffb621", 51 | }; 52 | 53 | let [{ region, status }] = await Promise.all([testDisneyPlus()]); 54 | let youtubeResult = await check_youtube_premium(); 55 | let netflixResult = await check_netflix(); 56 | 57 | let disney_result = formatDisneyPlusResult(status, region); 58 | let content = `${youtubeResult} ${netflixResult} ${disney_result}`; 59 | 60 | let traceData = await getTraceData(); 61 | let gptSupportStatus = SUPPORTED_LOCATIONS.includes(traceData.loc) ? "G: \u2611" : "G: \u2612"; 62 | 63 | content += ` ${gptSupportStatus}${traceData.loc}`; 64 | 65 | let log = `${hour}:${minutes}.${now.getMilliseconds()} 解锁检测完成:${content}`; 66 | console.log(log); 67 | 68 | panel_result['content'] = content; 69 | 70 | $done(panel_result); 71 | })(); 72 | 73 | function getArgs() { 74 | return Object.fromEntries( 75 | $argument 76 | .split("&") 77 | .map((item) => item.split("=")) 78 | .map(([k, v]) => [k, decodeURIComponent(v)]) 79 | ); 80 | } 81 | 82 | function formatDisneyPlusResult(status, region) { 83 | switch (status) { 84 | case STATUS_COMING: 85 | return `D: 即将登陆~ ${region.toUpperCase()} |`; 86 | case STATUS_AVAILABLE: 87 | return `D: \u2611${region.toUpperCase()} |`; 88 | case STATUS_NOT_AVAILABLE: 89 | return `D: \u2612 |`; 90 | case STATUS_TIMEOUT: 91 | return `D: N/A |`; 92 | default: 93 | return `D: 错误 |`; 94 | } 95 | } 96 | 97 | async function check_youtube_premium() { 98 | let inner_check = () => { 99 | return new Promise((resolve, reject) => { 100 | let option = { 101 | url: 'https://www.youtube.com/premium', 102 | headers: REQUEST_HEADERS, 103 | }; 104 | $httpClient.get(option, function (error, response, data) { 105 | if (error || response.status !== 200) { 106 | reject('Error'); 107 | return; 108 | } 109 | 110 | if (data.indexOf('Premium is not available in your country') !== -1) { 111 | resolve('Not Available'); 112 | return; 113 | } 114 | 115 | let region = ''; 116 | let re = new RegExp('"countryCode":"(.*?)"', 'gm'); 117 | let result = re.exec(data); 118 | if (result && result.length === 2) { 119 | region = result[1]; 120 | } else if (data.indexOf('www.google.cn') !== -1) { 121 | region = 'CN'; 122 | } else { 123 | region = 'US'; 124 | } 125 | resolve(region); 126 | }); 127 | }); 128 | }; 129 | 130 | let youtube_check_result = 'Y: '; 131 | 132 | await inner_check() 133 | .then((code) => { 134 | if (code === 'Not Available') { 135 | youtube_check_result += '\u2612 |'; 136 | } else { 137 | youtube_check_result += "\u2611" + code.toUpperCase() + ' |'; 138 | } 139 | }) 140 | .catch(() => { 141 | youtube_check_result += 'N/A |'; 142 | }); 143 | 144 | return youtube_check_result; 145 | } 146 | 147 | async function check_netflix() { 148 | let inner_check = (filmId) => { 149 | return new Promise((resolve, reject) => { 150 | let option = { 151 | url: 'https://www.netflix.com/title/' + filmId, 152 | headers: REQUEST_HEADERS, 153 | }; 154 | $httpClient.get(option, function (error, response, data) { 155 | if (error) { 156 | reject('Error'); 157 | return; 158 | } 159 | 160 | if (response.status === 403) { 161 | reject('Not Available'); 162 | return; 163 | } 164 | 165 | if (response.status === 404) { 166 | resolve('Not Found'); 167 | return; 168 | } 169 | 170 | if (response.status === 200) { 171 | let url = response.headers['x-originating-url']; 172 | let region = url.split('/')[3]; 173 | region = region.split('-')[0]; 174 | if (region === 'title') { 175 | region = 'US'; 176 | } 177 | resolve(region); 178 | return; 179 | } 180 | 181 | reject('Error'); 182 | }); 183 | }); 184 | }; 185 | 186 | let netflix_check_result = 'N: '; 187 | 188 | await inner_check(81280792) 189 | .then((code) => { 190 | if (code === 'Not Found') { 191 | return inner_check(80018499); 192 | } 193 | netflix_check_result += '\u2611' + code.toUpperCase() + ' |'; 194 | return Promise.reject('BreakSignal'); 195 | }) 196 | .then((code) => { 197 | if (code === 'Not Found') { 198 | return Promise.reject('Not Available'); 199 | } 200 | 201 | netflix_check_result += '⚠' + code.toUpperCase() + ' |'; 202 | return Promise.reject('BreakSignal'); 203 | }) 204 | .catch((error) => { 205 | if (error === 'BreakSignal') { 206 | return; 207 | } 208 | if (error === 'Not Available') { 209 | netflix_check_result += '\u2612 |'; 210 | return; 211 | } 212 | netflix_check_result += 'N/A |'; 213 | }); 214 | 215 | return netflix_check_result; 216 | } 217 | 218 | async function testDisneyPlus() { 219 | try { 220 | let { region, cnbl } = await Promise.race([testHomePage(), timeout(7000)]); 221 | console.log(`homepage: region=${region}, cnbl=${cnbl}`); 222 | let { countryCode, inSupportedLocation } = await Promise.race([getLocationInfo(), timeout(7000)]); 223 | console.log(`getLocationInfo: countryCode=${countryCode}, inSupportedLocation=${inSupportedLocation}`); 224 | 225 | region = countryCode ?? region; 226 | console.log("region:" + region); 227 | // 即将登陆 228 | if (inSupportedLocation === false || inSupportedLocation === 'false') { 229 | return { region, status: STATUS_COMING }; 230 | } else { 231 | // 支持解锁 232 | return { region, status: STATUS_AVAILABLE }; 233 | } 234 | 235 | } catch (error) { 236 | console.log("error:" + error); 237 | 238 | // 不支持解锁 239 | if (error === 'Not Available') { 240 | console.log("不支持"); 241 | return { status: STATUS_NOT_AVAILABLE }; 242 | } 243 | 244 | // 检测超时 245 | if (error === 'Timeout') { 246 | return { status: STATUS_TIMEOUT }; 247 | } 248 | 249 | return { status: STATUS_ERROR }; 250 | } 251 | } 252 | 253 | function getLocationInfo() { 254 | return new Promise((resolve, reject) => { 255 | let opts = { 256 | url: 'https://disney.api.edge.bamgrid.com/graph/v1/device/graphql', 257 | headers: { 258 | 'Accept-Language': 'en', 259 | Authorization: 'ZGlzbmV5JmJyb3dzZXImMS4wLjA.Cu56AgSfBTDag5NiRA81oLHkDZfu5L3CKadnefEAY84', 260 | 'Content-Type': 'application/json', 261 | 'User-Agent': UA, 262 | }, 263 | body: JSON.stringify({ 264 | query: 'mutation registerDevice($input: RegisterDeviceInput!) { registerDevice(registerDevice: $input) { grant { grantType assertion } } }', 265 | variables: { 266 | input: { 267 | applicationRuntime: 'chrome', 268 | attributes: { 269 | browserName: 'chrome', 270 | browserVersion: '94.0.4606', 271 | manufacturer: 'apple', 272 | model: null, 273 | operatingSystem: 'macintosh', 274 | operatingSystemVersion: '10.15.7', 275 | osDeviceIds: [], 276 | }, 277 | deviceFamily: 'browser', 278 | deviceLanguage: 'en', 279 | deviceProfile: 'macosx', 280 | }, 281 | }, 282 | }), 283 | }; 284 | 285 | $httpClient.post(opts, function (error, response, data) { 286 | if (error) { 287 | reject('Error'); 288 | return; 289 | } 290 | 291 | if (response.status !== 200) { 292 | console.log('getLocationInfo: ' + data); 293 | reject('Not Available'); 294 | return; 295 | } 296 | 297 | data = JSON.parse(data); 298 | if (data?.errors) { 299 | console.log('getLocationInfo: ' + data); 300 | reject('Not Available'); 301 | return; 302 | } 303 | 304 | let { 305 | token: { accessToken }, 306 | session: { 307 | inSupportedLocation, 308 | location: { countryCode }, 309 | }, 310 | } = data?.extensions?.sdk; 311 | resolve({ inSupportedLocation, countryCode, accessToken }); 312 | }); 313 | }); 314 | } 315 | 316 | function testHomePage() { 317 | return new Promise((resolve, reject) => { 318 | let opts = { 319 | url: 'https://www.disneyplus.com/', 320 | headers: { 321 | 'Accept-Language': 'en', 322 | 'User-Agent': UA, 323 | }, 324 | }; 325 | 326 | $httpClient.get(opts, function (error, response, data) { 327 | if (error) { 328 | reject('Error'); 329 | return; 330 | } 331 | if (response.status !== 200 || data.indexOf('Sorry, Disney+ is not available in your region.') !== -1) { 332 | reject('Not Available'); 333 | return; 334 | } 335 | 336 | let match = data.match(/Region: ([A-Za-z]{2})[\s\S]*?CNBL: ([12])/); 337 | if (!match) { 338 | resolve({ region: '', cnbl: '' }); 339 | return; 340 | } 341 | 342 | let region = match[1]; 343 | let cnbl = match[2]; 344 | resolve({ region, cnbl }); 345 | }); 346 | }); 347 | } 348 | 349 | function timeout(delay = 5000) { 350 | return new Promise((resolve, reject) => { 351 | setTimeout(() => { 352 | reject('Timeout'); 353 | }, delay); 354 | }); 355 | } 356 | 357 | async function getTraceData() { 358 | return new Promise((resolve, reject) => { 359 | $httpClient.get("http://chat.openai.com/cdn-cgi/trace", function(error, response, data) { 360 | if (error) { 361 | reject(error); 362 | return; 363 | } 364 | let lines = data.split("\n"); 365 | let cf = lines.reduce((acc, line) => { 366 | let [key, value] = line.split("="); 367 | acc[key] = value; 368 | return acc; 369 | }, {}); 370 | resolve(cf); 371 | }); 372 | }); 373 | } 374 | -------------------------------------------------------------------------------- /Script/WageEarner.boxjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "WageEarner.app.sub", 3 | "name": "打工仔 应用订阅", 4 | "author": "@打工仔", 5 | "icon": "https://raw.githubusercontent.com/Former-Years/icon/master/Part-timeJob.png", 6 | "repo": "", 7 | "apps": [ 8 | { 9 | "id": "GrabID", 10 | "name": "超级无线抓取ID", 11 | "keys": ["TG_BOT_TOKEN", "TG_USER_ID", "QUOTATION"], 12 | "settings": [ 13 | { 14 | "id": "TG_BOT_TOKEN", 15 | "name": "TG_BOT_TOKEN", 16 | "val": "", 17 | "type": "text", 18 | "placeholder": "123:ABCD", 19 | "autoGrow": true, 20 | "rows": 2, 21 | "desc": "输入后你将是一个合格的打工人" 22 | }, 23 | { 24 | "id": "TG_USER_ID", 25 | "name": "TG群组/频道ID", 26 | "type": "text", 27 | "val": "-1001393498619", 28 | "placeholder": "TG群组/频道ID", 29 | "autoGrow": true, 30 | "rows": 2, 31 | "desc": "默认为偷撸频道ID-1001393498619" 32 | }, 33 | { 34 | "id": "QUOTATION", 35 | "name": "打工语录", 36 | "type": "text", 37 | "val": "", 38 | "placeholder": "打工人,打工魂!", 39 | "autoGrow": true, 40 | "rows": 8, 41 | "desc": "使用&分割" 42 | } 43 | ], 44 | "author": "@打工仔", 45 | "repo": "", 46 | "script": "", 47 | "icons": [ 48 | "https://raw.githubusercontent.com/Former-Years/icon/master/Part-timeJob.png", 49 | "https://raw.githubusercontent.com/Former-Years/icon/master/Part-timeJob.png" 50 | ] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /Script/daka.js: -------------------------------------------------------------------------------- 1 | const $ = new Env('学掌门打卡'); 2 | const notify = $.isNode() ? require('./sendNotify') : ''; 3 | const API_HOST = 'https://www.atstudy.com'; 4 | // let daka_url = $.getdata('daka_url'); 5 | let daka_hd = $.getdata('daka_hd'); 6 | // let daka_body = $.getdata('daka_body'); 7 | // let KEY_daka_url = 'daka_url'; 8 | let KEY_daka_hd = 'daka_hd'; 9 | // KEY_daka_body = 'daka_body'; 10 | 11 | let Message = "", daka_hd_Arr = []; 12 | 13 | if (isGetUserInfo = typeof $request !== `undefined`) { 14 | GetUserInfo(); 15 | $.done() 16 | } else { 17 | !(async () => { 18 | if ($.isNode()) { 19 | // daka_url = process.env.daka_url || ""; 20 | daka_hd = process.env.daka_hd || ""; 21 | // daka_body = process.env.daka_body || ""; 22 | } 23 | daka_hd = daka_hd.split('@') 24 | Object.keys(daka_hd).forEach((item) => { 25 | daka_hd_Arr.push(daka_hd[item]); 26 | }) 27 | if (!daka_hd_Arr[0]) { 28 | $.msg($.name, '【提示】请先获取daka_hd。'); 29 | return; 30 | } 31 | for (let i = 0; i < daka_hd_Arr.length; i++) { 32 | if (daka_hd_Arr[i]) { 33 | daka_hd = JSON.parse(daka_hd_Arr[i]); 34 | $.index = i + 1; 35 | console.log(`账号 ${$.index} 开始执行\n`); 36 | await ucid(); // 获取课程ID 37 | await $.wait(1000) 38 | await ksdaka(); // 打卡 39 | } 40 | } 41 | if (Message) { 42 | $.msg($.name, '', Message); 43 | if ($.isNode()) await notify.sendNotify($.name, Message); 44 | } 45 | })() 46 | .catch((e) => { 47 | $.log('', `❌ ${$.name}, 失败! 原因: ${e}!`, '') 48 | }) 49 | .finally(() => { 50 | $.done(); 51 | }) 52 | } 53 | 54 | 55 | // 获取 daka_hd 56 | function GetUserInfo() { 57 | // if ($request && $request.url.indexOf("summary") > -1 && $request.headers && $request.body) { 58 | if ($request && $request.url.indexOf("summary") > -1 && $request.headers) { 59 | // $.setdata($request.url, KEY_daka_url) 60 | $.setdata(JSON.stringify($request.headers), KEY_daka_hd) 61 | $.msg($.name, "", "🎉 打卡headers获取成功") 62 | } 63 | } 64 | 65 | 66 | // 获取课程ID 67 | function ucid() { 68 | let opt = { 69 | url: `${API_HOST}/api/consume/UserCourse`, 70 | headers: daka_hd, 71 | } 72 | return new Promise(resolve => { 73 | // console.log(opt) 74 | $.get(opt, (err, resp, data) => { 75 | try { 76 | if (err) { 77 | $.log(err) 78 | } else { 79 | if (data) { 80 | data = JSON.parse(data); 81 | // console.log(data) 82 | if (data.results[0].title == "软件测试就业班全国套课") { 83 | $.ucid = data.results[0].userCourseId 84 | console.log(`🎉 用户课程ID获取成功:${$.ucid}`) 85 | } else { 86 | console.log(`🎉 用户课程ID获取失败,全国套课下标不是第一个`) 87 | } 88 | } else { 89 | $.log("服务器返回了空数据") 90 | } 91 | } 92 | } catch (error) { 93 | $.log(error) 94 | } finally { 95 | resolve(); 96 | } 97 | }) 98 | }) 99 | } 100 | 101 | // 打卡 102 | function ksdaka() { 103 | daka_hd['Content-Type'] = "application/json;charset=UTF-8" 104 | let opt = { 105 | url: `${API_HOST}/api/v2/consume/usercourseattendance`, 106 | headers: daka_hd, 107 | body: `{"attendanceLongitude": 113.301858,"attendanceTime": 1660125556,"userCourseId": ${$.ucid},"campusAddressId": 11,"attendanceLatitude": 23.13756,"attendanceEquipment":"97A26A6A-B9C8-4929-86D6-39A44F9B2B5D"}`, 108 | } 109 | return new Promise(resolve => { 110 | // console.log(opt) 111 | $.post(opt, (err, resp, data) => { 112 | try { 113 | if (err) { 114 | $.log(err) 115 | } else { 116 | if (data) { 117 | // console.log(data) 118 | if (typeof data !== 'number') { 119 | console.log(`🎉 打卡成功:${data}`) 120 | Message += `账号 ${$.index} 🎉 打卡成功:${data}\n\n` 121 | } else { 122 | $.log(`❌ 打卡失败:${data}`) 123 | Message += `账号 ${$.index} ❌ 打卡失败:${data}\n\n` 124 | } 125 | } 126 | } 127 | } catch (error) { 128 | $.log(error) 129 | } finally { 130 | resolve(); 131 | } 132 | }) 133 | }) 134 | } 135 | 136 | 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("", `🔔${this.name}, 开始!`) } 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, n] = i.split("@"), a = { url: `http://${n}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": o, Accept: "*/*" } }; this.post(a, (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), n = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(n); 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: n } = t, a = s.decode(n, this.encoding); e(null, { status: i, statusCode: r, headers: o, rawBody: n, body: a }, a) }, 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: n } = t, a = i.decode(n, this.encoding); e(null, { status: s, statusCode: r, headers: o, rawBody: n, body: a }, a) }, 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 } 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 = "", 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 = ["", "==============📣系统通知📣=============="]; 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("", `❗️${this.name}, 错误!`, t.stack) : this.log("", `❗️${this.name}, 错误!`, t) } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; this.log("", `🔔${this.name}, 结束! 🕛 ${s} 秒`), this.log(), this.isSurge() || this.isQuanX() || this.isLoon() ? $done(t) : this.isNode() && process.exit(1) } }(t, e) } 137 | -------------------------------------------------------------------------------- /Script/erke.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:鸿星尔克签到 3 | 活动入口:鸿星尔克小程序 4 | 环境变量:erke_data(Node环境,多账号以@隔开) 5 | 使用说明:添加重写规则并打开鸿星尔克小程序即可获取Cookie 6 | 更新时间:2024-11-27 7 | 8 | ================ Surge 配置 ================ 9 | [MITM] 10 | hostname = %APPEND% hope.demogic.com 11 | 12 | [Script] 13 | 鸿星尔克Cookie = type=http-request,pattern=^https:\/\/hope\.demogic\.com\/gic-wx-app\/get-member-asset\.json,script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js 14 | 15 | 鸿星尔克 = type=cron, cronexp=15 9 * * *, script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js 16 | 17 | ============ Quantumult X 配置 ============= 18 | [MITM] 19 | hostname = hope.demogic.com 20 | 21 | [rewrite_local] 22 | ^https:\/\/hope\.demogic\.com\/gic-wx-app\/get-member-asset\.json url script-request-header https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js 23 | 24 | [task_local] 25 | 15 9 * * * https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js, tag=鸿星尔克, enabled=true 26 | 27 | ================ Loon 配置 ================ 28 | [MITM] 29 | hostname = hope.demogic.com 30 | 31 | cron "15 9 * * *" script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js, tag=鸿星尔克 32 | 33 | http-request ^https:\/\/hope\.demogic\.com\/gic-wx-app\/get-member-asset\.json script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js, tag=鸿星尔克Cookie 34 | 35 | ================ Boxjs订阅 ================ 36 | 订阅地址:https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/xinian.boxjs.json 37 | 38 | */ 39 | 40 | const $ = new Env('鸿星尔克'); 41 | const notify = $.isNode() ? require('./sendNotify') : ''; 42 | const API_HOST = 'https://hope.demogic.com'; 43 | 44 | let Message = ''; 45 | const KEY_erke_data = 'erke_data'; 46 | 47 | // 判断运行环境 48 | if (typeof $request !== 'undefined') { 49 | captureRequestURL(); // 抓取 URL 数据 50 | $.done(); 51 | } else { 52 | !(async () => { 53 | const requestData = loadRequestData(); 54 | if (!requestData) { 55 | $.msg($.name, '【提示】未找到有效的请求数据', '请先完成抓取或配置环境变量。'); 56 | return; 57 | } 58 | 59 | await executeForMultipleAccounts(requestData); 60 | 61 | if (Message) { 62 | if ($.isNode() && notify) { 63 | // 在 Node 环境下使用 sendNotify 发送通知 64 | await notify.sendNotify($.name, Message); 65 | } else { 66 | // 在非 Node 环境下使用 $.msg 发送通知 67 | $.msg($.name, '', Message); 68 | } 69 | } 70 | })() 71 | .catch((e) => console.error(`❌ ${$.name}, 执行失败: ${e}`)) 72 | .finally(() => $.done()); 73 | } 74 | 75 | // 抓取并存储 URL 数据 76 | function captureRequestURL() { 77 | let savedData = $.getdata(KEY_erke_data) || ''; 78 | const currentURL = $request.url; 79 | 80 | // 提取当前 URL 中的 memberId 81 | const params = extractURLParams(currentURL); 82 | const memberId = params.memberId; 83 | 84 | // 查找是否有相同 memberId 的 URL 85 | const existingData = savedData.split('@').find(accountURL => { 86 | const accountParams = extractURLParams(accountURL); 87 | return accountParams.memberId === memberId; 88 | }); 89 | 90 | if (existingData) { 91 | // 如果找到相同的 memberId,覆盖该 URL 并提示 92 | savedData = savedData.replace(existingData, currentURL); 93 | $.setdata(savedData, KEY_erke_data); // 更新存储数据 94 | $.msg($.name, '', `🎉 对应账号 URL 已存在,已覆盖并保存新 URL`); 95 | } else { 96 | // 如果没有找到相同的 memberId,添加新的 URL 97 | const newData = savedData ? `${savedData}@${currentURL}` : currentURL; 98 | $.setdata(newData, KEY_erke_data); // 更新存储数据 99 | const accountCount = newData.split('@').length; 100 | $.msg($.name, '', `账号 ${accountCount} 🎉 URL 已抓取并保存`); 101 | } 102 | } 103 | 104 | // 加载存储的 URL 数据 105 | function loadRequestData() { 106 | const data = $.getdata(KEY_erke_data) || ($.isNode() ? process.env.erke_data : ''); 107 | if (data) { 108 | console.log('🎉 加载到请求数据'); 109 | return data; 110 | } else { 111 | console.error('❌ 未找到有效的 URL 数据'); 112 | return null; 113 | } 114 | } 115 | 116 | // 执行多账号签到任务 117 | async function executeForMultipleAccounts(data) { 118 | const accounts = data.split('@'); // 按 @ 分隔多个账号 119 | for (let i = 0; i < accounts.length; i++) { 120 | const accountURL = accounts[i].trim(); 121 | if (accountURL) { 122 | console.log(`\n============= 执行账号 ${i + 1} =============`); 123 | await executeForAccount(i + 1, accountURL); 124 | } 125 | } 126 | } 127 | 128 | // 执行单个账号的签到任务 129 | async function executeForAccount(accountNumber, url) { 130 | const accountMessage = []; 131 | try { 132 | const params = extractURLParams(url); // 提取 URL 参数 133 | const requestBody = buildSignInRequestBody(params); 134 | const signInResult = await signInRequest(requestBody); 135 | 136 | if (signInResult.code === '0') { 137 | const todayData = signInResult.result.find(entry => entry.currentDayFlag === 1); 138 | if (todayData) { 139 | const earnedPoints = todayData.memberSignAwards.reduce((acc, award) => 140 | award.type === 'integral' ? acc + award.count : acc, 0 141 | ); 142 | accountMessage.push(`🎉 签到成功: 积分 +${earnedPoints}`); 143 | } else { 144 | accountMessage.push(`❌ 未找到今日签到数据`); 145 | } 146 | } else { 147 | accountMessage.push(`❌ 签到失败: ${signInResult.message}`); 148 | } 149 | 150 | const totalPointsResult = await getTotalPoints(url); 151 | accountMessage.push(`🎉 当前积分: ${totalPointsResult.result.D007 || 0}`); 152 | console.log(`账号 ${accountNumber}:\n${accountMessage.join('\n')}`); 153 | } catch (error) { 154 | accountMessage.push(`❌ 执行失败: ${error.message}`); 155 | } finally { 156 | Message += `账号 ${accountNumber}\n${accountMessage.join('\n')}\n\n`; 157 | } 158 | } 159 | 160 | // 提取 URL 参数 161 | function extractURLParams(url) { 162 | const urlParams = new URLSearchParams(url.split('?')[1]); 163 | const params = {}; 164 | for (const [key, value] of urlParams.entries()) { 165 | params[key] = value; 166 | } 167 | return params; 168 | } 169 | 170 | // 构建签到请求体 171 | function buildSignInRequestBody(params) { 172 | return { 173 | random: params.random, 174 | wxOpenid: params.wxOpenid, 175 | enterpriseId: params.enterpriseId, 176 | appid: params.appid, 177 | gicWxaVersion: params.gicWxaVersion, 178 | timestamp: params.timestamp, 179 | cliqueMemberId: params.cliqueMemberId, 180 | unionid: params.unionid, 181 | source: 'wxapp', 182 | cliqueId: params.cliqueId, 183 | openid: params.openid, 184 | memberId: params.memberId, 185 | transId: params.transId, 186 | useClique: params.useClique, 187 | launchOptions: JSON.stringify({ path: 'pages/pointsmall-index/pointsmall-index', query: {}, scene: 1089, referrerInfo: {}, mode: 'default', apiCategory: 'default' }), 188 | sign: params.sign 189 | }; 190 | } 191 | 192 | // 发起签到请求 193 | function signInRequest(requestBody) { 194 | const signInOptions = { 195 | url: `${API_HOST}/gic-wx-app/sign/member_sign.json`, 196 | headers: { 'Content-Type': 'application/json' }, 197 | body: JSON.stringify(requestBody), 198 | }; 199 | 200 | return new Promise((resolve, reject) => { 201 | $.post(signInOptions, (err, resp, data) => { 202 | if (err) { 203 | console.error(`❌ 签到请求失败: ${err}`); 204 | reject(err); 205 | } else { 206 | try { 207 | resolve(JSON.parse(data)); 208 | } catch (error) { 209 | console.warn('❌ 签到响应解析失败:', error); 210 | reject(new Error(`签到请求解析失败: ${data}`)); 211 | } 212 | } 213 | }); 214 | }); 215 | } 216 | 217 | // 查询积分请求 218 | function getTotalPoints(url) { 219 | const options = { 220 | url: url, 221 | headers: { 'Content-Type': 'application/json' } 222 | }; 223 | 224 | return new Promise((resolve, reject) => { 225 | $.get(options, (err, resp, data) => { 226 | if (err) { 227 | console.error(`❌ 查询积分请求失败: ${err}`); 228 | reject(err); 229 | } else { 230 | try { 231 | resolve(JSON.parse(data)); 232 | } catch (error) { 233 | console.warn('❌ 查询积分响应解析失败:', error); 234 | reject(new Error(`查询积分请求解析失败: ${data}`)); 235 | } 236 | } 237 | }); 238 | }); 239 | } 240 | 241 | 242 | 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; "POST" === e && (s = this.post); const i = new Promise(((e, i) => { s.call(this, t, ((t, s, o) => { t ? i(t) : e(s) })) })); return t.timeout ? ((t, e = 1e3) => Promise.race([t, new Promise(((t, s) => { setTimeout((() => { s(new Error("请求超时")) }), e) }))]))(i, t.timeout) : i } 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: "*/*" }, policy: "DIRECT", 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) } 243 | -------------------------------------------------------------------------------- /Script/http_meta_availability.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 节点测活(适配 Sub-Store Node.js 版) 4 | * 5 | * 说明: https://t.me/zhetengsha/1210 6 | * 7 | * 欢迎加入 Telegram 群组 https://t.me/zhetengsha 8 | * 9 | * HTTP META(https://github.com/xream/http-meta) 参数 10 | * - [http_meta_protocol] 协议 默认: http 11 | * - [http_meta_host] 服务地址 默认: 127.0.0.1 12 | * - [http_meta_port] 端口号 默认: 9876 13 | * - [http_meta_authorization] Authorization 默认无 14 | * - [http_meta_start_delay] 初始启动延时(单位: 毫秒) 默认: 3000 15 | * - [http_meta_proxy_timeout] 每个节点耗时(单位: 毫秒). 此参数是为了防止脚本异常退出未关闭核心. 设置过小将导致核心过早退出. 目前逻辑: 启动初始的延时 + 每个节点耗时. 默认: 10000 16 | * 17 | * 其它参数 18 | * - [timeout] 请求超时(单位: 毫秒) 默认 5000 19 | * - [retries] 重试次数 默认 1 20 | * - [retry_delay] 重试延时(单位: 毫秒) 默认 1000 21 | * - [concurrency] 并发数 默认 10 22 | * - [url] 检测的 URL. 需要 encodeURIComponent. 默认 http://www.apple.com/library/test/success.html 23 | * - [status] 合法的状态码. 默认 200 24 | * - [method] 请求方法. 默认 head, 如果测试 URL 不支持, 可设为 get 25 | * - [show_latency] 显示延迟. 默认不显示. 注: 即使不开启这个参数, 节点上也会添加一个 _latency 字段 26 | * - [keep_incompatible] 保留当前客户端不兼容的协议. 默认不保留. 27 | * - [cache] 使用缓存, 默认不使用缓存 28 | * - [telegram_bot_token] Telegram Bot Token 29 | * - [telegram_chat_id] Telegram Chat ID 30 | */ 31 | 32 | // 定时缓存清除与刷新 33 | const refreshCacheInterval = 2 * 60 * 60 * 1000; // 每2小时刷新一次 34 | setInterval(() => { 35 | $.info('开始清除缓存并重新缓存节点信息...') 36 | clearCache() // 调用清除缓存的函数 37 | // 重新执行节点检测 38 | executeAsyncTasks( 39 | internalProxies.map(proxy => () => check(proxy)), 40 | { concurrency } 41 | ) 42 | }, refreshCacheInterval); 43 | 44 | async function operator(proxies = [], targetPlatform, env) { 45 | const cacheEnabled = $arguments.cache 46 | const cache = scriptResourceCache 47 | const telegram_chat_id = $arguments.telegram_chat_id 48 | const telegram_bot_token = $arguments.telegram_bot_token 49 | const http_meta_host = $arguments.http_meta_host ?? '127.0.0.1' 50 | const http_meta_port = $arguments.http_meta_port ?? 9876 51 | const http_meta_protocol = $arguments.http_meta_protocol ?? 'http' 52 | const http_meta_authorization = $arguments.http_meta_authorization ?? '' 53 | const http_meta_api = `${http_meta_protocol}://${http_meta_host}:${http_meta_port}` 54 | 55 | const http_meta_start_delay = parseFloat($arguments.http_meta_start_delay ?? 3000) 56 | const http_meta_proxy_timeout = parseFloat($arguments.http_meta_proxy_timeout ?? 10000) 57 | 58 | const method = $arguments.method || 'head' 59 | const keepIncompatible = $arguments.keep_incompatible 60 | const validStatus = parseInt($arguments.status || 200) 61 | const url = decodeURIComponent($arguments.url || 'http://www.apple.com/library/test/success.html') 62 | 63 | const $ = $substore 64 | const validProxies = [] 65 | const incompatibleProxies = [] 66 | const internalProxies = [] 67 | const failedProxies = [] 68 | const sub = env.source[proxies?.[0]?._subName || proxies?.[0]?.subName] 69 | const subName = sub?.displayName || sub?.name 70 | 71 | proxies.map((proxy, index) => { 72 | try { 73 | const node = ProxyUtils.produce([{ ...proxy }], 'ClashMeta', 'internal')?.[0] 74 | if (node) { 75 | for (const key in proxy) { 76 | if (/^_/i.test(key)) { 77 | node[key] = proxy[key] 78 | } 79 | } 80 | // $.info(JSON.stringify(node, null, 2)) 81 | internalProxies.push({ ...node, _proxies_index: index }) 82 | } else { 83 | if (keepIncompatible) { 84 | incompatibleProxies.push(proxy) 85 | } 86 | } 87 | } catch (e) { 88 | $.error(e) 89 | } 90 | }) 91 | // $.info(JSON.stringify(internalProxies, null, 2)) 92 | $.info(`核心支持节点数: ${internalProxies.length}/${proxies.length}`) 93 | if (!internalProxies.length) return proxies 94 | 95 | const http_meta_timeout = http_meta_start_delay + internalProxies.length * http_meta_proxy_timeout 96 | 97 | let http_meta_pid 98 | let http_meta_ports = [] 99 | // 启动 HTTP META 100 | const res = await http({ 101 | retries: 0, 102 | method: 'post', 103 | url: `${http_meta_api}/start`, 104 | headers: { 105 | 'Content-type': 'application/json', 106 | Authorization: http_meta_authorization, 107 | }, 108 | body: JSON.stringify({ 109 | proxies: internalProxies, 110 | timeout: http_meta_timeout, 111 | }), 112 | }) 113 | let body = res.body 114 | try { 115 | body = JSON.parse(body) 116 | } catch (e) {} 117 | const { ports, pid } = body 118 | if (!pid || !ports) { 119 | throw new Error(`======== HTTP META 启动失败 ====\n${body}`) 120 | } 121 | http_meta_pid = pid 122 | http_meta_ports = ports 123 | $.info( 124 | `\n======== HTTP META 启动 ====\n[端口] ${ports}\n[PID] ${pid}\n[超时] 若未手动关闭 ${ 125 | Math.round(http_meta_timeout / 60 / 10) / 100 126 | } 分钟后自动关闭\n` 127 | ) 128 | $.info(`等待 ${http_meta_start_delay / 1000} 秒后开始检测`) 129 | await $.wait(http_meta_start_delay) 130 | 131 | const concurrency = parseInt($arguments.concurrency || 10) // 一组并发数 132 | await executeAsyncTasks( 133 | internalProxies.map(proxy => () => check(proxy)), 134 | { concurrency } 135 | ) 136 | // const batches = [] 137 | // for (let i = 0; i < internalProxies.length; i += concurrency) { 138 | // const batch = internalProxies.slice(i, i + concurrency) 139 | // batches.push(batch) 140 | // } 141 | // for (const batch of batches) { 142 | // await Promise.all(batch.map(check)) 143 | // } 144 | 145 | // stop http meta 146 | try { 147 | const res = await http({ 148 | method: 'post', 149 | url: `${http_meta_api}/stop`, 150 | headers: { 151 | 'Content-type': 'application/json', 152 | Authorization: http_meta_authorization, 153 | }, 154 | body: JSON.stringify({ 155 | pid: [http_meta_pid], 156 | }), 157 | }) 158 | $.info(`\n======== HTTP META 关闭 ====\n${JSON.stringify(res, null, 2)}`) 159 | } catch (e) { 160 | $.error(e) 161 | } 162 | 163 | if (telegram_chat_id && telegram_bot_token && failedProxies.length > 0) { 164 | const text = `\`${subName}\` 节点测试:\n${failedProxies 165 | .map(proxy => `❌ [${proxy.type}] \`${proxy.name}\``) 166 | .join('\n')}` 167 | await http({ 168 | method: 'post', 169 | url: `https://api.telegram.org/bot${telegram_bot_token}/sendMessage`, 170 | headers: { 171 | 'Content-Type': 'application/json', 172 | }, 173 | body: JSON.stringify({ chat_id: telegram_chat_id, text, parse_mode: 'MarkdownV2' }), 174 | }) 175 | } 176 | 177 | return keepIncompatible ? [...validProxies, ...incompatibleProxies] : validProxies 178 | 179 | async function check(proxy) { 180 | // $.info(`[${proxy.name}] 检测`) 181 | // $.info(`检测 ${JSON.stringify(proxy, null, 2)}`) 182 | const id = cacheEnabled 183 | ? `http-meta:availability:${url}:${method}:${validStatus}:${JSON.stringify( 184 | Object.fromEntries( 185 | Object.entries(proxy).filter(([key]) => !/^(name|collectionName|subName|id|_.*)$/i.test(key)) 186 | ) 187 | )}` 188 | : undefined 189 | // $.info(`检测 ${id}`) 190 | try { 191 | const cached = cache.get(id) 192 | if (cacheEnabled && cached) { 193 | $.info(`[${proxy.name}] 使用缓存`) 194 | if (cached.latency) { 195 | validProxies.push({ 196 | ...proxy, 197 | name: `${$arguments.show_latency ? `[${cached.latency}] ` : ''}${proxy.name}`, 198 | _latency: cached.latency, 199 | }) 200 | } 201 | return 202 | } 203 | // $.info(JSON.stringify(proxy, null, 2)) 204 | const index = internalProxies.indexOf(proxy) 205 | const startedAt = Date.now() 206 | const res = await http({ 207 | proxy: `http://${http_meta_host}:${http_meta_ports[index]}`, 208 | method, 209 | headers: { 210 | 'User-Agent': 211 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Mobile/15E148 Safari/604.1', 212 | }, 213 | url, 214 | }) 215 | const status = parseInt(res.status || res.statusCode || 200) 216 | let latency = '' 217 | latency = `${Date.now() - startedAt}` 218 | $.info(`[${proxy.name}] status: ${status}, latency: ${latency}`) 219 | // 判断响应 220 | if (status == validStatus) { 221 | validProxies.push({ 222 | ...proxy, 223 | name: `${$arguments.show_latency ? `[${latency}] ` : ''}${proxy.name}`, 224 | _latency: latency, 225 | }) 226 | if (cacheEnabled) { 227 | $.info(`[${proxy.name}] 设置成功缓存`) 228 | cache.set(id, { latency }) 229 | } 230 | } else { 231 | if (cacheEnabled) { 232 | $.info(`[${proxy.name}] 设置失败缓存`) 233 | cache.set(id, {}) 234 | } 235 | failedProxies.push(proxy) 236 | } 237 | } catch (e) { 238 | $.error(`[${proxy.name}] ${e.message ?? e}`) 239 | if (cacheEnabled) { 240 | $.info(`[${proxy.name}] 设置失败缓存`) 241 | cache.set(id, {}) 242 | } 243 | failedProxies.push(proxy) 244 | } 245 | } 246 | // 请求 247 | async function http(opt = {}) { 248 | const METHOD = opt.method || $arguments.method || 'get' 249 | const TIMEOUT = parseFloat(opt.timeout || $arguments.timeout || 5000) 250 | const RETRIES = parseFloat(opt.retries ?? $arguments.retries ?? 1) 251 | const RETRY_DELAY = parseFloat(opt.retry_delay ?? $arguments.retry_delay ?? 1000) 252 | let count = 0 253 | const fn = async () => { 254 | try { 255 | return await $.http[METHOD]({ ...opt, timeout: TIMEOUT }) 256 | } catch (e) { 257 | // $.error(e.message ?? e) 258 | if (count >= RETRIES) throw e 259 | count++ 260 | await $.wait(RETRY_DELAY) 261 | return await fn() 262 | } 263 | } 264 | return await fn() 265 | } 266 | 267 | // 刷新缓存函数 268 | function clearCache() { 269 | $.info('清除缓存...') 270 | cache.clear() 271 | } 272 | 273 | // 执行异步任务 274 | async function executeAsyncTasks(tasks, options) { 275 | const { concurrency = 5 } = options 276 | const taskQueue = [...tasks] 277 | const results = [] 278 | const executing = [] 279 | while (taskQueue.length) { 280 | const task = taskQueue.shift() 281 | const taskPromise = task().finally(() => executing.splice(executing.indexOf(taskPromise), 1)) 282 | executing.push(taskPromise) 283 | results.push(taskPromise) 284 | if (executing.length >= concurrency) { 285 | await Promise.race(executing) 286 | } 287 | } 288 | return Promise.all(results) 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /Script/huke.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: xinian 3 | * @Telegram Messenger: xinian_x 4 | * @Github: https://github.com/58xinian 5 | * 超级感谢:@whyour 6 | * 虎课网APP:这是一个学习做图做视频的,主要签到获取积分换会员学习,也可以换实物 7 | * @Date: 2020-12-8 9:08:08 8 | * @Date: 2020-12-9 12:46:44(加入抽奖,可能需要重新获取Token) 9 | * @Date: 2020-12-27 16:45:44(测试解决需要签到2次才抽奖问题) 10 | 11 | 12 | quanx: 13 | [task_local] 14 | 0 9 * * * https://raw.githubusercontent.com/58xinian/Surge/master/Script/huke.js, tag=虎课网, img-url=https://raw.githubusercontent.com/58xinian/icon/master/huke.png, enabled=true 15 | [rewrite_local] 16 | ^https:\/\/api\.huke88\.com\/v5\/activity\/sign url script-request-header https://raw.githubusercontent.com/58xinian/Surge/master/Script/huke.js 17 | 18 | loon: 19 | [Script] 20 | http-request ^https:\/\/api\.huke88\.com\/v5\/activity\/sign script-path=https://raw.githubusercontent.com/58xinian/Surge/master/Script/huke.js, tag=虎课网cookie 21 | cron "0 9 * * *" script-path=https://raw.githubusercontent.com/58xinian/Surge/master/Script/huke.js, tag=虎课网 22 | 23 | surge: 24 | [Script] 25 | 虎课网 = type=cron,cronexp=0 9 * * *,script-path=https://raw.githubusercontent.com/58xinian/Surge/master/Script/huke.js 26 | 虎课网cookie = type=http-request,pattern=^https:\/\/api\.huke88\.com\/v5\/activity\/sign,script-path=https://raw.githubusercontent.com/58xinian/Surge/master/Script/huke.js 27 | 28 | [MITM] 29 | hostname = api.huke88.com 30 | * 31 | * 32 | **/ 33 | 34 | const hukeTokenKey = "huke_token"; 35 | const hukeHeaderKey = "huke_header"; 36 | const HK_API_HOST = "https://api.huke88.com/"; 37 | const getTokenRegex = /^https:\/\/api\.huke88\.com\/v5\/activity\/sign/; 38 | const $ = new Env("虎课网"); 39 | $.isRequest = typeof $request != "undefined"; 40 | $.result = []; 41 | 42 | !(async () => { 43 | await getSessionId(); 44 | let sign_result = await sign(); 45 | let currentSign = sign_result.data.sign_list.filter(x => x.date === '今天')[0]; 46 | if (currentSign.type === 2) { 47 | await prizeDraw(); 48 | sign_result = await sign(); 49 | } 50 | const { data: { continue_num, today_gold, gold_total }, msg } = sign_result; 51 | $.result.push( 52 | `签到结果: ${msg}, 获得 ${today_gold} 虎课币`, 53 | `账户总共 ${gold_total} 虎课币, 已连续签到 ${continue_num} 天` 54 | ); 55 | await showMsg(); 56 | })() 57 | .catch((e) => $.logErr(e)) 58 | .finally(() => $.done()); 59 | 60 | function getSessionId() { 61 | return new Promise((resolve) => { 62 | if ($.isRequest) { 63 | const url = $request.url; 64 | const headers = $request.headers; 65 | if (getTokenRegex.test(url)) { 66 | try { 67 | $.log("虎课网token响应", JSON.stringify(headers)); 68 | if (!headers["Cookie"]) { 69 | $.logErr(`虎课网写入Token失败,请先手动登录虎课网App`); 70 | } 71 | const token = headers["Cookie"].match(/PHPSESSID\=(\S*)/)[1]; 72 | 73 | $.setdata(token, hukeTokenKey); 74 | $.setdata(JSON.stringify(headers), hukeHeaderKey); 75 | $.log( 76 | `新的Token:\n${token},Token已更新。\n${JSON.stringify(headers)}` 77 | ); 78 | $.msg(`${$.name}Token`, "🎉虎课网写入Token成功!!"); 79 | } catch (err) { 80 | $.logErr(`虎课网写入Token失败,执行异常:${err}。`); 81 | $.msg(`${$.name}Token`, "❌虎课网写入Token失败"); 82 | } finally { 83 | $.done(); 84 | } 85 | } 86 | } else { 87 | resolve(); 88 | } 89 | }); 90 | } 91 | 92 | function prizeDraw() { 93 | return new Promise((resolve) => { 94 | $.post(taskUrl(`activity/prize-draw`), (err, resp, data) => { 95 | try { 96 | if (!data || err || data[0] === "<") { 97 | resolve(); 98 | $.log(`\n抽奖:失败\n${JSON.stringify(err)}\n${data}`); 99 | return; 100 | } 101 | const { 102 | code, 103 | msg, 104 | } = JSON.parse(data); 105 | $.log(`\n${msg}\n${data}`); 106 | $.result.push(msg); 107 | } catch (e) { 108 | $.logErr(e, resp); 109 | } finally { 110 | resolve(); 111 | } 112 | }); 113 | }); 114 | } 115 | 116 | function sign() { 117 | return new Promise((resolve) => { 118 | $.post(taskUrl(`activity/sign`), (err, resp, _data) => { 119 | try { 120 | if (!_data || err || _data[0] === "<") { 121 | resolve(); 122 | $.log(`\n签到:失败\n${JSON.stringify(err)}\n${_data}`); 123 | return; 124 | } 125 | const { 126 | code, 127 | data = {}, 128 | msg, 129 | } = JSON.parse(_data); 130 | $.log(`\n签到:${msg}\n${_data}`); 131 | resolve({ data, msg }); 132 | } catch (e) { 133 | $.logErr(e, resp); 134 | } finally { 135 | resolve(); 136 | } 137 | }); 138 | }); 139 | } 140 | 141 | function showMsg() { 142 | return new Promise((resolve) => { 143 | $.msg($.name, "", `${$.result.join("\n")}`); 144 | resolve(); 145 | }); 146 | } 147 | 148 | function taskUrl(function_path, body = '') { 149 | const headers = JSON.parse($.getdata(hukeHeaderKey)); 150 | return { 151 | url: `${HK_API_HOST}v5/${function_path}`, 152 | body, 153 | headers, 154 | }; 155 | } 156 | 157 | // prettier-ignore 158 | 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.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}toObj(t,e=null){try{return JSON.parse(t)}catch{return e}}toStr(t,e=null){try{return JSON.stringify(t)}catch{return e}}getjson(t,e){let s=e;const i=this.getdata(t);if(i)try{s=JSON.parse(this.getdata(t))}catch{}return s}setjson(t,e){try{return this.setdata(JSON.stringify(t),e)}catch{return!1}}getScript(t){return new Promise(e=>{this.get({url:t},(t,s,i)=>e(i))})}runScript(t,e){return new Promise(s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let r=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");r=r?1*r:20,r=e&&e.timeout?e.timeout:r;const[o,h]=i.split("@"),a={url:`http://${h}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:r},headers:{"X-Key":o,Accept:"*/*"}};this.post(a,(t,e,i)=>s(i))}).catch(t=>this.logErr(t))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e),r=JSON.stringify(this.data);s?this.fs.writeFileSync(t,r):i?this.fs.writeFileSync(e,r):this.fs.writeFileSync(t,r)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let r=t;for(const t of i)if(r=Object(r)[t],void 0===r)return s;return r}lodash_set(t,e,s){return Object(t)!==t?t:(Array.isArray(e)||(e=e.toString().match(/[^.[\]]+/g)||[]),e.slice(0,-1).reduce((t,s,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{},t)[e[e.length-1]]=s,t)}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),r=s?this.getval(s):"";if(r)try{const t=JSON.parse(r);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,r]=/^@(.*?)\.(.*?)$/.exec(e),o=this.getval(i),h=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(h);this.lodash_set(e,r,t),s=this.setval(JSON.stringify(e),i)}catch(e){const o={};this.lodash_set(o,r,t),s=this.setval(JSON.stringify(o),i)}}else s=this.setval(t,e);return s}getval(t){return this.isSurge()||this.isLoon()?$persistentStore.read(t):this.isQuanX()?$prefs.valueForKey(t):this.isNode()?(this.data=this.loaddata(),this.data[t]):this.data&&this.data[t]||null}setval(t,e){return this.isSurge()||this.isLoon()?$persistentStore.write(t,e):this.isQuanX()?$prefs.setValueForKey(t,e):this.isNode()?(this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0):this.data&&this.data[e]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar))}get(t,e=(()=>{})){t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?(this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)})):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))):this.isNode()&&(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:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)}))}post(t,e=(()=>{})){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.post(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)});else if(this.isQuanX())t.method="POST",this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t));else if(this.isNode()){this.initGotEnv(t);const{url:s,...i}=t;this.got.post(s,i).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)})}}time(t){let e={"M+":(new Date).getMonth()+1,"d+":(new Date).getDate(),"H+":(new Date).getHours(),"m+":(new Date).getMinutes(),"s+":(new Date).getSeconds(),"q+":Math.floor(((new Date).getMonth()+3)/3),S:(new Date).getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,((new Date).getFullYear()+"").substr(4-RegExp.$1.length)));for(let s in e)new RegExp("("+s+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?e[s]:("00"+e[s]).substr((""+e[s]).length)));return t}msg(e=t,s="",i="",r){const o=t=>{if(!t)return t;if("string"==typeof t)return this.isLoon()?t:this.isQuanX()?{"open-url":t}:this.isSurge()?{url:t}:void 0;if("object"==typeof t){if(this.isLoon()){let e=t.openUrl||t.url||t["open-url"],s=t.mediaUrl||t["media-url"];return{openUrl:e,mediaUrl:s}}if(this.isQuanX()){let e=t["open-url"]||t.url||t.openUrl,s=t["media-url"]||t.mediaUrl;return{"open-url":e,"media-url":s}}if(this.isSurge()){let e=t.url||t.openUrl||t["open-url"];return{url:e}}}};if(this.isMute||(this.isSurge()||this.isLoon()?$notification.post(e,s,i,o(r)):this.isQuanX()&&$notify(e,s,i,o(r))),!this.isMuteLog){let t=["","==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="];t.push(e),s&&t.push(s),i&&t.push(i),console.log(t.join("\n")),this.logs=this.logs.concat(t)}}log(...t){t.length>0&&(this.logs=[...this.logs,...t]),console.log(t.join(this.logSeparator))}logErr(t,e){const s=!this.isSurge()&&!this.isQuanX()&&!this.isLoon();s?this.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t.stack):this.log("",`\u2757\ufe0f${this.name}, \u9519\u8bef!`,t)}wait(t){return new Promise(e=>setTimeout(e,t))}done(t={}){const e=(new Date).getTime(),s=(e-this.startTime)/1e3;this.log("",`\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${s} \u79d2`),this.log(),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,e)} 159 | -------------------------------------------------------------------------------- /Script/jredrain.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ## Date: 2021-04-08 3 | ## 没错,如果你打开脚本来看就会发现我就是用Ctrl+C, Ctrl+V 大法从lkx0301 大佬 和 e大佬那里抄过来的,版权归他们 4 | ## 5 | 6 | IFS='' read -r -d '' lrr <<"EOF" 7 | /* 8 | 只能用一天。需每天寻找直播抓包 9 | 0,1 8-23 * * * lxk0301_live_redrain.js 10 | */ 11 | const $ = new Env('整点京豆雨'); 12 | let allMessage = ''; 13 | let bodyList = { 14 | '20': { 15 | url: 'https://api.m.jd.com/client.action?functionId=liveActivityV842&uuid=8888888&client=apple&clientVersion=9.4.4&st=1616761511931&sign=1c8a8ab69f27e2bb7b8539dfa5ec3a78&sv=100', 16 | body: 'body=%7B%22liveId%22:%223759184%22%7D' 17 | } 18 | } 19 | let ids = { 20 | '0': 'RRAID', 21 | '1': 'RRAID', 22 | '2': 'RRAID', 23 | '3': 'RRAID', 24 | '4': 'RRAID', 25 | '5': 'RRAID', 26 | '6': 'RRAID', 27 | '7': 'RRAID', 28 | '8': 'RRAID', 29 | '9': 'RRAID', 30 | '10': 'RRAID', 31 | '11': 'RRAID', 32 | '12': 'RRAID', 33 | '13': 'RRAID', 34 | '14': 'RRAID', 35 | '15': 'RRAID', 36 | '16': 'RRAID', 37 | '17': 'RRAID', 38 | '18': 'RRAID', 39 | '19': 'RRAID', 40 | '20': 'RRAID', 41 | '21': 'RRAID', 42 | '22': 'RRAID', 43 | '23': 'RRAID' 44 | } 45 | const notify = $.isNode() ? require('./sendNotify') : ''; 46 | //Node.js用户请在jdCookie.js处填写京东ck; 47 | const jdCookieNode = $.isNode() ? require('./jdCookie.js') : ''; 48 | //IOS等用户直接用NobyDa的jd cookie 49 | let cookiesArr = [], cookie = '', message; 50 | if ($.isNode()) { 51 | Object.keys(jdCookieNode).forEach((item) => { 52 | cookiesArr.push(jdCookieNode[item]) 53 | }) 54 | if (process.env.JD_DEBUG && process.env.JD_DEBUG === 'false') console.log = () => { 55 | }; 56 | if (JSON.stringify(process.env).indexOf('GITHUB') > -1) process.exit(0) 57 | } else { 58 | cookiesArr = [$.getdata('CookieJD'), $.getdata('CookieJD2'), ...jsonParse($.getdata('CookiesJD') || "[]").map(item => item.cookie)].filter(item => !!item); 59 | } 60 | const JD_API_HOST = 'https://api.m.jd.com/api'; 61 | !(async () => { 62 | if (!cookiesArr[0]) { 63 | $.msg($.name, '【提示】请先获取京东账号一cookie\n直接使用NobyDa的京东签到获取', 'https://bean.m.jd.com/', {"open-url": "https://bean.m.jd.com/"}); 64 | return; 65 | } 66 | await getRedRain(); 67 | 68 | let nowTs = new Date().getTime() 69 | // if (!($.st <= nowTs && nowTs < $.ed)) { 70 | $.log(`远程红包雨配置获取错误,从本地读取配置`) 71 | let hour = (new Date().getUTCHours() + 8) % 24 72 | if (ids[hour]) { 73 | $.activityId = ids[hour] 74 | $.log(`本地红包雨配置获取成功`) 75 | } else { 76 | $.log(`无法从本地读取配置,请检查运行时间`) 77 | return 78 | } 79 | // } else{ 80 | // $.log(`远程红包雨配置获取成功`) 81 | // } 82 | for (let i = 0; i < cookiesArr.length; i++) { 83 | if (cookiesArr[i]) { 84 | cookie = cookiesArr[i]; 85 | $.UserName = decodeURIComponent(cookie.match(/pt_pin=([^; ]+)(?=;?)/) && cookie.match(/pt_pin=([^; ]+)(?=;?)/)[1]) 86 | $.index = i + 1; 87 | $.isLogin = true; 88 | $.nickName = ''; 89 | message = ''; 90 | await TotalBean(); 91 | console.log(`\n******开始【京东账号${$.index}】${$.nickName || $.UserName}*********\n`); 92 | if (!$.isLogin) { 93 | $.msg($.name, `【提示】cookie已失效`, `京东账号${$.index} ${$.nickName || $.UserName}\n请重新登录获取\nhttps://bean.m.jd.com/`, {"open-url": "https://bean.m.jd.com/"}); 94 | 95 | if ($.isNode()) { 96 | await notify.sendNotify(`${$.name}cookie已失效 - ${$.UserName}`, `京东账号${$.index} ${$.UserName}\n请重新登录获取cookie`); 97 | } 98 | continue 99 | } 100 | let nowTs = new Date().getTime() + new Date().getTimezoneOffset() * 60 * 1000 + 8 * 60 * 60 * 1000 101 | console.log(nowTs, $.startTime, $.endTime, cookie) 102 | await receiveRedRain(); 103 | // await showMsg(); 104 | } 105 | } 106 | if (allMessage) { 107 | if ($.isNode()) await notify.sendNotify(`${$.name}`, `${allMessage}`); 108 | $.msg($.name, '', allMessage); 109 | } 110 | })() 111 | .catch((e) => { 112 | $.log('', `❌ ${$.name}, 失败! 原因: ${e}!`, '') 113 | }) 114 | .finally(() => { 115 | $.done(); 116 | }) 117 | 118 | function showMsg() { 119 | return new Promise(resolve => { 120 | $.msg($.name, '', `【京东账号${$.index}】${$.nickName}\n${message}`); 121 | resolve() 122 | }) 123 | } 124 | 125 | function getRedRain() { 126 | let body 127 | if (bodyList.hasOwnProperty(new Date().getDate())) { 128 | body = bodyList[new Date().getDate()] 129 | } else { 130 | return 131 | } 132 | return new Promise(resolve => { 133 | $.post(taskGetUrl(body.url, body.body), (err, resp, data) => { 134 | try { 135 | if (err) { 136 | console.log(`${JSON.stringify(err)}`) 137 | console.log(`${$.name} API请求失败,请检查网路重试`) 138 | } else { 139 | if (safeGet(data)) { 140 | data = JSON.parse(data); 141 | if (data.data && data.data.iconArea) { 142 | console.log(data.data.iconArea.filter(vo => vo['type'] === 'anchor_darw_lottery').length && 143 | data.data.iconArea.filter(vo => vo['type'] === 'anchor_darw_lottery')[0].data.lotteryId) 144 | let act = data.data.iconArea.filter(vo => vo['type'] === "platform_red_packege_rain")[0] 145 | if (act) { 146 | let url = act.data.activityUrl 147 | $.activityId = url.substr(url.indexOf("id=") + 3) 148 | $.st = act.startTime 149 | $.ed = act.endTime 150 | console.log($.activityId) 151 | 152 | console.log(`下一场红包雨开始时间:${new Date($.st)}`) 153 | console.log(`下一场红包雨结束时间:${new Date($.ed)}`) 154 | } else { 155 | console.log(`暂无红包雨`) 156 | } 157 | } else { 158 | console.log(`暂无红包雨`) 159 | } 160 | } 161 | } 162 | } catch (e) { 163 | $.logErr(e, resp) 164 | } finally { 165 | resolve(); 166 | } 167 | }) 168 | }) 169 | } 170 | 171 | function receiveRedRain() { 172 | return new Promise(resolve => { 173 | const body = {"actId": $.activityId}; 174 | $.get(taskUrl('noahRedRainLottery', body), (err, resp, data) => { 175 | try { 176 | if (err) { 177 | console.log(`${JSON.stringify(err)}`) 178 | console.log(`${$.name} API请求失败,请检查网路重试`) 179 | } else { 180 | if (safeGet(data)) { 181 | data = JSON.parse(data); 182 | if (data.subCode === '0') { 183 | console.log(`领取成功,获得${JSON.stringify(data.lotteryResult)}`) 184 | // message+= `领取成功,获得${JSON.stringify(data.lotteryResult)}\n` 185 | message += `领取成功,获得 ${(data.lotteryResult.jPeasList[0].quantity)}京豆` 186 | allMessage += `京东账号${$.index}${$.nickName || $.UserName}\n领取成功,获得 ${(data.lotteryResult.jPeasList[0].quantity)}京豆${$.index !== cookiesArr.length ? '\n\n' : ''}`; 187 | } else if (data.subCode === '8') { 188 | console.log(`今日次数已满`) 189 | message += `领取失败,本场已领过`; 190 | } else { 191 | console.log(`异常:${JSON.stringify(data)}`) 192 | } 193 | } 194 | } 195 | } catch (e) { 196 | $.logErr(e, resp) 197 | } finally { 198 | resolve(); 199 | } 200 | }) 201 | }) 202 | } 203 | 204 | function taskGetUrl(url, body) { 205 | return { 206 | url: url, 207 | body: body, 208 | headers: { 209 | "Accept": "*/*", 210 | "Accept-Encoding": "gzip, deflate, br", 211 | "Accept-Language": "zh-cn", 212 | "Connection": "keep-alive", 213 | "Content-Type": "application/x-www-form-urlencoded", 214 | "Host": "api.m.jd.com", 215 | "Referer": `https://h5.m.jd.com/active/redrain/index.html?id=${$.activityId}&lng=0.000000&lat=0.000000&sid=&un_area=`, 216 | "Cookie": cookie, 217 | "User-Agent": "JD4iPhone/9.3.5 CFNetwork/1209 Darwin/20.2.0" 218 | } 219 | } 220 | } 221 | 222 | function taskPostUrl(function_id, body = body) { 223 | return { 224 | url: `https://api.m.jd.com/client.action?functionId=${function_id}`, 225 | body: body, 226 | headers: { 227 | 'Host': 'api.m.jd.com', 228 | 'content-type': 'application/x-www-form-urlencoded', 229 | 'accept': '*/*', 230 | 'user-agent': 'JD4iPhone/167408 (iPhone; iOS 14.2; Scale/3.00)', 231 | 'accept-language': 'zh-Hans-JP;q=1, en-JP;q=0.9, zh-Hant-TW;q=0.8, ja-JP;q=0.7, en-US;q=0.6', 232 | //"Cookie": cookie, 233 | } 234 | } 235 | } 236 | 237 | function taskUrl(function_id, body = {}) { 238 | return { 239 | url: `${JD_API_HOST}?functionId=${function_id}&body=${escape(JSON.stringify(body))}&client=wh5&clientVersion=1.0.0&_=${new Date().getTime() + new Date().getTimezoneOffset() * 60 * 1000 + 8 * 60 * 60 * 1000}`, 240 | headers: { 241 | "Accept": "*/*", 242 | "Accept-Encoding": "gzip, deflate, br", 243 | "Accept-Language": "zh-cn", 244 | "Connection": "keep-alive", 245 | "Content-Type": "application/x-www-form-urlencoded", 246 | "Host": "api.m.jd.com", 247 | "Referer": `https://h5.m.jd.com/active/redrain/index.html?id=${$.activityId}&lng=0.000000&lat=0.000000&sid=&un_area=`, 248 | "Cookie": cookie, 249 | "User-Agent": "JD4iPhone/9.3.5 CFNetwork/1209 Darwin/20.2.0" 250 | } 251 | } 252 | } 253 | 254 | function TotalBean() { 255 | return new Promise(async resolve => { 256 | const options = { 257 | "url": `https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2`, 258 | "headers": { 259 | "Accept": "application/json,text/plain, */*", 260 | "Content-Type": "application/x-www-form-urlencoded", 261 | "Accept-Encoding": "gzip, deflate, br", 262 | "Accept-Language": "zh-cn", 263 | "Connection": "keep-alive", 264 | "Cookie": cookie, 265 | "Referer": "https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2", 266 | "User-Agent": $.isNode() ? (process.env.JD_USER_AGENT ? process.env.JD_USER_AGENT : "JD4iPhone/9.3.5 CFNetwork/1209 Darwin/20.2.0") : ($.getdata('JDUA') ? $.getdata('JDUA') : "JD4iPhone/9.3.5 CFNetwork/1209 Darwin/20.2.0") 267 | } 268 | } 269 | $.post(options, (err, resp, data) => { 270 | try { 271 | if (err) { 272 | console.log(`${JSON.stringify(err)}`) 273 | console.log(`${$.name} API请求失败,请检查网路重试`) 274 | } else { 275 | if (data) { 276 | data = JSON.parse(data); 277 | if (data['retcode'] === 13) { 278 | $.isLogin = false; //cookie过期 279 | return 280 | } 281 | if (data['retcode'] === 0) { 282 | $.nickName = (data['base'] && data['base'].nickname) || $.UserName; 283 | } else { 284 | $.nickName = $.UserName 285 | } 286 | } else { 287 | console.log(`京东服务器返回空数据`) 288 | } 289 | } 290 | } catch (e) { 291 | $.logErr(e, resp) 292 | } finally { 293 | resolve(); 294 | } 295 | }) 296 | }) 297 | } 298 | 299 | function safeGet(data) { 300 | try { 301 | if (typeof JSON.parse(data) == "object") { 302 | return true; 303 | } 304 | } catch (e) { 305 | console.log(e); 306 | console.log(`京东服务器访问数据为空,请检查自身设备网络情况`); 307 | return false; 308 | } 309 | } 310 | 311 | function jsonParse(str) { 312 | if (typeof str == "string") { 313 | try { 314 | return JSON.parse(str); 315 | } catch (e) { 316 | console.log(e); 317 | $.msg($.name, '', '不要在BoxJS手动复制粘贴修改cookie') 318 | return []; 319 | } 320 | } 321 | } 322 | // prettier-ignore 323 | function Env(t,e){"undefined"!=typeof process&&JSON.stringify(process.env).indexOf("GITHUB")>-1&&process.exit(0);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.startTime=(new Date).getTime(),Object.assign(this,e),this.log("",`🔔${this.name}, 开始!`)}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}toObj(t,e=null){try{return JSON.parse(t)}catch{return e}}toStr(t,e=null){try{return JSON.stringify(t)}catch{return e}}getjson(t,e){let s=e;const i=this.getdata(t);if(i)try{s=JSON.parse(this.getdata(t))}catch{}return s}setjson(t,e){try{return this.setdata(JSON.stringify(t),e)}catch{return!1}}getScript(t){return new Promise(e=>{this.get({url:t},(t,s,i)=>e(i))})}runScript(t,e){return new Promise(s=>{let i=this.getdata("@chavy_boxjs_userCfgs.httpapi");i=i?i.replace(/\n/g,"").trim():i;let r=this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout");r=r?1*r:20,r=e&&e.timeout?e.timeout:r;const[o,h]=i.split("@"),n={url:`http://${h}/v1/scripting/evaluate`,body:{script_text:t,mock_type:"cron",timeout:r},headers:{"X-Key":o,Accept:"*/*"}};this.post(n,(t,e,i)=>s(i))}).catch(t=>this.logErr(t))}loaddata(){if(!this.isNode())return{};{this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e);if(!s&&!i)return{};{const i=s?t:e;try{return JSON.parse(this.fs.readFileSync(i))}catch(t){return{}}}}}writedata(){if(this.isNode()){this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const t=this.path.resolve(this.dataFile),e=this.path.resolve(process.cwd(),this.dataFile),s=this.fs.existsSync(t),i=!s&&this.fs.existsSync(e),r=JSON.stringify(this.data);s?this.fs.writeFileSync(t,r):i?this.fs.writeFileSync(e,r):this.fs.writeFileSync(t,r)}}lodash_get(t,e,s){const i=e.replace(/\[(\d+)\]/g,".$1").split(".");let r=t;for(const t of i)if(r=Object(r)[t],void 0===r)return s;return r}lodash_set(t,e,s){return Object(t)!==t?t:(Array.isArray(e)||(e=e.toString().match(/[^.[\]]+/g)||[]),e.slice(0,-1).reduce((t,s,i)=>Object(t[s])===t[s]?t[s]:t[s]=Math.abs(e[i+1])>>0==+e[i+1]?[]:{},t)[e[e.length-1]]=s,t)}getdata(t){let e=this.getval(t);if(/^@/.test(t)){const[,s,i]=/^@(.*?)\.(.*?)$/.exec(t),r=s?this.getval(s):"";if(r)try{const t=JSON.parse(r);e=t?this.lodash_get(t,i,""):e}catch(t){e=""}}return e}setdata(t,e){let s=!1;if(/^@/.test(e)){const[,i,r]=/^@(.*?)\.(.*?)$/.exec(e),o=this.getval(i),h=i?"null"===o?null:o||"{}":"{}";try{const e=JSON.parse(h);this.lodash_set(e,r,t),s=this.setval(JSON.stringify(e),i)}catch(e){const o={};this.lodash_set(o,r,t),s=this.setval(JSON.stringify(o),i)}}else s=this.setval(t,e);return s}getval(t){return this.isSurge()||this.isLoon()?$persistentStore.read(t):this.isQuanX()?$prefs.valueForKey(t):this.isNode()?(this.data=this.loaddata(),this.data[t]):this.data&&this.data[t]||null}setval(t,e){return this.isSurge()||this.isLoon()?$persistentStore.write(t,e):this.isQuanX()?$prefs.setValueForKey(t,e):this.isNode()?(this.data=this.loaddata(),this.data[e]=t,this.writedata(),!0):this.data&&this.data[e]||null}initGotEnv(t){this.got=this.got?this.got:require("got"),this.cktough=this.cktough?this.cktough:require("tough-cookie"),this.ckjar=this.ckjar?this.ckjar:new this.cktough.CookieJar,t&&(t.headers=t.headers?t.headers:{},void 0===t.headers.Cookie&&void 0===t.cookieJar&&(t.cookieJar=this.ckjar))}get(t,e=(()=>{})){t.headers&&(delete t.headers["Content-Type"],delete t.headers["Content-Length"]),this.isSurge()||this.isLoon()?(this.isSurge()&&this.isNeedRewrite&&(t.headers=t.headers||{},Object.assign(t.headers,{"X-Surge-Skip-Scripting":!1})),$httpClient.get(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)})):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))):this.isNode()&&(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:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)}))}post(t,e=(()=>{})){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.post(t,(t,s,i)=>{!t&&s&&(s.body=i,s.statusCode=s.status),e(t,s,i)});else if(this.isQuanX())t.method="POST",this.isNeedRewrite&&(t.opts=t.opts||{},Object.assign(t.opts,{hints:!1})),$task.fetch(t).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>e(t));else if(this.isNode()){this.initGotEnv(t);const{url:s,...i}=t;this.got.post(s,i).then(t=>{const{statusCode:s,statusCode:i,headers:r,body:o}=t;e(null,{status:s,statusCode:i,headers:r,body:o},o)},t=>{const{message:s,response:i}=t;e(s,i,i&&i.body)})}}time(t,e=null){const s=e?new Date(e):new Date;let i={"M+":s.getMonth()+1,"d+":s.getDate(),"H+":s.getHours(),"m+":s.getMinutes(),"s+":s.getSeconds(),"q+":Math.floor((s.getMonth()+3)/3),S:s.getMilliseconds()};/(y+)/.test(t)&&(t=t.replace(RegExp.$1,(s.getFullYear()+"").substr(4-RegExp.$1.length)));for(let e in i)new RegExp("("+e+")").test(t)&&(t=t.replace(RegExp.$1,1==RegExp.$1.length?i[e]:("00"+i[e]).substr((""+i[e]).length)));return t}msg(e=t,s="",i="",r){const o=t=>{if(!t)return t;if("string"==typeof t)return this.isLoon()?t:this.isQuanX()?{"open-url":t}:this.isSurge()?{url:t}:void 0;if("object"==typeof t){if(this.isLoon()){let e=t.openUrl||t.url||t["open-url"],s=t.mediaUrl||t["media-url"];return{openUrl:e,mediaUrl:s}}if(this.isQuanX()){let e=t["open-url"]||t.url||t.openUrl,s=t["media-url"]||t.mediaUrl;return{"open-url":e,"media-url":s}}if(this.isSurge()){let e=t.url||t.openUrl||t["open-url"];return{url:e}}}};if(this.isMute||(this.isSurge()||this.isLoon()?$notification.post(e,s,i,o(r)):this.isQuanX()&&$notify(e,s,i,o(r))),!this.isMuteLog){let t=["","==============📣系统通知📣=============="];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("",`❗️${this.name}, 错误!`,t.stack):this.log("",`❗️${this.name}, 错误!`,t)}wait(t){return new Promise(e=>setTimeout(e,t))}done(t={}){const e=(new Date).getTime(),s=(e-this.startTime)/1e3;this.log("",`🔔${this.name}, 结束! 🕛 ${s} 秒`),this.log(),(this.isSurge()||this.isQuanX()||this.isLoon())&&$done(t)}}(t,e)} 324 | EOF 325 | 326 | if [ ! -d "/ql" ];then 327 | echo "$lrr" | sed -e "s/RRAID/$1/g" > /jd/scripts/lxk0301_live_redrain.js 328 | echo "红包雨RRA替换成$1完成,准备执行脚本" 329 | mtask /jd/scripts/lxk0301_live_redrain.js now 330 | else 331 | echo "$lrr" | sed -e "s/RRAID/$1/g" > /ql/data/scripts/ddSneak_woof/lxk0301_live_redrain.js 332 | echo "红包雨RRA替换成$1完成,准备执行脚本" 333 | task ddSneak_woof/lxk0301_live_redrain.js now 334 | fi 335 | 336 | -------------------------------------------------------------------------------- /Script/qcej.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:去重二剪 3 | 活动入口:去重二剪小程序-会员中心 4 | 环境变量:qcej_token(Node环境,多账号以@隔开) 5 | 使用说明:添加重写规则并打开去重二剪小程序即可获取Token 6 | 更新时间:2024-11-21 7 | 8 | ================ Surge 配置 ================ 9 | [MITM] 10 | hostname = %APPEND% apis.ddfans.com 11 | 12 | [Script] 13 | 去重二剪Cookie = type=http-request,pattern=^https:\/\/apis\.ddfans\.com\/bian\/public\/index\.php\/water,requires-body=1,script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js 14 | 15 | 去重二剪 = type=cron, cronexp=15 9 * * *, timeout=60, script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js 16 | 17 | ============ Quantumult X 配置 ============= 18 | [MITM] 19 | hostname = apis.ddfans.com 20 | 21 | [rewrite_local] 22 | ^https:\/\/apis\.ddfans\.com\/bian\/public\/index\.php\/water url script-request-body https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js 23 | 24 | [task_local] 25 | 15 9 * * * https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js, tag=去重二剪, enabled=true 26 | 27 | ================ Loon 配置 ================ 28 | [MITM] 29 | hostname = apis.ddfans.com 30 | 31 | cron "15 9 * * *" script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js, tag=去重二剪 32 | 33 | http-request ^https:\/\/apis\.ddfans\.com\/bian\/public\/index\.php\/water script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js, requires-body=true, timeout=10, enabled=false, tag=去重二剪Cookie 34 | 35 | ================ Boxjs订阅 ================ 36 | 订阅地址:https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/xinian.boxjs.json 37 | 38 | */ 39 | 40 | const $ = new Env('去重二剪'); 41 | const notify = $.isNode() ? require('./sendNotify') : ''; 42 | const API_HOST = 'https://apis.ddfans.com'; 43 | 44 | let Message = ''; 45 | let token_Arr = []; 46 | const KEY_qcej_token = 'qcej_token'; 47 | 48 | // 判断运行环境 49 | if (typeof $request !== 'undefined') { 50 | GetTokenFromBody(); 51 | $.done(); 52 | } else { 53 | !(async () => { 54 | loadTokens(); // 加载 token 55 | if (!token_Arr.length) { 56 | $.msg($.name, '【提示】未找到有效的 token', '请先完成抓包或配置环境变量。'); 57 | return; 58 | } 59 | 60 | for (let i = 0; i < token_Arr.length; i++) { 61 | const token = token_Arr[i]; 62 | $.index = i + 1; 63 | console.log(`\n====== 开始执行账号 ${$.index} ======\n`); 64 | await executeForAccount(token); 65 | } 66 | 67 | if (Message) { 68 | $.msg($.name, '', Message); 69 | if ($.isNode()) await notify.sendNotify($.name, Message); 70 | } 71 | })() 72 | .catch((e) => { 73 | console.error(`❌ ${$.name}, 执行失败: ${e}`); 74 | }) 75 | .finally(() => { 76 | $.done(); 77 | }); 78 | } 79 | 80 | // 加载 token 81 | function loadTokens() { 82 | let tokens = $.getdata(KEY_qcej_token) || ''; // 从持久化存储读取 83 | if ($.isNode()) tokens = process.env.qcej_token || tokens; // 兼容环境变量 84 | tokens = tokens.split('@').filter(Boolean); // 支持多个账号 85 | token_Arr = [...new Set(tokens)]; // 去重 86 | if (token_Arr.length) console.log(`🎉 加载到 ${token_Arr.length} 个 token`); 87 | } 88 | 89 | // 抓取 token 90 | function GetTokenFromBody() { 91 | if ($request && $request.body) { 92 | const match = $request.body.match(/token=([^&]+)/); 93 | if (match) { 94 | const token = decodeURIComponent(match[1]); 95 | console.log(`🎉 提取到 token: ${token}`); 96 | let savedTokens = $.getdata(KEY_qcej_token) || ''; 97 | const tokens = savedTokens.split('@').filter(Boolean); 98 | if (!tokens.includes(token)) tokens.push(token); 99 | $.setdata(tokens.join('@'), KEY_qcej_token); // 持久化 100 | $.msg($.name, '', '🎉 token 获取成功并已持久化'); 101 | } else { 102 | console.warn('❌ 未找到 token,请检查抓包内容!'); 103 | $.msg($.name, '', '❌ 未找到 token,请检查抓包工具是否正常运行。'); 104 | } 105 | } else { 106 | console.warn('❌ 未捕获到请求体!'); 107 | $.msg($.name, '', '❌ 未捕获到请求体,请检查抓包工具是否正常运行。'); 108 | } 109 | } 110 | 111 | // 执行单账号任务 112 | async function executeForAccount(token) { 113 | const accountMessage = []; 114 | const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 115 | let totalGoldBeans = 0; // 新增变量,记录本次运行的金豆总数 116 | 117 | try { 118 | // 签到 119 | const signInResult = await apiRequest('/bian/public/index.php/water/user/onSign', { token }); 120 | accountMessage.push(`🎉 签到: ${signInResult || '成功'}`); 121 | 122 | // 看广告 123 | for (let i = 0; i < 3; i++) { 124 | const watchResult = await apiRequest('/bian/public/index.php/water/user/onShowAd', { token }); 125 | 126 | // 提取金豆信息 127 | const match = watchResult.match(/金豆 \+(\d+)/); 128 | if (match) totalGoldBeans += parseInt(match[1], 10); // 累加金豆数 129 | 130 | await delay(1000); // 等待 1 秒,避免频繁请求 131 | } 132 | 133 | // 添加金豆统计 134 | accountMessage.push(`🎉 广告: 本次运行共金豆 +${totalGoldBeans}`); 135 | } catch (error) { 136 | console.error(`❌ 账号 ${$.index} 执行失败: ${error}`); 137 | accountMessage.push(`❌ 账号 ${$.index} 执行失败: ${error.message}`); 138 | } finally { 139 | Message += `账号 ${$.index}\n${accountMessage.join('\n')}\n\n`; 140 | } 141 | } 142 | 143 | 144 | // 发起 API 请求 145 | function apiRequest(endpoint, body = {}) { 146 | const options = { 147 | url: `${API_HOST}${endpoint}`, 148 | headers: { 149 | 'Content-Type': 'application/json', 150 | }, 151 | body: JSON.stringify(body), // 将 token 放入请求体 152 | }; 153 | 154 | return new Promise((resolve, reject) => { 155 | $.post(options, (err, resp, data) => { 156 | if (err) { 157 | console.error(`❌ 请求失败: ${err}`); 158 | reject(err); 159 | } else { 160 | try { 161 | const result = JSON.parse(data); 162 | if (result && result.msg) resolve(result.msg); 163 | else resolve('成功'); 164 | } catch (error) { 165 | console.warn('❌ 响应解析失败:', error); 166 | resolve('成功'); 167 | } 168 | } 169 | }); 170 | }); 171 | } 172 | 173 | 174 | 175 | 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; "POST" === e && (s = this.post); const i = new Promise(((e, i) => { s.call(this, t, ((t, s, o) => { t ? i(t) : e(s) })) })); return t.timeout ? ((t, e = 1e3) => Promise.race([t, new Promise(((t, s) => { setTimeout((() => { s(new Error("请求超时")) }), e) }))]))(i, t.timeout) : i } 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: "*/*" }, policy: "DIRECT", 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) } 176 | -------------------------------------------------------------------------------- /Script/sub_info_panel.js: -------------------------------------------------------------------------------- 1 | /* 2 | Surge配置参考注释,感谢@congcong. 3 | 4 | 示例↓↓↓ 5 | ---------------------------------------- 6 | 7 | [Script] 8 | Sub_info = type=generic,timeout=10,script-path=https://raw.githubusercontent.com/mieqq/mieqq/master/sub_info_panel.js,script-update-interval=0,argument=url=[URL encode 后的机场节点链接]&reset_day=1&title=AmyInfo&icon=bonjour&color=#007aff 9 | 10 | [Panel] 11 | Sub_info = script-name=Sub_info,update-interval=600 12 | 13 | ---------------------------------------- 14 | 15 | 先将带有流量信息的节点订阅链接encode,用encode后的链接替换"url="后面的[机场节点链接] 16 | 17 | (实在不会可以用这个捷径生成panel和脚本,https://www.icloud.com/shortcuts/3f24df391d594a73abd04ebdccd92584) 18 | 19 | 可选参数 &reset_day,后面的数字替换成流量每月重置的日期,如1号就写1,8号就写8。如"&reset_day=8",不加该参数不显示流量重置信息。 20 | 21 | 可选参数 &expire,机场链接不带expire信息的,可以手动传入expire参数,如"&expire=2022-02-01",注意一定要按照yyyy-MM-dd的格式。不希望显示到期信息也可以添加&expire=false取消显示。 22 | 23 | 可选参数"title=xxx" 可以自定义标题。 24 | 25 | 可选参数"icon=xxx" 可以自定义图标,内容为任意有效的 SF Symbol Name,如 bolt.horizontal.circle.fill,详细可以下载app https://apps.apple.com/cn/app/sf-symbols-browser/id1491161336 26 | 27 | 可选参数"color=xxx" 当使用 icon 字段时,可传入 color 字段控制图标颜色,字段内容为颜色的 HEX 编码。如:color=#007aff 28 | ---------------------------------------- 29 | */ 30 | 31 | let args = getArgs(); 32 | 33 | (async () => { 34 | let info = await getDataInfo(args.url); 35 | if (!info) $done(); 36 | 37 | let used = info.download + info.upload; 38 | let total = info.total; 39 | let expire = args.expire || info.expire; 40 | 41 | let content = [`用量: ${bytesToSize(used)} | ${bytesToSize(total)}`]; 42 | 43 | if (expire && expire !== "false") { 44 | if (/^[\d.]+$/.test(expire)) expire *= 1000; 45 | } 46 | 47 | if (args["reset_day"] && parseInt(args["reset_day"]) > 0) { 48 | let resetDayLeft = getRemainingDays(parseInt(args["reset_day"])); 49 | content.push(`到期: ${resetDayLeft}天 | ${formatTime(expire)}`); 50 | } else { 51 | content.push(`到期: ${formatTime(expire)}`); 52 | } 53 | 54 | let now = new Date(); 55 | let hour = now.getHours(); 56 | let minutes = now.getMinutes(); 57 | hour = hour > 9 ? hour : "0" + hour; 58 | minutes = minutes > 9 ? minutes : "0" + minutes; 59 | 60 | $done({ 61 | title: `${args.title} | ${hour}:${minutes}`, 62 | content: content.join("\n"), 63 | icon: args.icon || "airplane.circle", 64 | "icon-color": args.color || "#007aff", 65 | }); 66 | })(); 67 | 68 | function getArgs() { 69 | return Object.fromEntries( 70 | $argument 71 | .split("&") 72 | .map((item) => item.split("=")) 73 | .map(([k, v]) => [k, decodeURIComponent(v)]) 74 | ); 75 | } 76 | 77 | function getUserInfo(url) { 78 | let method = args.method || "head"; 79 | let request = { headers: { "User-Agent": "Quantumult%20X" }, url }; 80 | return new Promise((resolve, reject) => 81 | $httpClient[method](request, (err, resp) => { 82 | if (err != null) { 83 | reject(err); 84 | return; 85 | } 86 | if (resp.status !== 200) { 87 | reject(resp.status); 88 | return; 89 | } 90 | let header = Object.keys(resp.headers).find(key => key.toLowerCase() === "subscription-userinfo"); 91 | if (header) { 92 | resolve(resp.headers[header]); 93 | return; 94 | } 95 | reject("链接响应头不带有流量信息"); 96 | }) 97 | ); 98 | } 99 | 100 | async function getDataInfo(url) { 101 | const [err, data] = await getUserInfo(url) 102 | .then(data => [null, data]) 103 | .catch(err => [err, null]); 104 | if (err) { 105 | console.log(err); 106 | return; 107 | } 108 | 109 | return Object.fromEntries( 110 | data 111 | .match(/\w+=[\d.eE+-]+/g) 112 | .map(item => item.split("=")) 113 | .map(([k, v]) => [k, Number(v)]) 114 | ); 115 | } 116 | 117 | function getRemainingDays(resetDay) { 118 | if (!resetDay) return 0; 119 | 120 | let now = new Date(); 121 | let today = now.getDate(); 122 | let month = now.getMonth(); 123 | let year = now.getFullYear(); 124 | let daysInMonth = new Date(year, month + 1, 0).getDate(); 125 | 126 | if (resetDay >= today) { 127 | return resetDay - today; 128 | } else { 129 | return daysInMonth - today + resetDay; 130 | } 131 | } 132 | 133 | function bytesToSize(bytes) { 134 | if (bytes === 0) return "0B"; 135 | let k = 1024; 136 | let sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; 137 | let i = Math.floor(Math.log(bytes) / Math.log(k)); 138 | return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i]; 139 | } 140 | 141 | function formatTime(time) { 142 | let dateObj = new Date(time); 143 | let year = dateObj.getFullYear(); 144 | let month = dateObj.getMonth() + 1; 145 | let day = dateObj.getDate(); 146 | return year + "年" + month + "月" + day + "日"; 147 | } -------------------------------------------------------------------------------- /Script/tjdd.js: -------------------------------------------------------------------------------- 1 | /* 2 | 脚本名称:唐机豆豆 3 | 活动入口:唐机豆豆小程序-会员中心-签到 4 | 环境变量:tjdd_data(Node环境,多账号以@隔开) 5 | 使用说明:添加重写规则并打开唐机豆豆小程序即可获取Token 6 | 更新时间:2024-12-23 7 | 8 | ================ Surge 配置 ================ 9 | [MITM] 10 | hostname = %APPEND% h5.youzan.com 11 | 12 | [Script] 13 | 唐机豆豆Cookie = type=http-request,pattern=^https:\/\/h5\.youzan\.com\/wscshop\/weappp\/shop_business_hour\.json,script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js 14 | 15 | 唐机豆豆 = type=cron, cronexp=15 9 * * *, timeout=60, script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js 16 | 17 | ============ Quantumult X 配置 ============= 18 | [MITM] 19 | hostname = h5.youzan.com 20 | 21 | [rewrite_local] 22 | ^https:\/\/h5\.youzan\.com\/wscshop\/weappp\/shop_business_hour\.json url script-request-header https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js 23 | 24 | [task_local] 25 | 15 9 * * * https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js, tag=唐机豆豆, enabled=true 26 | 27 | ================ Loon 配置 ================ 28 | [MITM] 29 | hostname = h5.youzan.com 30 | 31 | cron "15 9 * * *" script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js, tag=唐机豆豆 32 | 33 | http-request ^https:\/\/h5\.youzan\.com\/wscshop\/weappp\/shop_business_hour\.json script-path=https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js, tag=唐机豆豆Cookie 34 | 35 | ================ Boxjs订阅 ================ 36 | 订阅地址:https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/xinian.boxjs.json 37 | */ 38 | 39 | const $ = new Env('唐机豆豆'); 40 | const notify = $.isNode() ? require('./sendNotify') : ''; 41 | const KEY_TJDD_DATA = 'tjdd_data'; // 存储抓取到的 URL 数据 42 | let message = ''; // 用于存储消息通知内容 43 | 44 | // 判断运行环境 45 | if (typeof $request !== 'undefined') { 46 | captureRequestURL(); // 抓取 URL 数据 47 | $.done(); 48 | } else { 49 | main(); // 进入主流程 50 | } 51 | 52 | // 主流程函数 53 | async function main() { 54 | const requestData = loadRequestData(); 55 | if (!requestData) { 56 | return $.msg($.name, '【提示】未找到有效的请求数据', '请先完成抓取或配置环境变量。'); 57 | } 58 | 59 | await processMultipleAccounts(requestData); 60 | 61 | if (message) { 62 | sendNotification(message); 63 | } 64 | } 65 | 66 | // 发送通知 67 | function sendNotification(content) { 68 | if ($.isNode() && notify) { 69 | notify.sendNotify($.name, content); // Node 环境发送通知 70 | } else { 71 | $.msg($.name, '', content); // 非 Node 环境直接使用 $.msg 72 | } 73 | } 74 | 75 | // 加载存储的 URL 数据 76 | function loadRequestData() { 77 | const data = $.getdata(KEY_TJDD_DATA) || ($.isNode() ? process.env.tjdd_data : ''); 78 | return data || null; 79 | } 80 | 81 | // 执行多个账号签到任务 82 | async function processMultipleAccounts(data) { 83 | const accounts = data.split('@'); // 按 @ 分隔多个账号 84 | for (let i = 0; i < accounts.length; i++) { 85 | const accountData = accounts[i].trim(); 86 | if (accountData) { 87 | console.log(`\n============= 执行账号 ${i + 1} =============`); 88 | await processSingleAccount(i + 1, accountData); 89 | } 90 | } 91 | } 92 | 93 | // 执行单个账号的签到任务 94 | async function processSingleAccount(accountNumber, data) { 95 | const accountMessage = []; 96 | 97 | // 从 data 中提取 access_token、sid 和 uuid 98 | const [accessToken, sid, uuid] = data.split('&'); // 使用 destructuring 来简化提取 99 | 100 | try { 101 | const signInResult = await signInRequest(accessToken, sid, uuid); 102 | 103 | if (signInResult.code === 0) { 104 | // 处理签到成功的情况 105 | const awardInfo = signInResult.data.list && signInResult.data.list[0]?.infos?.title 106 | ? signInResult.data.list[0].infos.title 107 | : "未提供积分信息"; 108 | accountMessage.push(`🎉 签到成功: ${awardInfo}`); 109 | } else if (signInResult.code === 1000030071) { 110 | // 处理已达最大参与次数的情况 111 | accountMessage.push(`❌ 已签到: ${signInResult.msg}`); 112 | } else { 113 | // 处理其他错误 114 | accountMessage.push(`❌ 签到失败: ${signInResult.msg}`); 115 | } 116 | console.log(`账号 ${accountNumber}:\n${accountMessage.join('\n')}`); 117 | } catch (error) { 118 | accountMessage.push(`❌ 执行失败: ${error.message}`); 119 | } finally { 120 | message += `账号 ${accountNumber}\n${accountMessage.join('\n')}\n\n`; 121 | } 122 | } 123 | 124 | // 抓取并存储 URL 数据 125 | function captureRequestURL() { 126 | let savedData = $.getdata(KEY_TJDD_DATA) || ''; 127 | const url = $request.url; // 获取当前请求的 URL 128 | const headers = $request.headers; // 获取请求头 129 | 130 | console.log('请求头:', JSON.stringify(headers)); // 调试:打印请求头 131 | 132 | // 从 URL 中提取 access_token 133 | const urlParams = new URLSearchParams(url.split('?')[1]); 134 | const accessToken = urlParams.get('access_token'); // 获取 access_token 135 | 136 | // 从请求头中提取 extra-data 137 | const extraData = headers['extra-data'] || headers['Extra-Data']; // 根据实际情况,尝试不同的键名 138 | if (extraData) { 139 | try { 140 | const parsedExtraData = JSON.parse(extraData); 141 | const sid = parsedExtraData.sid; 142 | const uuid = parsedExtraData.uuid; 143 | 144 | // 存储 sid 和 uuid 145 | $.setdata(sid, 'sid'); 146 | $.setdata(uuid, 'uuid'); 147 | } catch (error) { 148 | console.error('❌ 解析 Extra-Data 错误:', error); 149 | $.msg($.name, '【错误】解析 Extra-Data 错误', '请确保请求头中的 Extra-Data 格式正确'); 150 | return; 151 | } 152 | } else { 153 | console.error('❌ 未找到 Extra-Data 请求头'); 154 | $.msg($.name, '【错误】未找到 Extra-Data 请求头', '请确保请求头包含正确的 Extra-Data'); 155 | return; 156 | } 157 | 158 | if (accessToken) { 159 | // 合并 access_token、sid 和 uuid,用 & 分隔 160 | const sid = $.getdata('sid'); 161 | const uuid = $.getdata('uuid'); 162 | const accountData = `${accessToken}&${sid}&${uuid}`; 163 | 164 | // 计算当前账号的数量 165 | const accountCount = savedData.split('@').length; 166 | 167 | // 更新存储数据,避免重复存储 168 | savedData = savedData.split('@').includes(accountData) 169 | ? savedData // 如果已存在该 token,则不添加 170 | : savedData ? `${savedData}@${accountData}` : accountData; 171 | 172 | $.setdata(savedData, KEY_TJDD_DATA); // 更新存储数据 173 | $.msg($.name, '', `账号 ${accountCount} 🎉 数据已抓取并保存`); 174 | } else { 175 | console.error('❌ 缺少 access_token'); 176 | $.msg($.name, '【错误】缺少必要的参数', '无法抓取有效的数据'); 177 | } 178 | } 179 | 180 | // 发起签到请求 181 | function signInRequest(accessToken, sid, uuid) { 182 | if (!sid || !uuid) { 183 | console.error('❌ sid 或 uuid 不存在'); 184 | return Promise.reject(new Error('缺少 sid 或 uuid')); 185 | } 186 | 187 | // 构建 extraData 188 | const extraData = JSON.stringify({ 189 | is_weapp: 1, 190 | sid: sid, 191 | version: '2.164.10.101', 192 | client: 'weapp', 193 | bizEnv: 'wsc', 194 | uuid: uuid, 195 | ftime: Date.now(), 196 | }); 197 | 198 | const signInOptions = { 199 | url: `https://h5.youzan.com/wscump/checkin/checkinV2.json?checkinId=1997&access_token=${accessToken}`, 200 | method: 'GET', 201 | headers: { 202 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.54(0x18003631) NetType/4G Language/zh_CN', 203 | 'Extra-Data': extraData, 204 | }, 205 | }; 206 | 207 | return new Promise((resolve, reject) => { 208 | $.get(signInOptions, (err, resp, data) => { 209 | if (err) { 210 | console.error(`❌ 签到请求失败: ${err}`); 211 | reject(err); 212 | } else { 213 | try { 214 | resolve(JSON.parse(data)); 215 | } catch (error) { 216 | console.warn('❌ 签到响应解析失败:', error); 217 | reject(new Error(`签到请求解析失败: ${data}`)); 218 | } 219 | } 220 | }); 221 | }); 222 | } 223 | 224 | 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; "POST" === e && (s = this.post); const i = new Promise(((e, i) => { s.call(this, t, ((t, s, o) => { t ? i(t) : e(s) })) })); return t.timeout ? ((t, e = 1e3) => Promise.race([t, new Promise(((t, s) => { setTimeout((() => { s(new Error("请求超时")) }), e) }))]))(i, t.timeout) : i } 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: "*/*" }, policy: "DIRECT", 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) } 225 | -------------------------------------------------------------------------------- /Script/tls.js: -------------------------------------------------------------------------------- 1 | /**************************************** 2 | @Author: Sliverkiss 3 | @Date: 2023-08-04 21:25:27 4 | @tg频道: https://github.com/Sliverkiss 5 | @Description: 6 | 微信小程序 特仑苏官方商城 日常签到领积分,积分可以兑换实物 7 | 使用教程: 8 | 1.复制Cookie脚本到本地 9 | 2.打开微信小程序->个人界面,若提示获取cookie成功则可以使用签到脚本 10 | 3.关闭获取token脚本 11 | 12 | 【Loon】 : 13 | ************************* 14 | [Script] 15 | cron "30 0 7 * * *" script-path=https://raw.githubusercontent.com/Sliverkiss/helloworld/master/Study/tls.js, timeout=300, tag=特仑苏官方商城 16 | http-request ^https:\/\/mall\.telunsu\.net\/wxapi\/rest\/getUser\?openid=.+ script-path=https://raw.githubusercontent.com/Sliverkiss/helloworld/master/Study/tls.js, timeout=10, tag=特仑苏官方商城获取token 17 | ************************* 18 | 19 | [MITM] 20 | hostname = mall.telunsu.net 21 | 22 | ************************* 23 | ⚠️【免责声明】 24 | ------------------------------------------ 25 | 1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 26 | 2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 27 | 3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。 28 | 4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 29 | 5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 30 | 6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。 31 | 7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。 32 | 33 | ******************************************/ 34 | 35 | // env.js 全局 36 | const $ = new Env("特仑苏官方商城"); 37 | 38 | //环境变量 39 | const env_name = "tls_data";//环境变量名字 40 | const env = $.getdata(env_name) 41 | 42 | //通知相关 43 | var message = ""; 44 | var account; 45 | var user; 46 | 47 | //主程序执行入口 48 | !(async () => { 49 | //没有设置变量,执行Cookie获取 50 | if (typeof $request != "undefined") { 51 | getCookie(); 52 | return; 53 | } 54 | //开始执行日常签到 55 | await signin(); 56 | await status(); 57 | await notify(); 58 | })() 59 | .catch((e) => { 60 | $.log("", `❌失败! 原因: ${e}!`, ""); 61 | }) 62 | .finally(() => { 63 | $.done(); 64 | }); 65 | 66 | //签到函数 67 | function signin() { 68 | return new Promise((resolve) => { 69 | const signinRequest = { 70 | //签到任务调用签到接口 71 | url: "https://mall.telunsu.net/wxapi/user/signIn", 72 | //请求头, 所有接口通用 73 | headers: { 74 | 'Host': "mall.telunsu.net", 75 | 'Content-Length': '41', 76 | 'Content-Type': 'application/json;charset=UTF-8', 77 | }, 78 | body: JSON.stringify({ 79 | "openid": env 80 | }) 81 | }; 82 | //post方法 83 | $.post(signinRequest, (error, response, data) => { 84 | try { 85 | var result = JSON.parse(data); 86 | //成功时返回{"error":0} 87 | if (result?.msg == '签到成功') { 88 | //obj.error是0代表完成 89 | message += `签到:${result?.msg}\n`; 90 | } else if (result?.data == '100023') { 91 | message += `签到:${result.msg},请勿重复签到\n`; 92 | } else { 93 | message+=`❌签到失败,原因未知!\n` 94 | } 95 | } catch (e) { 96 | $.logErr(e,"❌请重新登陆更新Cookie"); 97 | } finally { 98 | resolve(); 99 | } 100 | }); 101 | }); 102 | } 103 | 104 | 105 | //查询积分函数 106 | function status() { 107 | return new Promise((resolve) => { 108 | const statusRequest = { 109 | url: `https://mall.telunsu.net/wxapi/rest/getUser?openid=${env}`, 110 | headers: { 111 | 'Host': `mall.telunsu.net`, 112 | 'Content-Length': '41', 113 | 'Content-Type': 'application/json;charset=UTF-8', 114 | }, 115 | body: JSON.stringify({}) 116 | }; 117 | $.post(statusRequest, (error, response, data) => { 118 | var result = JSON.parse(data); 119 | if (result?.code == 0) { 120 | message+=`当前积分:${result?.data.mnCommonUser.integral}` 121 | } else { 122 | $.msg($.name, "", "❌请重新登陆更新Cookie"); 123 | } 124 | resolve(); 125 | }); 126 | }); 127 | } 128 | 129 | //获取Cookie 130 | function getCookie() { 131 | if ($request && $request.method != 'OPTIONS') { 132 | const signurl = $request.url 133 | let ck_info=signurl.split('='); 134 | let openId=ck_info[1]; 135 | $.setdata(openId, env_name); 136 | $.msg($.name, "", "获取签到Cookie成功🎉"); 137 | } 138 | } 139 | //通知函数 140 | async function notify() { 141 | $.msg($.name, "", message); 142 | } 143 | 144 | /** ---------------------------------固定不动区域----------------------------------------- */ 145 | 146 | //From chavyleung's Env.js 147 | function Env(name, opts) { 148 | class Http { 149 | constructor(env) { 150 | this.env = env; 151 | } 152 | 153 | send(opts, method = "GET") { 154 | opts = typeof opts === "string" ? { url: opts } : opts; 155 | let sender = this.get; 156 | if (method === "POST") { 157 | sender = this.post; 158 | } 159 | return new Promise((resolve, reject) => { 160 | sender.call(this, opts, (err, resp, body) => { 161 | if (err) reject(err); 162 | else resolve(resp); 163 | }); 164 | }); 165 | } 166 | 167 | get(opts) { 168 | return this.send.call(this.env, opts); 169 | } 170 | 171 | post(opts) { 172 | return this.send.call(this.env, opts, "POST"); 173 | } 174 | } 175 | 176 | return new (class { 177 | constructor(name, opts) { 178 | this.name = name; 179 | this.http = new Http(this); 180 | this.data = null; 181 | this.dataFile = "box.dat"; 182 | this.logs = []; 183 | this.isMute = false; 184 | this.isNeedRewrite = false; 185 | this.logSeparator = "\n"; 186 | this.startTime = new Date().getTime(); 187 | Object.assign(this, opts); 188 | this.log("", `🔔${this.name}, 开始!`); 189 | } 190 | 191 | isNode() { 192 | return "undefined" !== typeof module && !!module.exports; 193 | } 194 | 195 | isQuanX() { 196 | return "undefined" !== typeof $task; 197 | } 198 | 199 | isSurge() { 200 | return "undefined" !== typeof $httpClient && "undefined" === typeof $loon; 201 | } 202 | 203 | isLoon() { 204 | return "undefined" !== typeof $loon; 205 | } 206 | 207 | toObj(str, defaultValue = null) { 208 | try { 209 | return JSON.parse(str); 210 | } catch { 211 | return defaultValue; 212 | } 213 | } 214 | 215 | toStr(obj, defaultValue = null) { 216 | try { 217 | return JSON.stringify(obj); 218 | } catch { 219 | return defaultValue; 220 | } 221 | } 222 | 223 | getjson(key, defaultValue) { 224 | let json = defaultValue; 225 | const val = this.getdata(key); 226 | if (val) { 227 | try { 228 | json = JSON.parse(this.getdata(key)); 229 | } catch { } 230 | } 231 | return json; 232 | } 233 | 234 | setjson(val, key) { 235 | try { 236 | return this.setdata(JSON.stringify(val), key); 237 | } catch { 238 | return false; 239 | } 240 | } 241 | 242 | getScript(url) { 243 | return new Promise((resolve) => { 244 | this.get({ url }, (err, resp, body) => resolve(body)); 245 | }); 246 | } 247 | 248 | runScript(script, runOpts) { 249 | return new Promise((resolve) => { 250 | let httpapi = this.getdata("@chavy_boxjs_userCfgs.httpapi"); 251 | httpapi = httpapi ? httpapi.replace(/\n/g, "").trim() : httpapi; 252 | let httpapi_timeout = this.getdata( 253 | "@chavy_boxjs_userCfgs.httpapi_timeout" 254 | ); 255 | httpapi_timeout = httpapi_timeout ? httpapi_timeout * 1 : 20; 256 | httpapi_timeout = 257 | runOpts && runOpts.timeout ? runOpts.timeout : httpapi_timeout; 258 | const [key, addr] = httpapi.split("@"); 259 | const opts = { 260 | url: `http://${addr}/v1/scripting/evaluate`, 261 | body: { 262 | script_text: script, 263 | mock_type: "cron", 264 | timeout: httpapi_timeout, 265 | }, 266 | headers: { "X-Key": key, Accept: "*/*" }, 267 | }; 268 | this.post(opts, (err, resp, body) => resolve(body)); 269 | }).catch((e) => this.logErr(e)); 270 | } 271 | 272 | loaddata() { 273 | if (this.isNode()) { 274 | this.fs = this.fs ? this.fs : require("fs"); 275 | this.path = this.path ? this.path : require("path"); 276 | const curDirDataFilePath = this.path.resolve(this.dataFile); 277 | const rootDirDataFilePath = this.path.resolve( 278 | process.cwd(), 279 | this.dataFile 280 | ); 281 | const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath); 282 | const isRootDirDataFile = 283 | !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath); 284 | if (isCurDirDataFile || isRootDirDataFile) { 285 | const datPath = isCurDirDataFile 286 | ? curDirDataFilePath 287 | : rootDirDataFilePath; 288 | try { 289 | return JSON.parse(this.fs.readFileSync(datPath)); 290 | } catch (e) { 291 | return {}; 292 | } 293 | } else return {}; 294 | } else return {}; 295 | } 296 | 297 | writedata() { 298 | if (this.isNode()) { 299 | this.fs = this.fs ? this.fs : require("fs"); 300 | this.path = this.path ? this.path : require("path"); 301 | const curDirDataFilePath = this.path.resolve(this.dataFile); 302 | const rootDirDataFilePath = this.path.resolve( 303 | process.cwd(), 304 | this.dataFile 305 | ); 306 | const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath); 307 | const isRootDirDataFile = 308 | !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath); 309 | const jsondata = JSON.stringify(this.data); 310 | if (isCurDirDataFile) { 311 | this.fs.writeFileSync(curDirDataFilePath, jsondata); 312 | } else if (isRootDirDataFile) { 313 | this.fs.writeFileSync(rootDirDataFilePath, jsondata); 314 | } else { 315 | this.fs.writeFileSync(curDirDataFilePath, jsondata); 316 | } 317 | } 318 | } 319 | 320 | lodash_get(source, path, defaultValue = undefined) { 321 | const paths = path.replace(/\[(\d+)\]/g, ".$1").split("."); 322 | let result = source; 323 | for (const p of paths) { 324 | result = Object(result)[p]; 325 | if (result === undefined) { 326 | return defaultValue; 327 | } 328 | } 329 | return result; 330 | } 331 | 332 | lodash_set(obj, path, value) { 333 | if (Object(obj) !== obj) return obj; 334 | if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || []; 335 | path 336 | .slice(0, -1) 337 | .reduce( 338 | (a, c, i) => 339 | Object(a[c]) === a[c] 340 | ? a[c] 341 | : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {}), 342 | obj 343 | )[path[path.length - 1]] = value; 344 | return obj; 345 | } 346 | 347 | getdata(key) { 348 | let val = this.getval(key); 349 | // 如果以 @ 350 | if (/^@/.test(key)) { 351 | const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key); 352 | const objval = objkey ? this.getval(objkey) : ""; 353 | if (objval) { 354 | try { 355 | const objedval = JSON.parse(objval); 356 | val = objedval ? this.lodash_get(objedval, paths, "") : val; 357 | } catch (e) { 358 | val = ""; 359 | } 360 | } 361 | } 362 | return val; 363 | } 364 | 365 | setdata(val, key) { 366 | let issuc = false; 367 | if (/^@/.test(key)) { 368 | const [, objkey, paths] = /^@(.*?)\.(.*?)$/.exec(key); 369 | const objdat = this.getval(objkey); 370 | const objval = objkey 371 | ? objdat === "null" 372 | ? null 373 | : objdat || "{}" 374 | : "{}"; 375 | try { 376 | const objedval = JSON.parse(objval); 377 | this.lodash_set(objedval, paths, val); 378 | issuc = this.setval(JSON.stringify(objedval), objkey); 379 | } catch (e) { 380 | const objedval = {}; 381 | this.lodash_set(objedval, paths, val); 382 | issuc = this.setval(JSON.stringify(objedval), objkey); 383 | } 384 | } else { 385 | issuc = this.setval(val, key); 386 | } 387 | return issuc; 388 | } 389 | 390 | getval(key) { 391 | if (this.isSurge() || this.isLoon()) { 392 | return $persistentStore.read(key); 393 | } else if (this.isQuanX()) { 394 | return $prefs.valueForKey(key); 395 | } else if (this.isNode()) { 396 | this.data = this.loaddata(); 397 | return this.data[key]; 398 | } else { 399 | return (this.data && this.data[key]) || null; 400 | } 401 | } 402 | 403 | setval(val, key) { 404 | if (this.isSurge() || this.isLoon()) { 405 | return $persistentStore.write(val, key); 406 | } else if (this.isQuanX()) { 407 | return $prefs.setValueForKey(val, key); 408 | } else if (this.isNode()) { 409 | this.data = this.loaddata(); 410 | this.data[key] = val; 411 | this.writedata(); 412 | return true; 413 | } else { 414 | return (this.data && this.data[key]) || null; 415 | } 416 | } 417 | 418 | initGotEnv(opts) { 419 | this.got = this.got ? this.got : require("got"); 420 | this.cktough = this.cktough ? this.cktough : require("tough-cookie"); 421 | this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar(); 422 | if (opts) { 423 | opts.headers = opts.headers ? opts.headers : {}; 424 | if (undefined === opts.headers.Cookie && undefined === opts.cookieJar) { 425 | opts.cookieJar = this.ckjar; 426 | } 427 | } 428 | } 429 | 430 | get(opts, callback = () => { }) { 431 | if (opts.headers) { 432 | delete opts.headers["Content-Type"]; 433 | delete opts.headers["Content-Length"]; 434 | } 435 | if (this.isSurge() || this.isLoon()) { 436 | if (this.isSurge() && this.isNeedRewrite) { 437 | opts.headers = opts.headers || {}; 438 | Object.assign(opts.headers, { "X-Surge-Skip-Scripting": false }); 439 | } 440 | $httpClient.get(opts, (err, resp, body) => { 441 | if (!err && resp) { 442 | resp.body = body; 443 | resp.statusCode = resp.status; 444 | } 445 | callback(err, resp, body); 446 | }); 447 | } else if (this.isQuanX()) { 448 | if (this.isNeedRewrite) { 449 | opts.opts = opts.opts || {}; 450 | Object.assign(opts.opts, { hints: false }); 451 | } 452 | $task.fetch(opts).then( 453 | (resp) => { 454 | const { statusCode: status, statusCode, headers, body } = resp; 455 | callback(null, { status, statusCode, headers, body }, body); 456 | }, 457 | (err) => callback(err) 458 | ); 459 | } else if (this.isNode()) { 460 | this.initGotEnv(opts); 461 | this.got(opts) 462 | .on("redirect", (resp, nextOpts) => { 463 | try { 464 | if (resp.headers["set-cookie"]) { 465 | const ck = resp.headers["set-cookie"] 466 | .map(this.cktough.Cookie.parse) 467 | .toString(); 468 | if (ck) { 469 | this.ckjar.setCookieSync(ck, null); 470 | } 471 | nextOpts.cookieJar = this.ckjar; 472 | } 473 | } catch (e) { 474 | this.logErr(e); 475 | } 476 | // this.ckjar.setCookieSync(resp.headers['set-cookie'].map(Cookie.parse).toString()) 477 | }) 478 | .then( 479 | (resp) => { 480 | const { statusCode: status, statusCode, headers, body } = resp; 481 | callback(null, { status, statusCode, headers, body }, body); 482 | }, 483 | (err) => { 484 | const { message: error, response: resp } = err; 485 | callback(error, resp, resp && resp.body); 486 | } 487 | ); 488 | } 489 | } 490 | 491 | post(opts, callback = () => { }) { 492 | // 如果指定了请求体, 但没指定`Content-Type`, 则自动生成 493 | if (opts.body && opts.headers && !opts.headers["Content-Type"]) { 494 | opts.headers["Content-Type"] = "application/x-www-form-urlencoded"; 495 | } 496 | if (opts.headers) delete opts.headers["Content-Length"]; 497 | if (this.isSurge() || this.isLoon()) { 498 | if (this.isSurge() && this.isNeedRewrite) { 499 | opts.headers = opts.headers || {}; 500 | Object.assign(opts.headers, { "X-Surge-Skip-Scripting": false }); 501 | } 502 | $httpClient.post(opts, (err, resp, body) => { 503 | if (!err && resp) { 504 | resp.body = body; 505 | resp.statusCode = resp.status; 506 | } 507 | callback(err, resp, body); 508 | }); 509 | } else if (this.isQuanX()) { 510 | opts.method = "POST"; 511 | if (this.isNeedRewrite) { 512 | opts.opts = opts.opts || {}; 513 | Object.assign(opts.opts, { hints: false }); 514 | } 515 | $task.fetch(opts).then( 516 | (resp) => { 517 | const { statusCode: status, statusCode, headers, body } = resp; 518 | callback(null, { status, statusCode, headers, body }, body); 519 | }, 520 | (err) => callback(err) 521 | ); 522 | } else if (this.isNode()) { 523 | this.initGotEnv(opts); 524 | const { url, ..._opts } = opts; 525 | this.got.post(url, _opts).then( 526 | (resp) => { 527 | const { statusCode: status, statusCode, headers, body } = resp; 528 | callback(null, { status, statusCode, headers, body }, body); 529 | }, 530 | (err) => { 531 | const { message: error, response: resp } = err; 532 | callback(error, resp, resp && resp.body); 533 | } 534 | ); 535 | } 536 | } 537 | /** 538 | * 539 | * 示例:$.time('yyyy-MM-dd qq HH:mm:ss.S') 540 | * :$.time('yyyyMMddHHmmssS') 541 | * y:年 M:月 d:日 q:季 H:时 m:分 s:秒 S:毫秒 542 | * 其中y可选0-4位占位符、S可选0-1位占位符,其余可选0-2位占位符 543 | * @param {string} fmt 格式化参数 544 | * @param {number} 可选: 根据指定时间戳返回格式化日期 545 | * 546 | */ 547 | time(fmt, ts = null) { 548 | const date = ts ? new Date(ts) : new Date(); 549 | let o = { 550 | "M+": date.getMonth() + 1, 551 | "d+": date.getDate(), 552 | "H+": date.getHours(), 553 | "m+": date.getMinutes(), 554 | "s+": date.getSeconds(), 555 | "q+": Math.floor((date.getMonth() + 3) / 3), 556 | S: date.getMilliseconds(), 557 | }; 558 | if (/(y+)/.test(fmt)) 559 | fmt = fmt.replace( 560 | RegExp.$1, 561 | (date.getFullYear() + "").substr(4 - RegExp.$1.length) 562 | ); 563 | for (let k in o) 564 | if (new RegExp("(" + k + ")").test(fmt)) 565 | fmt = fmt.replace( 566 | RegExp.$1, 567 | RegExp.$1.length == 1 568 | ? o[k] 569 | : ("00" + o[k]).substr(("" + o[k]).length) 570 | ); 571 | return fmt; 572 | } 573 | 574 | /** 575 | * 系统通知 576 | * 577 | * > 通知参数: 同时支持 QuanX 和 Loon 两种格式, EnvJs根据运行环境自动转换, Surge 环境不支持多媒体通知 578 | * 579 | * 示例: 580 | * $.msg(title, subt, desc, 'twitter://') 581 | * $.msg(title, subt, desc, { 'open-url': 'twitter://', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) 582 | * $.msg(title, subt, desc, { 'open-url': 'https://bing.com', 'media-url': 'https://github.githubassets.com/images/modules/open_graph/github-mark.png' }) 583 | * 584 | * @param {*} title 标题 585 | * @param {*} subt 副标题 586 | * @param {*} desc 通知详情 587 | * @param {*} opts 通知参数 588 | * 589 | */ 590 | msg(title = name, subt = "", desc = "", opts) { 591 | const toEnvOpts = (rawopts) => { 592 | if (!rawopts) return rawopts; 593 | if (typeof rawopts === "string") { 594 | if (this.isLoon()) return rawopts; 595 | else if (this.isQuanX()) return { "open-url": rawopts }; 596 | else if (this.isSurge()) return { url: rawopts }; 597 | else return undefined; 598 | } else if (typeof rawopts === "object") { 599 | if (this.isLoon()) { 600 | let openUrl = rawopts.openUrl || rawopts.url || rawopts["open-url"]; 601 | let mediaUrl = rawopts.mediaUrl || rawopts["media-url"]; 602 | return { openUrl, mediaUrl }; 603 | } else if (this.isQuanX()) { 604 | let openUrl = rawopts["open-url"] || rawopts.url || rawopts.openUrl; 605 | let mediaUrl = rawopts["media-url"] || rawopts.mediaUrl; 606 | return { "open-url": openUrl, "media-url": mediaUrl }; 607 | } else if (this.isSurge()) { 608 | let openUrl = rawopts.url || rawopts.openUrl || rawopts["open-url"]; 609 | return { url: openUrl }; 610 | } 611 | } else { 612 | return undefined; 613 | } 614 | }; 615 | if (!this.isMute) { 616 | if (this.isSurge() || this.isLoon()) { 617 | $notification.post(title, subt, desc, toEnvOpts(opts)); 618 | } else if (this.isQuanX()) { 619 | $notify(title, subt, desc, toEnvOpts(opts)); 620 | } 621 | } 622 | if (!this.isMuteLog) { 623 | let logs = ["", "==============📣系统通知📣=============="]; 624 | logs.push(title); 625 | subt ? logs.push(subt) : ""; 626 | desc ? logs.push(desc) : ""; 627 | console.log(logs.join("\n")); 628 | this.logs = this.logs.concat(logs); 629 | } 630 | } 631 | 632 | log(...logs) { 633 | if (logs.length > 0) { 634 | this.logs = [...this.logs, ...logs]; 635 | } 636 | console.log(logs.join(this.logSeparator)); 637 | } 638 | 639 | logErr(err, msg) { 640 | const isPrintSack = !this.isSurge() && !this.isQuanX() && !this.isLoon(); 641 | if (!isPrintSack) { 642 | this.log("", `❗️${this.name}, 错误!`, err); 643 | } else { 644 | this.log("", `❗️${this.name}, 错误!`, err.stack); 645 | } 646 | } 647 | 648 | wait(time) { 649 | return new Promise((resolve) => setTimeout(resolve, time)); 650 | } 651 | 652 | done(val = {}) { 653 | const endTime = new Date().getTime(); 654 | const costTime = (endTime - this.startTime) / 1000; 655 | this.log("", `🔔${this.name}, 结束! 🕛 ${costTime} 秒`); 656 | this.log(); 657 | if (this.isSurge() || this.isQuanX() || this.isLoon()) { 658 | $done(val); 659 | } 660 | } 661 | })(name, opts); 662 | } 663 | -------------------------------------------------------------------------------- /Script/xinian.boxjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "xinian.app.sub", 3 | "name": "xinian 自用订阅", 4 | "author": "@xinian", 5 | "icon": "https://raw.githubusercontent.com/Former-Years/icon/master/xinian.png", 6 | "repo": "https://github.com/Former-Years/Surge/tree/main/Script", 7 | "apps": [ 8 | { 9 | "id": "huke", 10 | "name": "虎课网", 11 | "keys": [ 12 | "huke_token", 13 | "huke_header" 14 | ], 15 | "author": "@xinian", 16 | "repo": "https://github.com/Former-Years/Surge/blob/main/Script/huke.js", 17 | "script": "https://raw.githubusercontent.com/Former-Years/Surge/main/Script/huke.js", 18 | "icons": [ 19 | "https://raw.githubusercontent.com/Former-Years/icon/master/huke_mini.png", 20 | "https://raw.githubusercontent.com/Former-Years/icon/master/huke.png" 21 | ] 22 | }, 23 | { 24 | "id": "AtstudyDK", 25 | "name": "学掌门打卡", 26 | "keys": [ 27 | "daka_hd" 28 | ], 29 | "settings": [ 30 | { 31 | "id": "daka_hd", 32 | "name": "学掌门headers", 33 | "val": "", 34 | "type": "text", 35 | "placeholder": "", 36 | "autoGrow": true, 37 | "rows": 2, 38 | "desc": "" 39 | } 40 | ], 41 | "author": "@xinian", 42 | "repo": "https://raw.githubusercontent.com/Former-Years/Surge/main/Script/daka.js", 43 | "script": "https://raw.githubusercontent.com/Former-Years/Surge/main/Script/daka.js", 44 | "icons": [ 45 | "https://raw.githubusercontent.com/Former-Years/icon/master/atstudy.png", 46 | "https://raw.githubusercontent.com/Former-Years/icon/master/atstudy.png" 47 | ] 48 | }, 49 | { 50 | "id": "qcej", 51 | "name": "去重二剪", 52 | "keys": [ 53 | "qcej_token" 54 | ], 55 | "settings": [ 56 | { 57 | "id": "qcej_token", 58 | "name": "去重二剪Token", 59 | "val": "", 60 | "type": "text", 61 | "placeholder": "", 62 | "autoGrow": true, 63 | "rows": 2, 64 | "desc": "" 65 | } 66 | ], 67 | "author": "@xinian", 68 | "repo": "https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js", 69 | "script": "https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/qcej.js", 70 | "icons": [ 71 | "https://raw.githubusercontent.com/Former-Years/icon/master/qcej.png", 72 | "https://raw.githubusercontent.com/Former-Years/icon/master/qcej.png" 73 | ] 74 | }, 75 | { 76 | "id": "erke", 77 | "name": "鸿星尔克", 78 | "keys": [ 79 | "erke_data" 80 | ], 81 | "settings": [ 82 | { 83 | "id": "erke_data", 84 | "name": "鸿星尔克URL", 85 | "val": "", 86 | "type": "text", 87 | "placeholder": "", 88 | "autoGrow": true, 89 | "rows": 2, 90 | "desc": "" 91 | } 92 | ], 93 | "author": "@xinian", 94 | "repo": "https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js", 95 | "script": "https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/erke.js", 96 | "icons": [ 97 | "https://raw.githubusercontent.com/Former-Years/icon/master/erke.png", 98 | "https://raw.githubusercontent.com/Former-Years/icon/master/erke.png" 99 | ] 100 | }, 101 | { 102 | "id": "tjdd", 103 | "name": "唐机豆豆", 104 | "keys": [ 105 | "tjdd_data" 106 | ], 107 | "settings": [ 108 | { 109 | "id": "tjdd_data", 110 | "name": "唐机豆豆access_token", 111 | "val": "", 112 | "type": "text", 113 | "placeholder": "", 114 | "autoGrow": true, 115 | "rows": 2, 116 | "desc": "" 117 | } 118 | ], 119 | "author": "@xinian", 120 | "repo": "https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js", 121 | "script": "https://raw.githubusercontent.com/Former-Years/Surge/refs/heads/main/Script/tjdd.js", 122 | "icons": [ 123 | "https://raw.githubusercontent.com/Former-Years/icon/master/tjdd.png", 124 | "https://raw.githubusercontent.com/Former-Years/icon/master/tjdd.png" 125 | ] 126 | }, 127 | { 128 | "id": "ampDache", 129 | "name": "高德打车", 130 | "keys": [ 131 | "GD_Val" 132 | ], 133 | "settings": [ 134 | { 135 | "id": "GD_Val", 136 | "name": "GD_Val", 137 | "val": "", 138 | "type": "text", 139 | "placeholder": "", 140 | "autoGrow": true, 141 | "rows": 2, 142 | "desc": "" 143 | } 144 | ], 145 | "author": "@wf021325", 146 | "repo": "https://raw.githubusercontent.com/wf021325/qx/master/task/ampDache.js", 147 | "script": "https://raw.githubusercontent.com/wf021325/qx/master/task/ampDache.js", 148 | "icons": [ 149 | "https://raw.githubusercontent.com/Former-Years/icon/master/ampDache.png", 150 | "https://raw.githubusercontent.com/Former-Years/icon/master/ampDache.png" 151 | ] 152 | }, 153 | { 154 | "id": "10086", 155 | "name": "中国移动", 156 | "keys": [ 157 | "china_mobile_phonenumber", 158 | "china_mobile_cookie", 159 | "china_mobile_params" 160 | ], 161 | "settings": [ 162 | { 163 | "id": "china_mobile_phonenumber", 164 | "name": "phonenumber", 165 | "val": "", 166 | "type": "text", 167 | "placeholder": "", 168 | "autoGrow": true, 169 | "rows": 2, 170 | "desc": "手机号码" 171 | }, 172 | { 173 | "id": "china_mobile_cookie", 174 | "name": "cookie", 175 | "val": "", 176 | "type": "text", 177 | "placeholder": "", 178 | "autoGrow": true, 179 | "rows": 2, 180 | "desc": "中国移动cookie" 181 | }, 182 | { 183 | "id": "china_mobile_params", 184 | "name": "params", 185 | "val": "", 186 | "type": "text", 187 | "placeholder": "", 188 | "autoGrow": true, 189 | "rows": 2, 190 | "desc": "中国移动参数" 191 | } 192 | ], 193 | "author": "@null", 194 | "repo": "https://github.120399.xyz/Script/10086.js", 195 | "script": "https://github.120399.xyz/Script/10086.js", 196 | "icons": [ 197 | "https://raw.githubusercontent.com/Former-Years/icon/refs/heads/master/10086.png", 198 | "https://raw.githubusercontent.com/Former-Years/icon/refs/heads/master/10086.png" 199 | ] 200 | } 201 | ] 202 | } 203 | --------------------------------------------------------------------------------