├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── close_inactive_issues.yml │ ├── close_invaild_issues.yml │ ├── close_question_issues.yml │ └── issue_question.yml ├── .gitignore ├── CHANGELOGS.MD ├── Direct.list ├── GetCookie.sgmodule ├── LICENSE ├── MitmAllHostnames.sgmodule ├── Proxy.list ├── QQPet └── lkQQPet.js ├── QQVip └── qqVipCheckIn.js ├── README.MD ├── README_en.MD ├── Reject.list ├── Scriptable ├── 10000.js ├── README.MD ├── ScriptableToolKit.js ├── ScriptableToolKit.min.js ├── example │ └── ScriptableToolKitDemo.js ├── ugstoolkit └── widgetCutBg.js ├── Scripts.sgmodule ├── Snap.list ├── Ssid.sgmodule ├── Tasks-removed.sgmodule ├── Tasks.sgmodule ├── ali └── aliYunPanCheckIn.js ├── bilibili ├── bangumiMonitor.js └── privilegeReceive.js ├── bing └── bingPoint.js ├── boxjs-empty.bmp ├── boxjs.sysapps.json ├── doc ├── icon │ ├── ChinaTelecom.png │ ├── ChinaTelecom_logo.png │ ├── ChinaTelecom_logo.psd │ ├── ChinaTelecom_logo_blue.png │ ├── ChinaTelecom_logo_orange.png │ ├── aliYunPan.png │ ├── aliYunPana.png │ ├── bilibiliBigVip.svg │ ├── bingPoint.png │ ├── eu.org.png │ ├── hifinisignin-dark.png │ ├── hifinisignin.png │ ├── jump.png │ ├── miyoushe.png │ ├── pupu.png │ ├── pupua.png │ └── zzz.png └── pic │ ├── 10086.jpeg │ ├── toolkitdemo-show-phone.gif │ ├── toolkitdemo-show.gif │ └── ugtoolkit.jpg ├── douyu ├── streamQuality.js └── yubaSign.js ├── empty.app.boxjs.json ├── epic └── freeGames.js ├── github └── githubMonitor.js ├── hifini └── hifiniSign.js ├── jd ├── jd_cookie_search.js ├── ql_api.js └── ql_sync_box.js ├── jump └── jumpPrice.js ├── lowking.boxjs.json ├── lowking.boxjs.removed.json ├── mihoyo ├── miyousheCustom.js └── zzz.js ├── others.boxjs.json ├── others └── eu.org.js ├── pupu └── pupuCheckIn.js ├── self ├── MiyousheCustom.sgmodule ├── ZzzCapture.sgmodule ├── add2Favorite.scpt ├── fadeVolume.scpt ├── jable.js └── removeTrackFromAllPlaylist.scpt ├── sony └── sonyClub.js ├── util ├── README.MD ├── Ssid.js ├── ToolKit.js ├── ToolKit.min.js ├── example │ ├── README.MD │ └── ToolKitDemo.js ├── qqUtil.js ├── qqUtil.min.js ├── ugtoolkit ├── util.js └── util.min.js └── weibo ├── weiboST.js └── weiboSTCookie.js /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: "问题反馈" 2 | description: 问题反馈 3 | title: "[BUG]" 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | 感谢您花时间填写此错误报告,请**务必确认您的issue不是重复的且不是因为版本问题** 10 | 11 | - type: checkboxes 12 | attributes: 13 | label: 您必须确认以下所有内容 14 | description: | 15 | 您必须勾选以下所有内容,否则您的issue可能会被直接关闭。 16 | options: 17 | - label: | 18 | 我已经阅读了脚本开头的注释。 19 | - label: | 20 | 我确定没有重复的issue。 21 | - label: | 22 | 我确定是脚本的问题,而不是其他原因(例如,```网络```,```操作```等)。 23 | - label: | 24 | 我确定这个问题在最新版本中没有被修复。 25 | 26 | - type: input 27 | id: version 28 | attributes: 29 | label: 版本 30 | description: | 31 | 您使用的是哪个版本的脚本?请填写具体的版本号。 32 | placeholder: v1.xx.xx 33 | validations: 34 | required: true 35 | - type: textarea 36 | id: bug-description 37 | attributes: 38 | label: 问题描述 39 | validations: 40 | required: true 41 | - type: textarea 42 | id: logs 43 | attributes: 44 | label: 日志 45 | description: | 46 | 请复制粘贴错误日志。 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: "需求请求" 2 | description: 需求请求 3 | title: "[FEAT]" 4 | labels: [enhancement] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: 您必须确认以下所有内容 9 | description: 根据实际情况选择 10 | options: 11 | - label: 我确定没有重复的issue。 12 | - label: 我确定需求未实现。 13 | - label: 我确定需求合理。 14 | - type: textarea 15 | id: feature-description 16 | attributes: 17 | label: 需求描述 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: suggested-solution 22 | attributes: 23 | label: 实现思路 24 | description: | 25 | 实现此需求的解决思路。 26 | - type: textarea 27 | id: remark 28 | attributes: 29 | label: 备注 30 | description: | 31 | 相关的任何其他上下文或截图,或者你觉得有帮助的信息 32 | -------------------------------------------------------------------------------- /.github/workflows/close_inactive_issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v5 14 | with: 15 | days-before-issue-stale: ${{ secrets.DBIS }} 16 | days-before-issue-close: ${{ secrets.DBIC }} 17 | stale-issue-label: "stale" 18 | stale-issue-message: | 19 | ci: This issue is stale because it has been open for ${{ secrets.DBIS }} days with no activity. 20 | close-issue-message: | 21 | ci: This issue was closed because it has been inactive for ${{ secrets.DBIC }} days since being marked as stale. 22 | days-before-pr-stale: -1 23 | days-before-pr-close: -1 24 | repo-token: ${{ secrets.TOKEN }} 25 | -------------------------------------------------------------------------------- /.github/workflows/close_invaild_issues.yml: -------------------------------------------------------------------------------- 1 | name: Close invaild issues 2 | 3 | on: 4 | issues: 5 | types: [labeled] 6 | 7 | jobs: 8 | create-comment: 9 | runs-on: ubuntu-latest 10 | if: github.event.label.name == 'invalid' 11 | steps: 12 | - name: Create comment 13 | uses: actions-cool/issues-helper@v3 14 | with: 15 | actions: 'create-comment' 16 | token: ${{ secrets.TOKEN }} 17 | issue-number: ${{ github.event.issue.number }} 18 | body: | 19 | Hello @${{ github.event.issue.user.login }}, your issue is invalid and will be closed. 20 | 你好 @${{ github.event.issue.user.login }},你的issue无效,将被关闭。 21 | - name: Close issue 22 | uses: actions-cool/issues-helper@v3 23 | with: 24 | actions: 'close-issue' 25 | token: ${{ secrets.TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/close_question_issues.yml: -------------------------------------------------------------------------------- 1 | name: Close question issues 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 */1 * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | close-need-info: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: close-issues 13 | uses: actions-cool/issues-helper@v3 14 | with: 15 | actions: 'close-issues' 16 | token: ${{ secrets.TOKEN }} 17 | labels: 'question' 18 | inactive-day: 3 19 | close-reason: 'not_planned' 20 | body: | 21 | Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 3 days. 22 | 你好 @${{ github.event.issue.user.login }},此issue因超过3天未回复被关闭。 -------------------------------------------------------------------------------- /.github/workflows/issue_question.yml: -------------------------------------------------------------------------------- 1 | name: Issue question 2 | 3 | on: 4 | issues: 5 | types: [labeled] 6 | 7 | jobs: 8 | create-comment: 9 | runs-on: ubuntu-latest 10 | if: github.event.label.name == 'question' 11 | steps: 12 | - name: Create comment 13 | uses: actions-cool/issues-helper@v3.5.1 14 | with: 15 | actions: 'create-comment' 16 | token: ${{ secrets.TOKEN }} 17 | issue-number: ${{ github.event.issue.number }} 18 | body: | 19 | Hello @${{ github.event.issue.user.login }}, please input issue by template and add detail. It will be closed if no activities in 3 days. 20 | 你好 @${{ github.event.issue.user.login }},请按照issue模板填写, 3天内未回复issue自动关闭。 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .settings/ 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | .sts4-cache 14 | 15 | ### IntelliJ IDEA ### 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | *.pid 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /build/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | /logs/ 30 | /pay-service/.gitignore 31 | 32 | ### node ### 33 | package-lock.json 34 | node_modules 35 | 36 | ### others 37 | github.js 38 | myGithub.js 39 | lk*.dat 40 | lk*.boxjs.json 41 | github.json 42 | .wakatime-project 43 | test/* 44 | *.bak 45 | jd/ 46 | -------------------------------------------------------------------------------- /CHANGELOGS.MD: -------------------------------------------------------------------------------- 1 | # ChangeLogs 2 | #### 2021-12-14 3 | > ##### 新增 4 | > - hifini签到 5 | 6 | #### 2021-02-18 7 | > ##### 新增 8 | > - Scriptable中国电信查询 9 | > ##### 更新 10 | > - ScriptableToolKit支持自定义操作入口 11 | 12 | #### 2020-12-14 13 | > ##### 新增 14 | > - 朴朴签到 15 | 16 | #### 2020-11-09 17 | > ##### 新增 18 | > - 添加scriptable相关脚本和ToolKit 19 | 20 | #### 2020-10-08 21 | > ##### 新增 22 | > - 哔哩哔哩追番监控添加追剧监控 23 | 24 | #### 2020-10-08 25 | > ##### 新增 26 | > - 上传ToolKit压缩工具脚本,便于一键压缩替换.js中的代码 27 | 28 | #### 2020-09-12 29 | > ##### 更新 30 | > - ToolKit支持quanx和surge通知跳转url以及多媒体文件(surge暂无) 31 | 32 | #### 2020-09-01 33 | > ##### 新增 34 | > - 新增QQ萌宠签到获取成长值 35 | 36 | #### 2020-08-28 37 | > ##### 新增 38 | > - 新增斗鱼鱼吧签到 39 | > ##### 更新 40 | > - 更新ToolKit,内容如下: 41 | > > 1. 调整传入options.httpApi的格式校验 42 | > > 2. 发送到ios surge允许传入超时参数:node ToolKitDemo.js p 5,最后一个参数"5"单位秒 43 | > > 3. 加入prependNotifyInfo()方法,便于脚本最后统计执行情况的时候把汇总信息放在第一行 44 | > > 4. 完善boxJsJsonBuilder(),构建更全的配置 45 | 46 | #### 2020-07-24 47 | > ##### 更新 48 | > - ToolKit支持surge新特性http api 49 | > > 1. 写法参考[ToolKitDemo](https://github.com/lowking/Scripts/blob/master/util/example/useToolKitDemo.js),之后就可以在脚本所在的目录下执行```node ToolKitDemo.js p```,记得命令尾巴加空格p,就可以把当前这个脚本发送到手机端测试了 50 | > > 2. 执行```node ToolKitDemo.js```可在当前目录下生成BoxJs配置文件,详细实现可以查看ToolKit中的boxJsJsonBuilder方法 51 | 52 | #### 2020-07-20 53 | > ##### 更新 54 | > - ToolKit新增randomString和autoComplete方法 55 | > - QQ会员成长值签到改造,使用ToolKit 56 | 57 | #### 2020-07-19 58 | > ##### 新增 59 | > - 索尼俱乐部签到,支持自动登录签到 60 | > - 新增ToolKit,根据自己的习惯整合各个开发者而形成的工具包(@NobyDa, @chavyleung) 61 | 62 | #### 2020-07-10 63 | > ##### 更新 64 | > - 订阅转换器引用[KOP-XIAO的资源转换器](https://raw.githubusercontent.com/KOP-XIAO/QuantumultX/master/Scripts/resource-parser.js)(我就简单的支持了surge,就测试过vmess格式的订阅) 65 | 66 | #### 2020-07-08 67 | > ##### 新增 68 | > - 添加Wi-Fi助手(从gist迁移过来,并汉化通知) 69 | #### 2020-07-02 70 | > ##### 新增 71 | > - [Wi-Fi助手模块(Surge专用)](https://gist.githubusercontent.com/lowking/3aa8748416e938528967885bc403b2f1/raw/ssid.sgmodule):在boxjs中配置需直连的ssid 72 | > - 自用GetCookie、Tasks模块 73 | > - 订阅转换器(目前功能简陋,自用) 74 | 75 | > ##### 更新 76 | > - QQ会员成长值签到支持好友列表点赞(每天20点) 77 | > - 微博超话签到支持获取超话关注列表,批量签到 78 | 79 | #### 2020-06-16 80 | > ##### 新增 81 | > - QQ会员成长值签到 82 | > - 微博超话签到(原作者NavePnow,因为通知太多进行修改,同时重构了代码) 83 | > - 微博超话签到支持获取关注列表(要是关注很多不知道行不行,没帐号测试) 84 | > - 微博超话签到支持手动清除cookie(isClearCookie改成true之后运行一次脚本) 85 | -------------------------------------------------------------------------------- /Direct.list: -------------------------------------------------------------------------------- 1 | DOMAIN-SUFFIX,hkmytv.com 2 | DOMAIN-SUFFIX,bimiacg2.net 3 | DOMAIN-SUFFIX,cmy.network 4 | DOMAIN-SUFFIX,rewards.bing.com 5 | DOMAIN-SUFFIX,routerlogin.net 6 | # > plex 7 | DOMAIN,v4.plex.tv 8 | DOMAIN-SUFFIX,amazonaws.com 9 | DOMAIN-SUFFIX,plex.direct 10 | -------------------------------------------------------------------------------- /GetCookie.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=GetCookie by lowking. Self-use 2 | #!desc=该模块自用,获取cookie合集 3 | #!category=Self 4 | 5 | [Script] 6 | 115-cookie = type=http-request,pattern=^https?:\/\/proapi\.115\.com\/ios\/user\/takespc\?,script-path=https://raw.githubusercontent.com/zZPiglet/Task/master/115/115.js 7 | 8 | 贴吧-cookie = script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/tieba/tieba.cookie.js,type=http-request,pattern=^https?:\/\/tieba\.baidu\.com\/?.? 9 | 10 | 万达电影-cookie = type=http-request,pattern=^https:\/\/user-api-prd-mx\.wandafilm\.com\/user\/islogin\.api,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/wanda/wanda.cookie.js,debug=true 11 | 12 | #腾讯视频-cookie = script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/videoqq/videoqq.cookie.js,type=http-request,pattern=^https:\/\/access.video.qq.com\/user\/auth_refresh 13 | 14 | 智行火车票-cookie = script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/zxhc/zxhc.cookie.js,requires-body=true,type=http-request,pattern=^https:\/\/m\.ctrip\.com/restapi/soa2/14593/json/attendanceDay? 15 | 16 | 美团-cookie = script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/meituan/meituan.cookie.js,requires-body=true,type=http-request,pattern=^https:\/\/i.meituan.com\/evolve\/signin\/signpost\/ 17 | 18 | 芒果TV-cookie = script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/mgtv/mgtv.cookie.js,type=http-request,pattern=^https:\/\/credits.bz.mgtv.com\/user\/creditsTake 19 | 20 | 斗鱼鱼吧-cookie = type=http-request,pattern=^https://yuba.douyu.com/wbapi/web/group/myFollow,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/douyu/yubaSign.js 21 | 22 | 什么值得买-cookie = type=http-request,pattern=^https:\/\/www\.smzdm\.com\/?.?,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/smzdm/smzdm.cookie.js 23 | 24 | 爱奇艺-cookie = type=http-request,pattern=^https:\/\/passport\.iqiyi\.com\/apis\/user\/,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/iQIYI-DailyBonus/iQIYI.js 25 | 26 | 哔哩哔哩番剧监控-cookie = type=http-request,pattern=https?:\/\/app.bilibili.com\/x\/v2\/space\/bangumi,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bilibili/bangumiMonitor.js 27 | 28 | 吾爱破解签到-cookie = type=http-request,pattern=https:\/\/www\.52pojie\.cn\/home\.php\?,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/52pojie-DailyBonus/52pojie.js 29 | 30 | QQ萌宠-cookie = requires-body=1,type=http-response,pattern=https:\/\/qqpet.jwetech.com\/api\/authorizations,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/QQPet/lkQQPet.js 31 | 32 | 10086-cookie = type=http-request,pattern=^https:\/\/clientaccess.10086.cn\/biz-orange\/LN\/uamrandcodelogin\/autoLogin,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/10086/10086.fee.cookie.js,requires-body=true,debug=true 33 | 10086-cookie = type=http-request,pattern=^https:\/\/clientaccess.10086.cn\/biz-orange\/BN\/realFeeQuery\/getRealFee,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/10086/10086.fee.cookie.js,requires-body=true,debug=true 34 | 35 | 朴朴签到cookie = requires-body=1,type=http-response,pattern=https:\/\/cauth.pupuapi.com\/clientauth\/user\/verify_login,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/pupu/pupuCheckIn.js 36 | 37 | hifini签到cookie = type=http-request,pattern=https:\/\/www.hifini.com\/my.htm,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/hifini/hifiniSign.js 38 | 39 | 阿里云盘签到cookie = requires-body=1,type=http-response,pattern=https:\/\/auth.(aliyundrive|alipan).com\/v2\/account\/token,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/ali/aliYunPanCheckIn.js 40 | 41 | 顺丰速运cookie = requires-body=1,type=http-request, pattern=^https:\/\/ccsp-egmas.sf-express.com\/cx-app-member\/member\/app\/user\/universalSign,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/sfexpress/sfexpress.cookie.js 42 | 43 | bing-cookie = requires-body=0,type=http-request, pattern=^https:\/\/rewards\.bing\.com,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bing/bingPoint.js 44 | 45 | Jump游戏价格监控cookie = requires-body=0,type=http-request,pattern=https:\/\/switch\.jumpvg\.com\/jump\/app\/conf,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/jump/jumpPrice.js 46 | 47 | [MITM] 48 | hostname = %APPEND% switch.jumpvg.com, rewards.bing.com, ccsp-egmas.sf-express.com, auth.alipan.com, *.hifini.com, cauth.pupuapi.com, qqpet.jwetech.com, clientaccess.10086.cn, www.52pojie.cn, *.bilibili.com, passport.iqiyi.com, *.smzdm.com, yuba.douyu.com, tieba.baidu.com, as.xiaojukeji.com, user-api-prd-mx.wandafilm.com, api.m.jd.com, *.video.qq.com, *.bilibili.com, m.ctrip.com, i.meituan.com, credits.bz.mgtv.com 49 | -------------------------------------------------------------------------------- /MitmAllHostnames.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=MitMAllHostnames 2 | #!desc=Perform MitM on all hostnames with port 443, except those to Apple and other common sites which can't be inspected. You still need configure CA certificate and enable the main switch of MitM. 3 | 4 | [MITM] 5 | hostname = -*.apple.com, -*.icloud.com, -*.mzstatic.com, -*.crashlytics.com, -*.facebook.com, -*.instagram.com, * -------------------------------------------------------------------------------- /Proxy.list: -------------------------------------------------------------------------------- 1 | # > 18mag 2 | DOMAIN,18mag.net 3 | # > 中国移动 4 | DOMAIN,clientaccess.10086.cn 5 | # > da 6 | DOMAIN,deviantart.com 7 | # > plex 8 | DOMAIN,downloads.plex.tv 9 | DOMAIN,metadata-static.plex.tv 10 | DOMAIN,metadata.provider.plex.tv 11 | DOMAIN,meta.plex.tv 12 | # > cdn.jsdelivr.net 13 | DOMAIN,cdn.jsdelivr.net 14 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Scripts 2 | ![](https://img.shields.io/badge/license-GPL-blueviolet.svg) 3 | [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) 4 | [![LICENSE](https://img.shields.io/badge/感谢-Orz3的图标-red.svg)](https://github.com/Orz-3) 5 | [![LICENSE](https://img.shields.io/badge/感谢-58xinian的图标-red.svg)](https://github.com/58xinian) 6 | [![Close inactive issues](https://github.com/lowking/Scripts/actions/workflows/close_inactive_issues.yml/badge.svg)](https://github.com/lowking/Scripts/actions/workflows/close_inactive_issues.yml) 7 | 8 | ## [English](https://github.com/lowking/Scripts/blob/master/README_en.MD) 9 | 10 | ## 维护列表 11 | 🟢在用  🟡未在用  🔴有问题 12 | ### Scripts(Surge, Quantumult X, Loon, nodeJs) 13 |    14 | 🟢 15 |    16 | 🟡 17 |    18 | 🟡 19 |    20 | 🟡 21 |    22 | 🟡 23 |    24 | 🟡 25 |    26 | 🟡 27 |    28 | 🟢 29 |    30 | 🟡 31 |    32 | 🔴 33 |    34 | 🟢 35 |    36 | 🔴 37 |    38 | 🟢 39 |    40 | 🟢 41 |    42 | 🟢 43 | 44 | ### Scriptable 45 |   🟢 46 | 47 | ### Applescript 48 | * 将当前播放的歌曲添加到指定播放列表。[点击前往](https://github.com/lowking/Scripts/blob/master/self/add2Favorite.scpt) 49 | * 将当前播放的歌曲添加到指定播放列表并从其他播放列表移除。[点击前往](https://github.com/lowking/Scripts/blob/master/self/removeTrackFromAllPlaylist.scpt) 50 | * 音乐app音量淡入淡出。2次快速触发实现暂停,单次触发则在最大/最小音量直接切换[点击前往](https://github.com/lowking/Scripts/blob/master/self/fadeVolume.scpt) 51 | 52 | ## 许可 53 | Copyright © 2020-present lowking. Licensed under [GPL](https://github.com/lowking/Scripts/blob/master/LICENSE) License. 54 | -------------------------------------------------------------------------------- /README_en.MD: -------------------------------------------------------------------------------- 1 | # Scripts 2 | ![](https://img.shields.io/badge/license-GPL-blueviolet.svg) 3 | [![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) 4 | [![LICENSE](https://img.shields.io/badge/Thanks-Orz3's%20icons-red.svg)](https://github.com/Orz-3) 5 | [![LICENSE](https://img.shields.io/badge/Thanks-58xinian's%20icons-red.svg)](https://github.com/58xinian) 6 | [![Close inactive issues](https://github.com/lowking/Scripts/actions/workflows/close_inactive_issues.yml/badge.svg)](https://github.com/lowking/Scripts/actions/workflows/close_inactive_issues.yml) 7 | 8 | ## [中文](https://github.com/lowking/Scripts/blob/master/README.MD) 9 | 10 | ## Maintenance List 11 | 🟢In use  🟡Not in use  🔴Problem 12 | ### Scripts(Surge, Quantumult X, Loon, nodeJs) 13 |    14 | 🟢 15 |    16 | 🟡 17 |    18 | 🟡 19 |    20 | 🟡 21 |    22 | 🟡 23 |    24 | 🟡 25 |    26 | 🟡 27 |    28 | 🟢 29 |    30 | 🟡 31 |    32 | 🔴 33 |    34 | 🟢 35 |    36 | 🔴 37 |    38 | 🟢 39 |    40 | 🟢 41 |    42 | 🟢 43 | 44 | ### Scriptable 45 |   🟢 46 | 47 | ### Applescript 48 | * Adds the currently playing song to the specified playlist. [here](https://github.com/lowking/Scripts/blob/master/self/add2Favorite.scpt) 49 | * Adds the currently playing song to the specified playlist and removes it from other playlists. [here](https://github.com/lowking/Scripts/blob/master/self/removeTrackFromAllPlaylist.scpt) 50 | * Music app volume fades in and out. Two quick triggers pause, and a single trigger switches directly at the maximum/minimum volume. [here](https://github.com/lowking/Scripts/blob/master/self/fadeVolume.scpt) 51 | 52 | ## LICENSE 53 | Copyright © 2020-present lowking. Licensed under [GPL](https://github.com/lowking/Scripts/blob/master/LICENSE) License. 54 | -------------------------------------------------------------------------------- /Reject.list: -------------------------------------------------------------------------------- 1 | DOMAIN-SUFFIX,ntb.lanjie100.com -------------------------------------------------------------------------------- /Scriptable/README.MD: -------------------------------------------------------------------------------- 1 | # Scriptsable脚本说明 2 | > #### 10086 3 | > - 根据[GideonSenku](https://github.com/GideonSenku)和[mzeryck](https://github.com/mzeryck)两位作者脚本整合而成 4 | > - app内点击运行抠图,实现伪透明背景(效果看下图) 5 | > - 脚本内可以修改lang变量修改语言(已经调整成根据手机语言环境设置) 6 | 7 | 10086效果图
8 | 9 | > #### widgetCutBg 10 | > - 该脚本已经集成到ScriptableToolKit中,具体使用方法查看example中的demo 11 | > - 该脚本收入自[mzeryck](https://github.com/mzeryck)这位作者,本人汉化而成(目前只有中文和英文,欢迎其他国家开发者翻译pr) 12 | > - 以上脚本均通过整个该脚本实现伪背景透明效果,有兴趣的自行研究 13 | -------------------------------------------------------------------------------- /Scriptable/ScriptableToolKit.min.js: -------------------------------------------------------------------------------- 1 | function ScriptableToolKit(scriptName,scriptId,options){return new class{constructor(scriptName,scriptId,options){this.isLimited=false;this.checkLimit();this.local=FileManager.local();this.icloud=FileManager.iCloud();this.curDateCache=this.local.joinPath(this.local.documentsDirectory(),"curDateCache");this.options=options;this.tgEscapeCharMapping={"&":"&"};this.userAgent=`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`;this.prefix=`lk`;this.name=scriptName;this.id=scriptId;this.data=null;this.dataFile=`${this.prefix}${this.id}.json`;this.bgImgPath=`${this.prefix}${this.id}Bg.jpg`;this.bgImgPath=this.local.joinPath(this.local.documentsDirectory(),this.bgImgPath);this.lang=Device.language();this.msg={zh:{s0:"在开始之前,先进入主屏幕,进入图标排列模式。滑到最右边的空白页,并进行截图。",s1:"看起来你选择的图片不是iPhone的截图,或者你的iPhone不支持。请换一张图片再试一次。",s2:"你想创建什么尺寸的widget?",s3:"你想把widget放在哪里?",s4:" (请注意,您的设备只支持两行小部件,所以中间和底部的选项是一样的)。",s5:"widget的背景图已裁切完成,想在Scriptable内部使用还是导出到相册?",s6:"已经截图,继续",s7:"退出去截图",s8:"小",s9:"中",s10:"大",s11:"顶部左边",s12:"顶部右边",s13:"中间左边",s14:"中间右边",s15:"底部左边",s16:"底部右边",s17:"顶部",s18:"中间",s19:"底部",s20:"在Scriptable内部使用",s21:"导出到相册",s22:"填写遮罩层颜色。(格式:#000000)",s23:"颜色(格式:#000000)",s24:"填写遮罩层不透明度(0-1之间)",s25:"0-1之间",s26:"确定",s27:"取消",s28:"预览widget",s29:"设置widget背景",s30:"入口",s31:"你用的是哪个型号?",s32:"退出",s33:"清除缓存",s34:"开始清除缓存",s35:"清除缓存完成"},en:{s0:"Before you start, go to your home screen and enter wiggle mode. Scroll to the empty page on the far right and take a screenshot.",s1:"It looks like you selected an image that isn't an iPhone screenshot, or your iPhone is not supported. Try again with a different image.",s2:"What size of widget are you creating?",s3:"What position will it be in?",s4:" (Note that your device only supports two rows of widgets, so the middle and bottom options are the same.)",s5:"Your widget background is ready. Would you like to use it in a Scriptable widget or export the image?",s6:"Continue",s7:"Exit to Take Screenshot",s8:"Small",s9:"Medium",s10:"Large",s11:"Top left",s12:"Top right",s13:"Middle left",s14:"Middle right",s15:"Bottom left",s16:"Bottom right",s17:"Top",s18:"Middle",s19:"Bottom",s20:"Use in Scriptable",s21:"Export to Photos",s22:"Fill in the mask layer color. (Format: #000000)",s23:"Color.(Format: #000000)",s24:"Fill in the mask layer opacity (between 0-1)",s25:"between 0-1",s26:"Confirm",s27:"Cancel",s28:"Preview widget",s29:"Setting widget background",s30:"ENTER",s31:"What type of iPhone do you have?",s32:"Exit",s33:"Clean cache",s34:"Clean cache started",s35:"Clean cache finished"}};this.curLang=this.msg[this.lang]||this.msg.en;this.isSaveLog=this.getResultByKey(`${this.prefix}IsSaveLog${this.id}`,false);this.isEnableLog=this.getResultByKey(`${this.prefix}IsEnableLog${this.id}`,true);this.logDir=this.icloud.documentsDirectory()+"/lklogs/"+this.id;this.logSeparator="\n██";this.now=new Date;this.execStatus=true;this.notifyInfo=[];this.operations=[]}async checkLimit(){const lastRunningTime=await this.getVal(`${this.prefix}LastRunningTime${this.id}`,"local",0);const runLimitNum=this.getResultByKey(`${this.prefix}RunLimitNum${this.id}`,3e5);this.log(`上次运行时间:${lastRunningTime},运行频率限制:${runLimitNum}`);if(lastRunningTime>=0){if(this.now.getTime()-lastRunningTime<=runLimitNum){this.appendNotifyInfo("限制运行");this.isLimited=true}else{await this.setVal(`${this.prefix}LastRunningTime${this.id}`,this.now.getTime(),"local")}}return this.isLimited}getResultByKey(key,defaultValue){if(!this.options){return defaultValue}const val=this.options[key];if(this.isEmpty(val)){return defaultValue}else{return val}}appendNotifyInfo(info,type){if(type==1){this.notifyInfo=info}else{this.notifyInfo.push(`${this.logSeparator}${this.formatDate(new Date,"yyyy-MM-dd HH:mm:ss.S")}█${info}`)}}saveLog(){if(this.isSaveLog){let message;if(Array.isArray(this.notifyInfo)){message=this.notifyInfo.join("")}else{message=this.notifyInfo}if(!this.icloud.isDirectory(this.logDir)){this.icloud.createDirectory(this.logDir,true)}this.icloud.writeString(`${this.logDir}/${this.formatDate(this.now,"yyyyMMddHHmmss")}.log`,message)}}prependNotifyInfo(info){this.notifyInfo.splice(0,0,info)}execFail(){this.execStatus=false}sleep(time){return new Promise(resolve=>setTimeout(resolve,time))}log(message){if(this.isEnableLog)console.log(`${this.logSeparator}${JSON.stringify(message)}`);this.appendNotifyInfo(message)}logErr(message){this.execStatus=false;if(this.isEnableLog){console.warn(`${this.logSeparator}${this.name}执行异常:`);console.warn(message);console.warn(`\n${message.message}`)}}getContainer(key){return key=="local"?this.local:this.icloud}async getVal(key,container,defaultValue){let containerInstance=this.getContainer(container);let data="";try{let realDataFile=containerInstance.joinPath(containerInstance.documentsDirectory(),this.dataFile);if(!containerInstance.fileExists(realDataFile)){await this.setVal(key,defaultValue,container);return defaultValue}data=await containerInstance.readString(realDataFile);data=JSON.parse(data)}catch(e){throw e}if(data.hasOwnProperty(key)){return data[key]}else{await this.setVal(key,defaultValue,container);return defaultValue}}async getDataFile(container){let containerInstance=this.getContainer(container);let data="";try{let realDataFile=containerInstance.joinPath(containerInstance.documentsDirectory(),this.dataFile);if(!containerInstance.fileExists(realDataFile)){return Promise.resolve("")}data=await containerInstance.readString(realDataFile)}catch(e){throw e}return Promise.resolve(data)}async saveImage(fileName,image,container){let containerInstance=this.getContainer(container);let imagePath=containerInstance.joinPath(containerInstance.documentsDirectory(),`${this.prefix}${this.id}/${fileName}`);let imageDir=imagePath.substring(0,imagePath.lastIndexOf("/")+1);if(!containerInstance.isDirectory(imageDir)){containerInstance.createDirectory(imageDir,true)}containerInstance.writeImage(imagePath,image)}async getImage(fileName,container){let containerInstance=this.getContainer(container);let imagePath=containerInstance.joinPath(containerInstance.documentsDirectory(),`${this.prefix}${this.id}/${fileName}`);if(!containerInstance.fileExists(imagePath)){this.logErr(`file not exist: ${imagePath}`);return false}return await containerInstance.readImage(imagePath)}async setVal(key,val,container){let containerInstance=this.getContainer(container);let data;let realDataFile=containerInstance.joinPath(containerInstance.documentsDirectory(),this.dataFile);try{if(!containerInstance.fileExists(realDataFile)){data={}}else{data=await containerInstance.readString(realDataFile);data=JSON.parse(data)}}catch(e){data={}}data[key]=val;await containerInstance.writeString(realDataFile,JSON.stringify(data))}async get(options,callback=(()=>{})){let request=new Request("");request.url=options.url;request.method="GET";request.headers=options.headers;try{const result=await request.loadString();callback(request.response,result);return result}catch(e){this.logErr(e);callback(undefined,undefined)}}async post(options,callback=(()=>{})){let request=new Request("");request.url=options.url;request.body=options.body;request.method="POST";request.headers=options.headers;request.timeout=5e3;try{const result=await request.loadString();callback(request.response,result);return result}catch(e){this.logErr(e);callback(undefined,undefined)}}async loadScript({scriptName:scriptName,url:url}){this.log(`获取脚本【${scriptName}】`);const content=await this.get({url:url});this.icloud.writeString(`${this.icloud.documentsDirectory()}/${scriptName}.js`,content);this.log(`获取脚本【${scriptName}】完成🎉`)}require({scriptName:scriptName,url:url="",reload:reload=false}){if(this.icloud.fileExists(this.icloud.joinPath(this.icloud.documentsDirectory(),`${scriptName}.js`))&&!reload){this.log(`引用脚本【${scriptName}】`);return importModule(scriptName)}else{this.loadScript({scriptName:scriptName,url:url});this.log(`引用脚本【${scriptName}】`);return importModule(scriptName)}}async generateInputAlert(message,field,defaultValue){let result=[];let alert=new Alert;alert.message=message;alert.addTextField(field,defaultValue);alert.addCancelAction(this.curLang.s27);alert.addAction(this.curLang.s26);result[0]=await alert.presentAlert();result[1]=alert.textFieldValue(0);return result}async generateAlert(message,options){let alert=new Alert;alert.message=message;for(const option of options){alert.addAction(option)}return await alert.presentAlert()}isEmpty(obj){return typeof obj=="undefined"||obj==null||obj==""||obj=="null"}isWorkingDays(now,workingDaysFlag="curlybraces",holidayFlag="gamecontroller"){return new Promise(async(resolve,reject)=>{let sp="≈";const d=this.formatDate(now,"yyyy-MM-dd");let resultStr=0;try{let curDate=await this.getVal("curDateCache","local","fff");let curDateErrorTime=await this.getVal("curDateCacheErrorTime","local",this.now.getTime());let isPreError=!this.isEmpty(curDateErrorTime)&&Number(curDateErrorTime)+5*60*1e3{if(data.indexOf("<")==0){resultStr="❌"}else{resultStr=JSON.parse(data);if(resultStr.code==-1){resultStr="❌"}else{resultStr=resultStr.type.type}}})}}catch(e){resultStr="❌";this.logErr(e)}finally{await this.setVal("curDateCache",`${d}${sp}${resultStr}`,"local");if(resultStr=="❌"){resolve(resultStr);this.log("写入运行错误时间,5分钟后重新请求!");this.setVal("curDateCache","","local");this.setVal("curDateCacheErrorTime",`${this.now.getTime()}`,"local")}else{this.setVal("curDateCacheErrorTime","","local");this.setVal("curDateCache",`${d}${sp}${resultStr}`,"local");resolve(resultStr==0||resultStr==3?workingDaysFlag:holidayFlag)}}})}randomString(len){len=len||32;var $chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";var maxPos=$chars.length;var pwd="";for(let i=0;i{return item.name[this.lang]});let customEnterCallback=customEnter.map((item,index)=>{return item.callback});if(isReset){options=customEnterNames}else{this.operations.push({callback:main});this.operations.push({callback:function(){$.widgetCutBg()}});this.operations.push({callback:function(){$.cleanCache()}});options=options.concat(customEnterNames)}customEnterCallback.forEach(callback=>{this.operations.push({callback:callback})})}options.push(this.curLang.s32);this.operations.push({callback:function(){}});return await this.generateAlert(this.curLang.s30,options)}async handleOperations(index){await this.operations[index].callback()}cleanCache(){this.log(this.curLang.s34);let filePath=this.local.joinPath(this.local.documentsDirectory(),this.dataFile);if(this.local.fileExists(filePath)){this.local.remove(filePath)}filePath=this.bgImgPath;if(this.local.fileExists(filePath)){this.local.remove(filePath)}this.log(this.curLang.s35)}formatTimeDuring(total,lang="zh",n=0){total=Number(total);let zhUnitArr=["毫秒","秒","分钟","小时","天","月","年"];let enUnitArr=["ms","s","min","h","d","m","y"];let scaleArr=[1e3,60,60,24,30,12,100];let len=total;if(len>scaleArr[n]){len=total/scaleArr[n];return this.formatTimeDuring(len,lang,++n)}else{let unit=zhUnitArr[n];if(lang==="en"){unit=enUnitArr[n]}return len.toFixed(2)+""+unit}}fileLengthFormat(total,unit="",toByte=false){total=Number(total);var unitArr=["","KB","MB","GB","TB","PB","EB","ZB"];var n=0;try{n=unitArr.indexOf(unit)}catch(e){throw e}if(toByte){if(n==0){return total}return this.fileLengthFormat(total*1024,unitArr[--n],true)}var len=total;if(len>1e3){len=total/1024;return this.fileLengthFormat(len,unitArr[++n])}else{if(n==0){return len.toFixed(2)}return len.toFixed(2)+" "+unitArr[n]}}}(scriptName,scriptId,options)} -------------------------------------------------------------------------------- /Scriptable/example/ScriptableToolKitDemo.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: yellow; icon-glyph: magic; 4 | const scriptId = 'ScriptableToolKitDemo' 5 | const scriptName = '工具包使用示例' 6 | var options = {} 7 | options[`lkIsSaveLog${scriptId}`] = true 8 | options[`lkRunLimitNum${scriptId}`] = 300000 9 | const $ = new ScriptableToolKit(scriptName, scriptId, options) 10 | 11 | let widget = new ListWidget() 12 | widget.backgroundImage = $.getWidgetBg() 13 | 14 | if (config.runsInWidget) { 15 | if (await $.checkLimit()) { 16 | $.execFail() 17 | $.saveLog() 18 | return false; 19 | } 20 | main() 21 | } else { 22 | const customEnter = [ 23 | { 24 | name:{ 25 | zh:"操作1", 26 | en:"operation1" 27 | }, 28 | callback: callback1 29 | }, 30 | { 31 | name:{ 32 | zh:"操作2", 33 | en:"operation2" 34 | }, 35 | callback: callback2 36 | } 37 | ] 38 | let enter = await $.widgetEnter(customEnter) 39 | await $.handleOperations(enter) 40 | } 41 | 42 | function callback1(){ 43 | $.log("操作1") 44 | } 45 | 46 | function callback2(){ 47 | $.log("操作2") 48 | } 49 | 50 | async function main() { 51 | // Your code here 52 | $.log('send request to baidu') 53 | const url = { 54 | url: 'http://www.baidu.com' 55 | } 56 | $.post(url, (response, data) => { 57 | $.log(JSON.stringify(response)) 58 | $.log(data) 59 | }) 60 | 61 | // persistence your data 62 | // get all data content 63 | $.log('get data file content') 64 | $.log(await $.getDataFile()) 65 | 66 | // get value of key from icloud container('local' or 'icloud'). If there is no value, return 'defaultValue' you passed in 67 | $.log('get value of key from icloud') 68 | $.log(await $.getVal('key', 'icloud', 'defaultValue')) 69 | 70 | // set value for key to target container('local' or 'icloud') 71 | $.log('set value for key') 72 | $.setVal('key', 'value', 'icloud') 73 | $.setVal('key1', 'value1', 'icloud') 74 | $.log(await $.getVal('key', 'icloud', 'defaultValue')) 75 | $.log(await $.getDataFile()) 76 | 77 | $.log('save log') 78 | $.saveLog() 79 | widget.presentSmall() 80 | Script.setWidget(widget) 81 | Script.complete() 82 | } 83 | 84 | //ScriptableToolKit-start 85 | function ScriptableToolKit(scriptName,scriptId,options){return new class{constructor(scriptName,scriptId,options){this.isLimited=false;this.checkLimit();this.local=FileManager.local();this.icloud=FileManager.iCloud();this.curDateCache=this.local.joinPath(this.local.documentsDirectory(),"curDateCache");this.options=options;this.tgEscapeCharMapping={"&":"&"};this.userAgent=`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`;this.prefix=`lk`;this.name=scriptName;this.id=scriptId;this.data=null;this.dataFile=`${this.prefix}${this.id}.json`;this.bgImgPath=`${this.prefix}${this.id}Bg.jpg`;this.bgImgPath=this.local.joinPath(this.local.documentsDirectory(),this.bgImgPath);this.lang=Device.language();this.msg={zh:{s0:"在开始之前,先进入主屏幕,进入图标排列模式。滑到最右边的空白页,并进行截图。",s1:"看起来你选择的图片不是iPhone的截图,或者你的iPhone不支持。请换一张图片再试一次。",s2:"你想创建什么尺寸的widget?",s3:"你想把widget放在哪里?",s4:" (请注意,您的设备只支持两行小部件,所以中间和底部的选项是一样的)。",s5:"widget的背景图已裁切完成,想在Scriptable内部使用还是导出到相册?",s6:"已经截图,继续",s7:"退出去截图",s8:"小",s9:"中",s10:"大",s11:"顶部左边",s12:"顶部右边",s13:"中间左边",s14:"中间右边",s15:"底部左边",s16:"底部右边",s17:"顶部",s18:"中间",s19:"底部",s20:"在Scriptable内部使用",s21:"导出到相册",s22:"填写遮罩层颜色。(格式:#000000)",s23:"颜色(格式:#000000)",s24:"填写遮罩层不透明度(0-1之间)",s25:"0-1之间",s26:"确定",s27:"取消",s28:"预览widget",s29:"设置widget背景",s30:"入口",s31:"你用的是哪个型号?",s32:"退出",s33:"清除缓存",s34:"开始清除缓存",s35:"清除缓存完成"},en:{s0:"Before you start, go to your home screen and enter wiggle mode. Scroll to the empty page on the far right and take a screenshot.",s1:"It looks like you selected an image that isn't an iPhone screenshot, or your iPhone is not supported. Try again with a different image.",s2:"What size of widget are you creating?",s3:"What position will it be in?",s4:" (Note that your device only supports two rows of widgets, so the middle and bottom options are the same.)",s5:"Your widget background is ready. Would you like to use it in a Scriptable widget or export the image?",s6:"Continue",s7:"Exit to Take Screenshot",s8:"Small",s9:"Medium",s10:"Large",s11:"Top left",s12:"Top right",s13:"Middle left",s14:"Middle right",s15:"Bottom left",s16:"Bottom right",s17:"Top",s18:"Middle",s19:"Bottom",s20:"Use in Scriptable",s21:"Export to Photos",s22:"Fill in the mask layer color. (Format: #000000)",s23:"Color.(Format: #000000)",s24:"Fill in the mask layer opacity (between 0-1)",s25:"between 0-1",s26:"Confirm",s27:"Cancel",s28:"Preview widget",s29:"Setting widget background",s30:"ENTER",s31:"What type of iPhone do you have?",s32:"Exit",s33:"Clean cache",s34:"Clean cache started",s35:"Clean cache finished"}};this.curLang=this.msg[this.lang]||this.msg.en;this.isSaveLog=this.getResultByKey(`${this.prefix}IsSaveLog${this.id}`,false);this.isEnableLog=this.getResultByKey(`${this.prefix}IsEnableLog${this.id}`,true);this.logDir=this.icloud.documentsDirectory()+"/lklogs/"+this.id;this.logSeparator="\n██";this.now=new Date;this.execStatus=true;this.notifyInfo=[];this.operations=[]}async checkLimit(){const lastRunningTime=await this.getVal(`${this.prefix}LastRunningTime${this.id}`,"local",0);const runLimitNum=this.getResultByKey(`${this.prefix}RunLimitNum${this.id}`,3e5);this.log(`上次运行时间:${lastRunningTime},运行频率限制:${runLimitNum}`);if(lastRunningTime>=0){if(this.now.getTime()-lastRunningTime<=runLimitNum){this.appendNotifyInfo("限制运行");this.isLimited=true}else{await this.setVal(`${this.prefix}LastRunningTime${this.id}`,this.now.getTime(),"local")}}return this.isLimited}getResultByKey(key,defaultValue){if(!this.options){return defaultValue}const val=this.options[key];if(this.isEmpty(val)){return defaultValue}else{return val}}appendNotifyInfo(info,type){if(type==1){this.notifyInfo=info}else{this.notifyInfo.push(`${this.logSeparator}${this.formatDate(new Date,"yyyy-MM-dd HH:mm:ss.S")}█${info}`)}}saveLog(){if(this.isSaveLog){let message;if(Array.isArray(this.notifyInfo)){message=this.notifyInfo.join("")}else{message=this.notifyInfo}if(!this.icloud.isDirectory(this.logDir)){this.icloud.createDirectory(this.logDir,true)}this.icloud.writeString(`${this.logDir}/${this.formatDate(this.now,"yyyyMMddHHmmss")}.log`,message)}}prependNotifyInfo(info){this.notifyInfo.splice(0,0,info)}execFail(){this.execStatus=false}sleep(time){return new Promise(resolve=>setTimeout(resolve,time))}log(message){if(this.isEnableLog)console.log(`${this.logSeparator}${JSON.stringify(message)}`);this.appendNotifyInfo(message)}logErr(message){this.execStatus=false;if(this.isEnableLog){console.warn(`${this.logSeparator}${this.name}执行异常:`);console.warn(message);console.warn(`\n${message.message}`)}}getContainer(key){return key=="local"?this.local:this.icloud}async getVal(key,container,defaultValue){let containerInstance=this.getContainer(container);let data="";try{let realDataFile=containerInstance.joinPath(containerInstance.documentsDirectory(),this.dataFile);if(!containerInstance.fileExists(realDataFile)){await this.setVal(key,defaultValue,container);return defaultValue}data=await containerInstance.readString(realDataFile);data=JSON.parse(data)}catch(e){throw e}if(data.hasOwnProperty(key)){return data[key]}else{await this.setVal(key,defaultValue,container);return defaultValue}}async getDataFile(container){let containerInstance=this.getContainer(container);let data="";try{let realDataFile=containerInstance.joinPath(containerInstance.documentsDirectory(),this.dataFile);if(!containerInstance.fileExists(realDataFile)){return Promise.resolve("")}data=await containerInstance.readString(realDataFile)}catch(e){throw e}return Promise.resolve(data)}async saveImage(fileName,image,container){let containerInstance=this.getContainer(container);let imagePath=containerInstance.joinPath(containerInstance.documentsDirectory(),`${this.prefix}${this.id}/${fileName}`);let imageDir=imagePath.substring(0,imagePath.lastIndexOf("/")+1);if(!containerInstance.isDirectory(imageDir)){containerInstance.createDirectory(imageDir,true)}containerInstance.writeImage(imagePath,image)}async getImage(fileName,container){let containerInstance=this.getContainer(container);let imagePath=containerInstance.joinPath(containerInstance.documentsDirectory(),`${this.prefix}${this.id}/${fileName}`);if(!containerInstance.fileExists(imagePath)){this.logErr(`file not exist: ${imagePath}`);return false}return await containerInstance.readImage(imagePath)}async setVal(key,val,container){let containerInstance=this.getContainer(container);let data;let realDataFile=containerInstance.joinPath(containerInstance.documentsDirectory(),this.dataFile);try{if(!containerInstance.fileExists(realDataFile)){data={}}else{data=await containerInstance.readString(realDataFile);data=JSON.parse(data)}}catch(e){data={}}data[key]=val;await containerInstance.writeString(realDataFile,JSON.stringify(data))}async get(options,callback=(()=>{})){let request=new Request("");request.url=options.url;request.method="GET";request.headers=options.headers;try{const result=await request.loadString();callback(request.response,result);return result}catch(e){this.logErr(e);callback(undefined,undefined)}}async post(options,callback=(()=>{})){let request=new Request("");request.url=options.url;request.body=options.body;request.method="POST";request.headers=options.headers;request.timeout=5e3;try{const result=await request.loadString();callback(request.response,result);return result}catch(e){this.logErr(e);callback(undefined,undefined)}}async loadScript({scriptName:scriptName,url:url}){this.log(`获取脚本【${scriptName}】`);const content=await this.get({url:url});this.icloud.writeString(`${this.icloud.documentsDirectory()}/${scriptName}.js`,content);this.log(`获取脚本【${scriptName}】完成🎉`)}require({scriptName:scriptName,url:url="",reload:reload=false}){if(this.icloud.fileExists(this.icloud.joinPath(this.icloud.documentsDirectory(),`${scriptName}.js`))&&!reload){this.log(`引用脚本【${scriptName}】`);return importModule(scriptName)}else{this.loadScript({scriptName:scriptName,url:url});this.log(`引用脚本【${scriptName}】`);return importModule(scriptName)}}async generateInputAlert(message,field,defaultValue){let result=[];let alert=new Alert;alert.message=message;alert.addTextField(field,defaultValue);alert.addCancelAction(this.curLang.s27);alert.addAction(this.curLang.s26);result[0]=await alert.presentAlert();result[1]=alert.textFieldValue(0);return result}async generateAlert(message,options){let alert=new Alert;alert.message=message;for(const option of options){alert.addAction(option)}return await alert.presentAlert()}isEmpty(obj){return typeof obj=="undefined"||obj==null||obj==""||obj=="null"}isWorkingDays(now,workingDaysFlag="curlybraces",holidayFlag="gamecontroller"){return new Promise(async(resolve,reject)=>{let sp="≈";const d=this.formatDate(now,"yyyy-MM-dd");let resultStr=0;try{let curDate=await this.getVal("curDateCache","local","fff");let curDateErrorTime=await this.getVal("curDateCacheErrorTime","local",this.now.getTime());let isPreError=!this.isEmpty(curDateErrorTime)&&Number(curDateErrorTime)+5*60*1e3{if(data.indexOf("<")==0){resultStr="❌"}else{resultStr=JSON.parse(data);if(resultStr.code==-1){resultStr="❌"}else{resultStr=resultStr.type.type}}})}}catch(e){resultStr="❌";this.logErr(e)}finally{await this.setVal("curDateCache",`${d}${sp}${resultStr}`,"local");if(resultStr=="❌"){resolve(resultStr);this.log("写入运行错误时间,5分钟后重新请求!");this.setVal("curDateCache","","local");this.setVal("curDateCacheErrorTime",`${this.now.getTime()}`,"local")}else{this.setVal("curDateCacheErrorTime","","local");this.setVal("curDateCache",`${d}${sp}${resultStr}`,"local");resolve(resultStr==0||resultStr==3?workingDaysFlag:holidayFlag)}}})}randomString(len){len=len||32;var $chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";var maxPos=$chars.length;var pwd="";for(let i=0;i{return item.name[this.lang]});let customEnterCallback=customEnter.map((item,index)=>{return item.callback});if(isReset){options=customEnterNames}else{this.operations.push({callback:main});this.operations.push({callback:function(){$.widgetCutBg()}});this.operations.push({callback:function(){$.cleanCache()}});options=options.concat(customEnterNames)}customEnterCallback.forEach(callback=>{this.operations.push({callback:callback})})}options.push(this.curLang.s32);this.operations.push({callback:function(){}});return await this.generateAlert(this.curLang.s30,options)}async handleOperations(index){await this.operations[index].callback()}cleanCache(){this.log(this.curLang.s34);let filePath=this.local.joinPath(this.local.documentsDirectory(),this.dataFile);if(this.local.fileExists(filePath)){this.local.remove(filePath)}filePath=this.bgImgPath;if(this.local.fileExists(filePath)){this.local.remove(filePath)}this.log(this.curLang.s35)}formatTimeDuring(total,lang="zh",n=0){total=Number(total);let zhUnitArr=["毫秒","秒","分钟","小时","天","月","年"];let enUnitArr=["ms","s","min","h","d","m","y"];let scaleArr=[1e3,60,60,24,30,12,100];let len=total;if(len>scaleArr[n]){len=total/scaleArr[n];return this.formatTimeDuring(len,lang,++n)}else{let unit=zhUnitArr[n];if(lang==="en"){unit=enUnitArr[n]}return len.toFixed(2)+""+unit}}fileLengthFormat(total,unit="",toByte=false){total=Number(total);var unitArr=["","KB","MB","GB","TB","PB","EB","ZB"];var n=0;try{n=unitArr.indexOf(unit)}catch(e){throw e}if(toByte){if(n==0){return total}return this.fileLengthFormat(total*1024,unitArr[--n],true)}var len=total;if(len>1e3){len=total/1024;return this.fileLengthFormat(len,unitArr[++n])}else{if(n==0){return len.toFixed(2)}return len.toFixed(2)+" "+unitArr[n]}}}(scriptName,scriptId,options)} 86 | //ScriptableToolKit-end -------------------------------------------------------------------------------- /Scriptable/ugstoolkit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | targetPath="/Users/lowking/Desktop/Scripts" 3 | 4 | # 执行min 5 | uglifyjs $targetPath/Scriptable/ScriptableToolKit.js -o $targetPath/Scriptable/ScriptableToolKit.min.js 6 | 7 | # 复制min文件内容 8 | pbcopy < $targetPath/Scriptable/ScriptableToolKit.min.js 9 | 10 | # 从剪辑版复制内容 11 | pasteStr=$(pbpaste -Prefer text) 12 | 13 | # 替换到所有脚本//ScriptableToolKit-start和//ScriptableToolKit-end之间的内容 14 | echo "批量替换.js中ScriptableToolKit开始" 15 | count=0 16 | while IFS= read -r -d '' i 17 | do 18 | lineno=$(grep -n "//ScriptableToolKit-start" "$i") 19 | if [ -n "$lineno" ]; then 20 | array=() 21 | IFS=":" read -r -a array <<< "$lineno" 22 | lineno=$(( array[0] + 1 )) 23 | if [ $lineno -ge 1 ]; then 24 | sed -i '' $lineno"G" "$i" 25 | sed -i '' $lineno"d" "$i" 26 | sed -i '' "$(( lineno - 1)) r $targetPath/Scriptable/ScriptableToolKit.min.js" "$i" 27 | printf "%-20s %s\n" "替换行:$lineno" "$i" 28 | count=$(( count + 1)) 29 | fi 30 | fi 31 | done < <(find $targetPath -name '*.js' -print0 ! -path '*node_modules') 32 | echo "批量替换.js中ScriptableToolKit完成。共替换"$count"个js" 33 | 34 | -------------------------------------------------------------------------------- /Scriptable/widgetCutBg.js: -------------------------------------------------------------------------------- 1 | // https://gist.githubusercontent.com/mzeryck/3a97ccd1e059b3afa3c6666d27a496c9/raw/bbcac348d540e452228bd85aa80a5b45bb023a65/mz_invisible_widget.js 2 | // 这是原作者gist地址,本人就汉化,只为引用到自己修改的Scriptable中 3 | // Variables used by Scriptable. 4 | // These must be at the very top of the file. Do not edit. 5 | // icon-color: deep-purple; icon-glyph: image; 6 | 7 | // This widget was created by Max Zeryck @mzeryck 8 | 9 | // Widgets are unique based on the name of the script. 10 | const filename = Script.name() + ".jpg" 11 | const files = FileManager.local() 12 | const path = files.joinPath(files.documentsDirectory(), filename) 13 | // zh_CN, en 14 | const lang = "zh_CN" 15 | const msg = { 16 | "zh_CN": [ 17 | "在开始之前,先进入主屏幕,进入图标排列模式。滑到最右边的空白页,并进行截图。", 18 | "看起来你选择的图片不是iPhone的截图,或者你的iPhone不支持。请换一张图片再试一次。", 19 | "你想创建什么尺寸的widget?", 20 | "你想把widget放在哪里?", 21 | " (请注意,您的设备只支持两行小部件,所以中间和底部的选项是一样的)。", 22 | "widget的背景图已裁切完成,想在Scriptable内部使用还是导出到相册?", 23 | "已经截图,继续", 24 | "退出去截图", 25 | "小","中","大", 26 | "顶部左边","顶部右边","中间左边","中间右边","底部左边","底部右边", 27 | "顶部","中间","底部", 28 | "在Scriptable内部使用","导出到相册" 29 | ], 30 | "en": [ 31 | "Before you start, go to your home screen and enter wiggle mode. Scroll to the empty page on the far right and take a screenshot.", 32 | "It looks like you selected an image that isn't an iPhone screenshot, or your iPhone is not supported. Try again with a different image.", 33 | "What size of widget are you creating?", 34 | "What position will it be in?", 35 | " (Note that your device only supports two rows of widgets, so the middle and bottom options are the same.)", 36 | "Your widget background is ready. Would you like to use it in a Scriptable widget or export the image?", 37 | "Continue", 38 | "Exit to Take Screenshot", 39 | "Small","Medium","Large", 40 | "Top left","Top right","Middle left","Middle right","Bottom left","Bottom right", 41 | "Top","Middle","Bottom", 42 | "Use in Scriptable","Export to Photos" 43 | ] 44 | } 45 | 46 | if (config.runsInWidget) { 47 | let widget = new ListWidget() 48 | widget.backgroundImage = files.readImage(path) 49 | 50 | // Your code here 51 | 52 | Script.setWidget(widget) 53 | Script.complete() 54 | } else { 55 | 56 | // Determine if user has taken the screenshot. 57 | var message 58 | var curLang = msg[lang] 59 | message = curLang[0] 60 | let exitOptions = [curLang[6],curLang[7]] 61 | let shouldExit = await generateAlert(message,exitOptions) 62 | if (shouldExit) return 63 | 64 | // Get screenshot and determine phone size. 65 | let img = await Photos.fromLibrary() 66 | let height = img.size.height 67 | let phone = phoneSizes()[height] 68 | if (!phone) { 69 | message = curLang[1] 70 | await generateAlert(message,["OK"]) 71 | return 72 | } 73 | 74 | // Prompt for widget size and position. 75 | message = curLang[2] 76 | let sizes = [curLang[8], curLang[9], curLang[10]] 77 | let size = await generateAlert(message,sizes) 78 | 79 | message = curLang[3] 80 | message += (height == 1136 ? curLang[4] : "") 81 | 82 | // Determine image crop based on phone size. 83 | let crop = { w: "", h: "", x: "", y: "" } 84 | if (size == 0) { 85 | crop.w = phone.small 86 | crop.h = phone.small 87 | let positions = ["Top left","Top right","Middle left","Middle right","Bottom left","Bottom right"] 88 | let positionsString = [curLang[11],curLang[12],curLang[13],curLang[14],curLang[15],curLang[16]] 89 | let position = await generateAlert(message,positionsString) 90 | 91 | // Convert the two words into two keys for the phone size dictionary. 92 | let keys = positions[position].toLowerCase().split(' ') 93 | crop.y = phone[keys[0]] 94 | crop.x = phone[keys[1]] 95 | 96 | } else if (size == 1) { 97 | crop.w = phone.medium 98 | crop.h = phone.small 99 | 100 | // Medium and large widgets have a fixed x-value. 101 | crop.x = phone.left 102 | let positions = ["Top","Middle","Bottom"] 103 | let positionsString = [curLang[17],curLang[18],curLang[19]] 104 | let position = await generateAlert(message,positionsString) 105 | let key = positions[position].toLowerCase() 106 | crop.y = phone[key] 107 | 108 | } else if(size == 2) { 109 | crop.w = phone.medium 110 | crop.h = phone.large 111 | crop.x = phone.left 112 | let positions = ["Top","Bottom"] 113 | let positionsString = [curLang[17],curLang[19]] 114 | let position = await generateAlert(message,positionsString) 115 | 116 | // Large widgets at the bottom have the "middle" y-value. 117 | crop.y = position ? phone.middle : phone.top 118 | } 119 | 120 | // Crop image and finalize the widget. 121 | let imgCrop = cropImage(img, new Rect(crop.x,crop.y,crop.w,crop.h)) 122 | 123 | message = curLang[5] 124 | const exportPhotoOptions = [curLang[20],curLang[21]] 125 | const exportPhoto = await generateAlert(message,exportPhotoOptions) 126 | 127 | if (exportPhoto) { 128 | Photos.save(imgCrop) 129 | } else { 130 | files.writeImage(path,imgCrop) 131 | } 132 | 133 | Script.complete() 134 | } 135 | 136 | // Generate an alert with the provided array of options. 137 | async function generateAlert(message,options) { 138 | 139 | let alert = new Alert() 140 | alert.message = message 141 | 142 | for (const option of options) { 143 | alert.addAction(option) 144 | } 145 | 146 | let response = await alert.presentAlert() 147 | return response 148 | } 149 | 150 | // Crop an image into the specified rect. 151 | function cropImage(img,rect) { 152 | 153 | let draw = new DrawContext() 154 | draw.size = new Size(rect.width, rect.height) 155 | 156 | draw.drawImageAtPoint(img,new Point(-rect.x, -rect.y)) 157 | return draw.getImage() 158 | } 159 | 160 | // Pixel sizes and positions for widgets on all supported phones. 161 | function phoneSizes() { 162 | let phones = { 163 | "2688": { 164 | "small": 507, 165 | "medium": 1080, 166 | "large": 1137, 167 | "left": 81, 168 | "right": 654, 169 | "top": 228, 170 | "middle": 858, 171 | "bottom": 1488 172 | }, 173 | 174 | "1792": { 175 | "small": 338, 176 | "medium": 720, 177 | "large": 758, 178 | "left": 54, 179 | "right": 436, 180 | "top": 160, 181 | "middle": 580, 182 | "bottom": 1000 183 | }, 184 | 185 | "2436": { 186 | "small": 465, 187 | "medium": 987, 188 | "large": 1035, 189 | "left": 69, 190 | "right": 591, 191 | "top": 213, 192 | "middle": 783, 193 | "bottom": 1353 194 | }, 195 | 196 | "2532": { 197 | "small": 474, 198 | "medium": 1014, 199 | "large": 1062, 200 | "left": 78, 201 | "right": 618, 202 | "top": 231, 203 | "middle": 819, 204 | "bottom": 1407 205 | }, 206 | 207 | "2208": { 208 | "small": 471, 209 | "medium": 1044, 210 | "large": 1071, 211 | "left": 99, 212 | "right": 672, 213 | "top": 114, 214 | "middle": 696, 215 | "bottom": 1278 216 | }, 217 | 218 | "1334": { 219 | "small": 296, 220 | "medium": 642, 221 | "large": 648, 222 | "left": 54, 223 | "right": 400, 224 | "top": 60, 225 | "middle": 412, 226 | "bottom": 764 227 | }, 228 | 229 | "1136": { 230 | "small": 282, 231 | "medium": 584, 232 | "large": 622, 233 | "left": 30, 234 | "right": 332, 235 | "top": 59, 236 | "middle": 399, 237 | "bottom": 399 238 | }, 239 | "1624": { 240 | "small": 310, 241 | "medium": 658, 242 | "large": 690, 243 | "left": 46, 244 | "right": 394, 245 | "top": 142, 246 | "middle": 522, 247 | "bottom": 902 248 | } 249 | } 250 | return phones 251 | } 252 | -------------------------------------------------------------------------------- /Scripts.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Scripts by lowking. Self-use 2 | #!desc=该模块自用,脚本合集 3 | #!category=Self 4 | #!arguments=repoPrefix,repoSuffix 5 | #!arguments-desc=repoPrefix:个人代码托管url前缀\nrepoSuffix:个人代码托管url后缀 6 | 7 | [URL Rewrite] 8 | (http[s]?:\/\/rule.l\/)(?[^\/]+[\/])(?[^\/]+[\/])(?[\w\W]+[\/])(?.+) {{{repoPrefix}}}$2$3raw/$4$5{{{repoSuffix}}} 302 9 | 10 | [Rule] 11 | # > 京东 去启动广告,必须是REJECT-TINYGIF, 否则比价会失效 12 | URL-REGEX,^https?:\/\/api\.m\.jd\.com\/client\.action\?functionId=start$,REJECT-TINYGIF 13 | 14 | # 哔哩哔哩 去广告 15 | URL-REGEX,https://app.bilibili.com/x/v2/(splash|search/(defaultword|square)),REJECT 16 | URL-REGEX,https://api.bilibili.com/x/v2/dm/ad,REJECT 17 | 18 | 19 | [Script] 20 | # 获取当前经纬度 21 | # location = type=http-request, pattern=https://weather-data.apple.com, script-path=https://raw.githubusercontent.com/Peng-YM/QuanX/master/Tools/Location/locate.js, require-body=false 22 | 23 | # 彩云获取经纬度,虽然和上面的一样,但是数据结构不一样 24 | 彩云位置 = type=http-request, pattern=https://weather-data.apple.com, script-path=https://raw.githubusercontent.com/Peng-YM/QuanX/master/Tasks/caiyun.js, require-body=false 25 | 26 | # > bilbii 换区、评分 27 | 哔哩换区 = type=http-response,pattern=^https:\/\/ap(p|i)\.bili(bili|api)\.(com|net)\/(pgc\/view\/v\d\/app\/season|x\/offline\/version)\?,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Surge/JS/Bili_Auto_Regions.js 28 | 哔哩搜索 = type=http-request,pattern=^https:\/\/ap(p|i)\.bili(bili|api)\.(com|net)\/x\/v\d\/search(\/type)?\?.+?%20(%E6%B8%AF|%E5%8F%B0|%E4%B8%AD)&,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/Surge/JS/Bili_Auto_Regions.js 29 | 30 | 斗鱼画质过滤 = requires-body=1,type=http-response,pattern=https:\/\/playclient\.douyucdn\.cn\/lapi\/live\/appGetPlayer\/stream,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/douyu/streamQuality.js 31 | 32 | [MITM] 33 | hostname = %APPEND% playclient.douyucdn.cn, ap?.bilibili.com, ap?.biliapi.net, api.m.jd.com, trade-acs.m.taobao.com, weather-data.apple.com -------------------------------------------------------------------------------- /Snap.list: -------------------------------------------------------------------------------- 1 | DOMAIN-SUFFIX,nvprod.snapgametech.com 2 | DOMAIN-SUFFIX,braze.com 3 | DOMAIN-SUFFIX,byteoversea.com 4 | DOMAIN-SUFFIX,isnssdk.com 5 | DOMAIN-SUFFIX,bytegsdk.com 6 | DOMAIN-SUFFIX,client-api.unity3dusercontent.com 7 | DOMAIN-SUFFIX,backtrace.io -------------------------------------------------------------------------------- /Ssid.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Ssid by lowking. Self-use 2 | #!desc=该模块自用,Wi-Fi助手 3 | #!category=Self 4 | 5 | [Script] 6 | SSID助手 = debug=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/util/Ssid.js,type=event,event-name=network-changed,control-api=true -------------------------------------------------------------------------------- /Tasks-removed.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Tasks-removed by lowking. Self-use 2 | #!desc=无需安装该模块,用于记录已移除的任务 3 | 4 | [Script] 5 | hifini签到 = type=cron,cronexp="55 59 23 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/hifini/hifiniSign.js,script-update-interval=0 6 | 7 | 10000 = type=cron,cronexp="0 10 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/10000/10000.js,script-update-interval=0 8 | 9 | 云闪付积分 = type=cron,cronexp="0 10 0 * * ?",wake-system=1,timeout=120,script-path=ysf.js,script-update-interval=0 10 | 11 | eu.org审核 = type=cron,cronexp="10 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/others/eu.org.js,script-update-interval=0 12 | 13 | 京东多合一签到 = type=cron,cronexp="20 1 0,20 * * ?",wake-system=1,timeout=120,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/JD-DailyBonus/JD_DailyBonus.js,script-update-interval=0,max-size=-1 14 | 15 | 京东金融领豆 = type=cron,cronexp="20 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/dompling/Script/master/jd/jd_jr_sign.js,script-update-interval=0 16 | 17 | 顺丰签到 = type=cron,cronexp="30 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/sfexpress/sfexpress.js,script-update-interval=0 18 | 19 | 爱奇艺 = type=cron,cronexp="30 2 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/NobyDa/Script/master/iQIYI-DailyBonus/iQIYI.js,script-update-interval=0 20 | 21 | 取关京东店铺和商品 = type=cron,cronexp="20 3 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lxk0301/jd_scripts/master/jd_unsubscribe.js,script-update-interval=0 22 | 23 | 微博超话 = type=cron,cronexp="0 0 0 * * ?",wake-system=1,timeout=30,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/weibo/weiboST.js,script-update-interval=0 24 | 25 | 腾讯视频 = type=cron,cronexp="0 4 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/videoqq/videoqq.js,script-update-interval=0 26 | 27 | 斗鱼鱼吧 = type=cron,cronexp="0 2 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/douyu/yubaSign.js,script-update-interval=0 28 | 29 | 芒果TV = type=cron,cronexp="0 3 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/mgtv/mgtv.js,script-update-interval=0 30 | 31 | 滴滴出行 = type=cron,cronexp="10 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/zZPiglet/Task/master/DiDi/DiDi_new.js,script-update-interval=0 32 | 33 | 智行火车票 = type=cron,cronexp="50 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/zxhc/zxhc.js,script-update-interval=0 34 | 35 | 京东保价 = type=cron,cronexp="20 3 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/id77/QuantumultX/master/task/jdGuaranteedPrice.js,script-update-interval=0 36 | 37 | 哔哩哔哩 = type=cron,cronexp="30 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/bilibili/bilibili.js,script-update-interval=0 38 | 哔哩哔哩硬币 = type=cron,cronexp="40 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/bilibili/bilibili.silver2coin.js,script-update-interval=0 39 | 40 | ##############其他时间特殊的任务############## 41 | 朴朴签到 = type=cron,cronexp="57 59 6,8,12,15,18,20 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/pupu/pupuCheckIn.js,script-update-interval=0 42 | 43 | 哔哩哔哩大会员特权领取 = type=cron,cronexp="0 1 0 10 * ?",script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bilibili/privilegeReceive.js,script-update-interval=0 44 | 45 | epic = type=cron,cronexp="0 0 10 * * 6",wake-system=1,control-api=1,script-path=https://raw.githubusercontent.com/Peng-YM/QuanX/master/Tasks/epic.js,script-update-interval=0 46 | 47 | qq会员签到 = type=cron,cronexp="0 3 12 * * ?",wake-system=1,timeout=30,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/QQVip/qqVipCheckIn.js,script-update-interval=0 48 | 49 | CMY机场签到 = type=cron,cronexp="5 59 23 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/personal/cmy/cmy.js,script-update-interval=0 50 | 51 | 彩云天气 = type=cron,cronexp="0 6-20 * * *",wake-system=1,control-api=1,script-path=https://raw.githubusercontent.com/Peng-YM/QuanX/master/Tasks/caiyun.js,script-update-interval=0 52 | 53 | # bing积分 = type=cron,cronexp="30 */30 20-23,0-11 * * ?",wake-system=1,timeout=30,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bing/bingPoint.js,script-update-interval=0 54 | 55 | 同步京东账号 = type=cron,cronexp="0 1 5,11,17,23 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/jd/ql_sync_box.js,script-update-interval=0 56 | 登录辅助初始化 = type=cron,cronexp="0 5 5,11,17,23 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/jd/jd_cookie_search.js,script-update-interval=0 57 | 58 | bing积分1 = type=cron,cronexp="0 0 10 * * ?",wake-system=1,timeout=880,script-path=bingPoint.js,script-update-interval=0 59 | 会话切换1 = type=cron,cronexp="0 50 10 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/box/switcher/box.switcher.js,script-update-interval=0 60 | bing积分2 = type=cron,cronexp="0 0 23 * * ?",wake-system=1,timeout=880,script-path=bingPoint.js,script-update-interval=0 61 | 会话切换2 = type=cron,cronexp="0 50 23 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/box/switcher/box.switcher.js,script-update-interval=0 62 | 63 | ##############需要BoxJs多会话切换的############## 64 | QQ萌宠 = type=cron,cronexp="30 10 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/QQPet/lkQQPet.js,script-update-interval=0 65 | 66 | 会话切换1 = type=cron,cronexp="30 11 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/box/switcher/box.switcher.js,script-update-interval=0 67 | 68 | QQ萌宠 = type=cron,cronexp="35 11 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/QQPet/lkQQPet.js,script-update-interval=0 69 | 70 | 71 | ##############常开获取cookie############## 72 | 哔哩哔哩番剧监控 = type=cron,cronexp="*/29 6-23 * * *",wake-system=1,timeout=360,control-api=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bilibili/bangumiMonitor.js,script-update-interval=0 73 | 74 | 哔哩哔哩大会员特权领取cookie = type=http-request,pattern=https:\/\/data.bilibili.com\/log\/mobile,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/bilibili/privilegeReceive.js,script-update-interval=0 75 | 76 | 微博超话-cookie = type=http-request,timeout=360,pattern=https:\/\/weibo\.com\/p\/aj\/general\/button\?ajwvr=6&api=http:\/\/i\.huati\.weibo\.com\/aj\/super\/checkin,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/weibo/weiboSTCookie.js 77 | 78 | 哔哩哔哩-cookie = script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/bilibili/bilibili.cookie.js,type=http-request,pattern=^https:\/\/(www|live)\.bilibili\.com\/?.? 79 | 80 | qq会员-cookie = type=http-request,pattern=https:\/\/proxy.vac.qq.com\/cgi-bin\/srfentry.fcgi,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/QQVip/qqVipCheckIn.js 81 | 82 | [MITM] 83 | hostname = %APPEND% data.bilibili.com, proxy.vac.qq.com, weibo.com 84 | -------------------------------------------------------------------------------- /Tasks.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=Tasks by lowking. Self-use 2 | #!desc=该模块自用,脚本签到合集 3 | #!category=Self 4 | 5 | [Script] 6 | 阿里云盘签到 = type=cron,cronexp="0 1 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/ali/aliYunPanCheckIn.js,script-update-interval=0 7 | 8 | 贴吧 = type=cron,cronexp="50 2 0 * * ?",wake-system=1,timeout=120,script-path=https://raw.githubusercontent.com/chavyleung/scripts/master/tieba/tieba.js,script-update-interval=0 9 | 10 | 索尼俱乐部 = type=cron,cronexp="10 3 0 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/sony/sonyClub.js,script-update-interval=0 11 | 12 | ##############其他时间特殊的任务############## 13 | Jump游戏价格监控 = type=cron,cronexp="0 30 17 * * ?",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/jump/jumpPrice.js,script-update-interval=0 14 | 15 | EPIC限免 = type=cron,cronexp="0 0 0 * * 6",wake-system=1,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/epic/freeGames.js 16 | 17 | 绝区零 = type=cron,cronexp="0 0 13 * * ?",wake-system=1,timeout=700,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/mihoyo/zzz.js,script-update-interval=0 18 | 19 | ##############常开获取cookie############## 20 | [MITM] 21 | hostname = %APPEND% data.bilibili.com 22 | -------------------------------------------------------------------------------- /boxjs-empty.bmp: -------------------------------------------------------------------------------- 1 | BM<6( 7!7! -------------------------------------------------------------------------------- /boxjs.sysapps.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "BoxSetting.lowking", 3 | "name": "偏好设置(lowking)", 4 | "author": "@lowking", 5 | "icon": "https://raw.githubusercontent.com/chavyleung/scripts/master/box/icons/BoxSetting.png", 6 | "repo": "https://github.com/lowking/Scripts/", 7 | "apps": [ 8 | { 9 | "id": "debuggerWebs", 10 | "name": "BoxJs配置", 11 | "keys": ["@chavy_boxjs_userCfgs.debugger_webs"], 12 | "settings": [ 13 | { 14 | "id": "@chavy_boxjs_userCfgs.debugger_webs", 15 | "name": "BoxJs调试页面配置", 16 | "val": "", 17 | "type": "textarea", 18 | "desc": "每行一个配置,用逗号分割每个配置的名字和链接:配置,url" 19 | } 20 | ], 21 | "author": "@lowking", 22 | "repo": "https://github.com/lowking/Scripts", 23 | "icons": [ 24 | "https://raw.githubusercontent.com/chavyleung/scripts/master/box/icons/BoxSetting.mini.png", 25 | "https://raw.githubusercontent.com/chavyleung/scripts/master/box/icons/BoxSetting.png" 26 | ] 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /doc/icon/ChinaTelecom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/ChinaTelecom.png -------------------------------------------------------------------------------- /doc/icon/ChinaTelecom_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/ChinaTelecom_logo.png -------------------------------------------------------------------------------- /doc/icon/ChinaTelecom_logo.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/ChinaTelecom_logo.psd -------------------------------------------------------------------------------- /doc/icon/ChinaTelecom_logo_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/ChinaTelecom_logo_blue.png -------------------------------------------------------------------------------- /doc/icon/ChinaTelecom_logo_orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/ChinaTelecom_logo_orange.png -------------------------------------------------------------------------------- /doc/icon/aliYunPan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/aliYunPan.png -------------------------------------------------------------------------------- /doc/icon/aliYunPana.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/aliYunPana.png -------------------------------------------------------------------------------- /doc/icon/bilibiliBigVip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/icon/bingPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/bingPoint.png -------------------------------------------------------------------------------- /doc/icon/eu.org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/eu.org.png -------------------------------------------------------------------------------- /doc/icon/hifinisignin-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/hifinisignin-dark.png -------------------------------------------------------------------------------- /doc/icon/hifinisignin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/hifinisignin.png -------------------------------------------------------------------------------- /doc/icon/jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/jump.png -------------------------------------------------------------------------------- /doc/icon/miyoushe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/miyoushe.png -------------------------------------------------------------------------------- /doc/icon/pupu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/pupu.png -------------------------------------------------------------------------------- /doc/icon/pupua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/pupua.png -------------------------------------------------------------------------------- /doc/icon/zzz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/icon/zzz.png -------------------------------------------------------------------------------- /doc/pic/10086.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/pic/10086.jpeg -------------------------------------------------------------------------------- /doc/pic/toolkitdemo-show-phone.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/pic/toolkitdemo-show-phone.gif -------------------------------------------------------------------------------- /doc/pic/toolkitdemo-show.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/pic/toolkitdemo-show.gif -------------------------------------------------------------------------------- /doc/pic/ugtoolkit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lowking/Scripts/212d5e13a6c76e7031d8fdc71d211a386fcaf15d/doc/pic/ugtoolkit.jpg -------------------------------------------------------------------------------- /douyu/streamQuality.js: -------------------------------------------------------------------------------- 1 | /* 2 | 斗鱼画质过滤-lowking-v1.0.7 3 | 4 | ************************ 5 | Surge 4.2.0+ 脚本配置(其他APP自行转换配置): 6 | ************************ 7 | 8 | [Script] 9 | # > 斗鱼画质过滤 10 | https://playclient.douyucdn.cn/lapi/live/appGetPlayer/stream/916749 11 | 斗鱼画质过滤 = requires-body=1,type=http-response,pattern=https:\/\/playclient\.douyucdn\.cn\/lapi\/live\/appGetPlayer\/stream,script-path=https://raw.githubusercontent.com/lowking/Scripts/master/douyu/streamQuality.js 12 | 13 | [MITM] 14 | hostname = %APPEND% playclient.douyucdn.cn 15 | */ 16 | const lk = new ToolKit(`斗鱼画质过滤`, `DouyuStreamQuality`) 17 | let respBody 18 | 19 | try { 20 | respBody = lk.getResponseBody().o() 21 | lk.log(`respBodyRow: ${respBody.s(null, 2)}`) 22 | let rateSettings = respBody?.data?.rateSetting 23 | lk.log(`rateSettings: ${rateSettings.s(null, 2)}`) 24 | if (rateSettings) { 25 | let best = rateSettings.reduce((pre, cur) => { 26 | return pre?.bit < cur?.bit ? cur : pre 27 | }) 28 | lk.log(`best: ${best.s()}`) 29 | // respBody.data.rateSetting = [best] 30 | respBody.data.short_raten = best.name 31 | // respBody.data.scnt_switch = 0 32 | respBody.data.rateSwitch = best.rate 33 | respBody.data.orate = best.rate 34 | respBody.data.rate = best.rate 35 | lk.log(`respBodyModify: ${respBody.s(null, 2)}`) 36 | lk.done({body: respBody.s()}) 37 | } else { 38 | lk.log(`respBody: ${respBody.s(null, 2)}`) 39 | lk.done() 40 | } 41 | } catch (e) { 42 | lk.logErr(e) 43 | } 44 | 45 | // * ToolKit v1.4.0 build 155 46 | function ToolKit(scriptName,scriptId,options){class Request{constructor(tk){this.tk=tk}fetch(options,method="GET"){options=typeof options=="string"?{url:options}:options;let fetcher;switch(method){case"PUT":fetcher=this.put;break;case"POST":fetcher=this.post;break;default:fetcher=this.get}const doFetch=new Promise((resolve,reject)=>{fetcher.call(this,options,(error,resp,data)=>error?reject({error,resp,data}):resolve({error,resp,data}))}),delayFetch=(promise,timeout=5e3)=>Promise.race([promise,new Promise((_,reject)=>setTimeout(()=>reject(new Error("请求超时")),timeout))]);return options.timeout>0?delayFetch(doFetch,options.timeout):doFetch}async get(options){return this.fetch.call(this.tk,options)}async post(options){return this.fetch.call(this.tk,options,"POST")}async put(options){return this.fetch.call(this.tk,options,"PUT")}}return new class{constructor(scriptName,scriptId,options){Object.prototype.s=function(replacer,space){return typeof this=="string"?this:JSON.stringify(this,replacer,space)},Object.prototype.o=function(reviver){return JSON.parse(this,reviver)},Object.prototype.getIgnoreCase=function(key){if(!key)throw"Key required";let target=this;try{typeof this=="string"&&(target=JSON.stringify(this))}catch{throw"It's not a JSON object or string!"}const ret=Object.keys(target).reduce((obj,key)=>(obj[key.toLowerCase()]=target[key],obj),{});return ret[key]},this.userAgent=`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`,this.a=`lk`,this.name=scriptName,this.id=scriptId,this.req=new Request(this),this.data=null,this.b=this.fb(`${this.a}${this.id}.dat`),this.c=this.fb(`${this.a}${this.id}.boxjs.json`),this.d=options,this.isExecComm=!1,this.f=this.getVal(`${this.a}IsEnableLog${this.id}`),this.f=!!this.isEmpty(this.f)||this.f.o(),this.g=this.getVal(`${this.a}NotifyOnlyFail${this.id}`),this.g=!this.isEmpty(this.g)&&this.g.o(),this.h=this.getVal(`${this.a}IsEnableTgNotify${this.id}`),this.h=!this.isEmpty(this.h)&&this.h.o(),this.i=this.getVal(`${this.a}TgNotifyUrl${this.id}`),this.h=this.h?!this.isEmpty(this.i):this.h,this.j=`${this.a}CostTotalString${this.id}`,this.k=this.getVal(this.j),this.k=this.isEmpty(this.k)?`0,0`:this.k.replace('"',""),this.l=this.k.split(",")[0],this.m=this.k.split(",")[1],this.n=0,this.o=` 47 | ██`,this.p=" ",this.now=new Date,this.q=this.now.getTime(),this.node=(()=>{if(this.isNode()){const request=require("request");return{request}}return null})(),this.r=!0,this.s=[],this.t="chavy_boxjs_cur__acs",this.u="chavy_boxjs__acs",this.v={"|`|":",backQuote,"},this.w={",backQuote,":"`","%2CbackQuote%2C":"`"},this.y={"_":"\\_","*":"\\*","`":"\\`"},this.x={"_":"\\_","*":"\\*","[":"\\[","]":"\\]","(":"\\(",")":"\\)","~":"\\~","`":"\\`",">":"\\>","#":"\\#","+":"\\+","-":"\\-","=":"\\=","|":"\\|","{":"\\{","}":"\\}",".":"\\.","!":"\\!"},this.log(`${this.name}, 开始执行!`),this.fd()}fb(_a){if(!this.isNode())return _a;let _b=process.argv.slice(1,2)[0].split("/");return _b[_b.length-1]=_a,_b.join("/")}fc(_a){const _c=this.path.resolve(_a),_d=this.path.resolve(process.cwd(),_a),_e=this.fs.existsSync(_c),_f=!_e&&this.fs.existsSync(_d);return{_c,_d,_e,_f}}async fd(){if(!this.isNode())return;if(this.e=process.argv.slice(1),this.e[1]!="p")return;this.isExecComm=!0,this.log(`开始执行指令【${this.e[1]}】=> 发送到其他终端测试脚本!`);let httpApi=this.d?.httpApi,_h;if(this.isEmpty(this?.d?.httpApi))this.log(`未设置options,使用默认值`),this.isEmpty(this?.d)&&(this.d={}),this.d.httpApi=`ffff@10.0.0.6:6166`,httpApi=this.d.httpApi,_h=httpApi.split("@")[1];else{if(typeof httpApi=="object")if(_h=this.isNumeric(this.e[2])?this.e[3]||"unknown":this.e[2],httpApi[_h])httpApi=httpApi[_h];else{const keys=Object.keys(httpApi);keys[0]?(_h=keys[0],httpApi=httpApi[keys[0]]):httpApi="error"}if(!/.*?@.*?:[0-9]+/.test(httpApi)){this.log(`❌httpApi格式错误!格式: ffff@3.3.3.18:6166`),this.done();return}}this.fe(this.e[2],_h,httpApi)}fe(timeout,_h,httpApi){let _i=this.e[0];const[_j,_k]=httpApi.split("@");this.log(`获取【${_i}】内容传给【${_h||_k}】`),this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(_i);if(!_e&&!_f){lk.done();return}const _m=_e?_c:_d;let options={url:`http://${_k}/v1/scripting/evaluate`,headers:{"X-Key":_j},body:{script_text:String(this.fs.readFileSync(_m)),mock_type:"cron",timeout:!this.isEmpty(timeout)&&timeout>5?timeout:5},json:!0};this.req.post(options).then(({error,resp,data})=>{this.log(`已将脚本【${_i}】发给【${_h||_k}】,执行结果: 48 | ${this.p}error: ${error} 49 | ${this.p}resp: ${resp?.s()} 50 | ${this.p}data: ${this.fj(data)}`),this.done()})}boxJsJsonBuilder(info,param){if(!this.isNode())return;if(!this.isJsonObject(info)||!this.isJsonObject(param)){this.log("构建BoxJsJson传入参数格式错误,请传入json对象");return}let _p=param?.targetBoxjsJsonPath||"/Users/lowking/Desktop/Scripts/lowking.boxjs.json";if(!this.fs.existsSync(_p))return;this.log("using node");let _q=["settings","keys"];const _r="https://raw.githubusercontent.com/Orz-3";let boxJsJson={},scritpUrl="#lk{script_url}";if(boxJsJson.id=`${this.a}${this.id}`,boxJsJson.name=this.name,boxJsJson.desc_html=`⚠️使用说明
详情【点我查看】`,boxJsJson.icons=[`${_r}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`,`${_r}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`],boxJsJson.keys=[],boxJsJson.settings=[{id:`${this.a}IsEnableLog${this.id}`,name:"开启/关闭日志",val:!0,type:"boolean",desc:"默认开启"},{id:`${this.a}NotifyOnlyFail${this.id}`,name:"只当执行失败才通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.a}IsEnableTgNotify${this.id}`,name:"开启/关闭Telegram通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.a}TgNotifyUrl${this.id}`,name:"Telegram通知地址",val:"",type:"text",desc:"Tg的通知地址,如: https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="}],boxJsJson.author="#lk{author}",boxJsJson.repo="#lk{repo}",boxJsJson.script=`${scritpUrl}?raw=true`,!this.isEmpty(info))for(let key of _q){if(this.isEmpty(info[key]))break;if(key==="settings")for(let i=0;iapp.id==boxJsJson.id)[0]);targetIdx>=0?boxjsJson.apps[targetIdx]=boxJsJson:boxjsJson.apps.push(boxJsJson);let ret=boxjsJson.s(null,2);if(!this.isEmpty(param))for(const key in param){let val=param[key];if(!val)switch(key){case"author":val="@lowking";break;case"repo":val="https://github.com/lowking/Scripts";break;default:continue}ret=ret.replaceAll(`#lk{${key}}`,val)}const regex=/(?:#lk\{)(.+?)(?=\})/;let m=regex.exec(ret);m!==null&&this.log(`⚠️生成BoxJs还有未配置的参数,请参考: 51 | ${this.p}https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L19 52 | ${this.p}传入参数: `);let _n=new Set;for(;(m=regex.exec(ret))!==null;)_n.add(m[1]),ret=ret.replace(`#lk{${m[1]}}`,``);_n.forEach(p=>console.log(`${this.p}${p}`)),this.fs.writeFileSync(_p,ret)}isJsonObject(obj){return typeof obj=="object"&&Object.prototype.toString.call(obj).toLowerCase()=="[object object]"&&!obj.length}appendNotifyInfo(info,type){type==1?this.s=info:this.s.push(info)}prependNotifyInfo(info){this.s.splice(0,0,info)}execFail(){this.r=!1}isRequest(){return typeof $request!="undefined"}isSurge(){return typeof $httpClient!="undefined"}isQuanX(){return typeof $task!="undefined"}isLoon(){return typeof $loon!="undefined"}isJSBox(){return typeof $app!="undefined"&&typeof $http!="undefined"}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}isNode(){return typeof require=="function"&&!this.isJSBox()}sleep(ms){return this.n+=ms,new Promise(resolve=>setTimeout(resolve,ms))}randomSleep(minMs,maxMs){return this.sleep(this.randomNumber(minMs,maxMs))}randomNumber(min,max){return Math.floor(Math.random()*(max-min+1)+min)}log(message){this.f&&console.log(`${this.o}${message}`)}logErr(message){if(this.r=!0,this.f){let msg="";this.isEmpty(message.error)||(msg=`${msg} 53 | ${this.p}${message.error.s()}`),this.isEmpty(message.message)||(msg=`${msg} 54 | ${this.p}${message.message.s()}`),msg=`${this.o}${this.name}执行异常:${this.p}${msg}`,message&&(msg=`${msg} 55 | ${this.p}${message.s()}`),console.log(msg)}}ff(mapping,message){for(let key in mapping){if(!mapping.hasOwnProperty(key))continue;message=message.replaceAll(key,mapping[key])}return message}msg(subtitle,message,openUrl,mediaUrl,copyText,disappearS){if(!this.isRequest()&&this.g&&this.r)return;if(this.isEmpty(message)&&(Array.isArray(this.s)?message=this.s.join(` 56 | `):message=this.s),this.isEmpty(message))return;if(this.h){this.log(`${this.name}Tg通知开始`);const fa=this.i&&this.i.indexOf("parse_mode=Markdown")!=-1;if(fa){message=this.ff(this.v,message);let _t=this.y;this.i.indexOf("parse_mode=MarkdownV2")!=-1&&(_t=this.x),message=this.ff(_t,message)}message=`📌${this.name} 57 | ${message}`,fa&&(message=this.ff(this.w,message));let u=`${this.i}${encodeURIComponent(message)}`;this.req.get({url:u})}else{let options={};const _u=!this.isEmpty(openUrl),_v=!this.isEmpty(mediaUrl),_w=!this.isEmpty(copyText),_x=disappearS>0;this.isSurge()||this.isLoon()||this.isStash()?(_u&&(options.url=openUrl,options.action="open-url"),_w&&(options.text=copyText,options.action="clipboard"),this.isSurge()&&_x&&(options["auto-dismiss"]=disappearS),_v&&(options["media-url"]=mediaUrl),$notification.post(this.name,subtitle,message,options)):this.isQuanX()?(_u&&(options["open-url"]=openUrl),_v&&(options["media-url"]=mediaUrl),$notify(this.name,subtitle,message,options)):this.isNode()?this.log("⭐️"+this.name+` 58 | `+subtitle+` 59 | `+message):this.isJSBox()&&$push.schedule({title:this.name,body:subtitle?subtitle+` 60 | `+message:message})}}getVal(key,defaultValue){let value;return this.isSurge()||this.isLoon()||this.isStash()?value=$persistentStore.read(key):this.isQuanX()?value=$prefs.valueForKey(key):this.isNode()?(this.data=this.fh(),value=process.env[key]||this.data[key]):value=this.data&&this.data[key]||null,value||defaultValue}fg(key,val){if(key==this.u)return;const _y=`${this.a}${this.id}`;let _z=this.getVal(this.t,"{}").o();if(!_z.hasOwnProperty(_y))return;let curSessionId=_z[_y],_aa=this.getVal(this.u,"[]").o();if(_aa.length==0)return;let _ab=[];if(_aa.forEach(_ac=>{_ac.id==curSessionId&&(_ab=_ac.datas)}),_ab.length==0)return;let _ad=!1;_ab.forEach(kv=>{kv.key==key&&(kv.val=val,_ad=!0)}),_ad||_ab.push({key,val}),_aa.forEach(_ac=>{_ac.id==curSessionId&&(_ac.datas=_ab)}),this.setVal(this.u,_aa.s())}setVal(key,val){return this.isSurge()||this.isLoon()||this.isStash()?(this.fg(key,val),$persistentStore.write(val,key)):this.isQuanX()?(this.fg(key,val),$prefs.setValueForKey(val,key)):this.isNode()?(this.data=this.fh(),this.data[key]=val,this.fi(),!0):this.data&&this.data[key]||null}fh(){if(!this.isNode())return{};this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(this.b);if(_e||_f){const _m=_e?_c:_d;return this.fs.readFileSync(_m).o()}return{}}fi(){if(!this.isNode())return;this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(this.b),_g=this.data.s();_e?this.fs.writeFileSync(_c,_g):_f?this.fs.writeFileSync(_d,_g):this.fs.writeFileSync(_c,_g)}fj(data){const _s=`${this.p}${this.p}`;let ret="";return Object.keys(data).forEach(key=>{let lines=data[key]?.s().split(` 61 | `);key=="output"&&(lines=lines.slice(0,-2)),ret=`${ret} 62 | ${_s}${key}: 63 | ${_s}${this.p}${lines?.join(` 64 | ${_s}${this.p}`)}`}),ret}fk(response){return response&&(response.status=response?.status||response?.statusCode,delete response.statusCode,response)}get(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?$httpClient.get(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="GET",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?this.node.request(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.get(options))}post(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?$httpClient.post(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="POST",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?this.node.request.post(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}put(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?(options.method="PUT",$httpClient.put(options,(error,response,body)=>{callback(error,this.fk(response),body)})):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="PUT",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?(options.method="PUT",this.node.request.put(options,(error,response,body)=>{callback(error,this.fk(response),body)})):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}sum(a,b){let aa=Array.from(a,Number),bb=Array.from(b,Number),ret=[],c=0,i=Math.max(a.length,b.length);for(;i--;)c+=(aa.pop()||0)+(bb.pop()||0),ret.unshift(c%10),c=Math.floor(c/10);for(;c;)ret.unshift(c%10),c=Math.floor(c/10);return ret.join("")}fl(){let info=`${this.name}, 执行完毕!`;this.isNode()&&this.isExecComm&&(info=`指令【${this.e[1]}】执行完毕!`);const endTime=(new Date).getTime(),ms=endTime-this.q,fl=ms/1e3,count=this.sum(this.m,"1"),total=this.sum(this.l,ms.s()),average=(Number(total)/Number(count)/1e3).toFixed(4);info=`${info} 65 | ${this.p}耗时【${fl}】秒(含休眠${this.n?(this.n/1e3).toFixed(4):0}秒)`,info=`${info} 66 | ${this.p}总共执行【${count}】次,平均耗时【${average}】秒`,info=`${info} 67 | ${this.p}ToolKit v1.4.0 build 155.`,this.log(info),this.setVal(this.j,`${total},${count}`.s())}done(value={}){this.fl(),(this.isSurge()||this.isQuanX()||this.isLoon()||this.isStash())&&$done(value)}getRequestUrl(){return $request.url}getResponseBody(){return $response.body}isMatch(reg){return!!($request.method!="OPTIONS"&&this.getRequestUrl().match(reg))}isEmpty(obj){return typeof obj=="undefined"||obj==null||obj.s()=="{}"||obj==""||obj.s()=='""'||obj.s()=="null"||obj.s()=="undefined"||obj.length===0}isNumeric(s){return!isNaN(parseFloat(s))&&isFinite(s)}randomString(len,chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"){len=len||32;let maxPos=chars.length,pwd="";for(let i=0;i 京东账号 ck 检索 13 | 3.点击右上角运行按钮初始化京东 ck 数据 14 | 4.初始完成之后,给各个账号添加备注就能愉快的搜索你的京东 ck 了。 15 | 5.搜索方式:设置关键字 下标(数组下标从 0 开始)、username(京东 ck 的 pin)、nickname(给京东账号设置的备注昵称), status(正常|未登录) 16 | 搜索示例:0,2Y,正常 17 | 返回结果:返回下标为 0 的,返回 2Y (username|nickname),返回正常状态的 18 | task : 每日九点检查 ck 过期状态 19 | 0 9 * * * https://raw.githubusercontent.com/dompling/Script/master/jd/jd_cookie_search.js 20 | 21 | */ 22 | const noTitle = '登陆助手' 23 | const $ = new API('jd_ck_remark') 24 | $.msg = '' 25 | const APIKey = 'CookiesJD' 26 | const CacheKey = `#${APIKey}` 27 | const remark_key = `remark` 28 | const searchKey = 'keyword' 29 | const keyword = ($.read(searchKey) || '').split(',') 30 | const cookiesRemark = JSON.parse($.read(remark_key) || '[]') 31 | const CookiesJD = JSON.parse($.read(CacheKey) || '[]') 32 | const CookieJD = $.read('#CookieJD') 33 | const CookieJD2 = $.read('#CookieJD2') 34 | const ckData = CookiesJD.map((item) => item.cookie) 35 | if (CookieJD) ckData.unshift(CookieJD) 36 | if (CookieJD2) ckData.unshift(CookieJD2) 37 | 38 | console.log('初始化备注开始') 39 | console.log(`=========== 检测到京东账号:【${ckData.length}】个 ===========`) 40 | 41 | const ckRemarkFormat = {} 42 | cookiesRemark.forEach((item) => { 43 | ckRemarkFormat[item.username] = item 44 | }) 45 | ;(async () => { 46 | const ckFormat = [] 47 | const notLogin = [] 48 | let ckIndex = 1 49 | for (const cookie of ckData) { 50 | let username = cookie.match(/pt_pin=(.+?);/)[1] 51 | username = decodeURIComponent(username) 52 | console.log('===================================') 53 | console.log(`检查开始:账号 ${username} 【登陆状态】`) 54 | const response = await isLogin(cookie) 55 | const status = response.retcode === '0' ? '正常' : '未登录' 56 | 57 | let avatar = '', 58 | nickname = '', 59 | isPlusVip = 0, 60 | beanNum = 0, 61 | fruit = '', 62 | jdPet = '', 63 | mobile = ckRemarkFormat[username] ? ckRemarkFormat[username].mobile : '' 64 | if (response.retcode === '0') { 65 | isPlusVip = response.data.userInfo.isPlusVip 66 | beanNum = response.data.assetInfo.beanNum 67 | avatar = response.data.userInfo.baseInfo.headImageUrl 68 | nickname = response.data.userInfo.baseInfo.nickname 69 | console.log('帐号昵称:' + nickname) 70 | if (!mobile) mobile = await getPhoneNumber(cookie) 71 | fruit = await getFruit(cookie) 72 | jdPet = await PetRequest('energyCollect', cookie) 73 | } 74 | 75 | console.log(`检查结束:账号【${ckIndex}】 ${username}【${status}】`) 76 | console.log('===================================') 77 | let newRemark = 78 | nickname || 79 | (ckRemarkFormat[username] ? ckRemarkFormat[username].remark : '') 80 | 81 | const item = { 82 | index: ckIndex, 83 | username, 84 | nickname, 85 | qywxUserId: '', 86 | cardId: '', 87 | paymentCode: '', 88 | avatar, 89 | ...ckRemarkFormat[username], 90 | jdPet, 91 | fruit, 92 | beanNum, 93 | mobile, 94 | isPlusVip, 95 | status, 96 | remark: newRemark, 97 | } 98 | if (status === '未登录') notLogin.push(item) 99 | ckFormat.push(item) 100 | ckIndex++ 101 | } 102 | $.msg = '检索完成,所有账号状态正常!' 103 | console.log($.msg) 104 | if (notLogin.length) { 105 | console.log( 106 | `----------------未登录账号【${notLogin.length}】----------------` 107 | ) 108 | console.log( 109 | notLogin.map((item) => `${item.username}【${item.nickname}】`).join(`\n`) 110 | ) 111 | $.msg = `未登录账号:\n ${notLogin 112 | .map((item) => `账号【${item.index}】:${item.nickname || item.username}`) 113 | .join(`\n`)}` 114 | } 115 | $.write(JSON.stringify(ckFormat, null, `\t`), remark_key) 116 | console.log(`检测到${keyword.length - 1}个搜索条件:${keyword.join(',')}`) 117 | if (keyword && keyword[0]) { 118 | console.log('开始搜索中') 119 | const searchValue = ckFormat.filter((item, index) => { 120 | return ( 121 | keyword.indexOf(`${index}`) > -1 || 122 | keyword.indexOf(item.username) > -1 || 123 | keyword.indexOf(item.nickname) > -1 || 124 | keyword.indexOf(item.status) > -1 125 | ) 126 | }) 127 | if (searchValue.length) { 128 | $.msg = `已找到搜索结果:\n` 129 | searchValue.forEach((item) => { 130 | $.msg += `${item.nickname || item.username}:${item.mobile} 【${ 131 | item.status 132 | }】\n` 133 | }) 134 | } else { 135 | $.msg = '未找到相关 ck' 136 | } 137 | console.log($.msg) 138 | if ($.read('mute') !== 'true') { 139 | $.notify(noTitle, `关键字:${keyword}`, $.msg) 140 | } 141 | } else { 142 | if ($.read('mute') !== 'true') { 143 | $.notify(noTitle, ``, $.msg) 144 | } 145 | } 146 | })() 147 | .catch((e) => { 148 | console.log(e) 149 | }) 150 | .finally(() => { 151 | $.done() 152 | }) 153 | 154 | async function isLogin(Cookie) { 155 | const opt = { 156 | url: 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?sceneval=2&sceneval=2&g_login_type=1&g_ty=ls', 157 | headers: { 158 | cookie: Cookie, 159 | Referer: 'https://home.m.jd.com/', 160 | }, 161 | } 162 | return $.http.get(opt).then((response) => { 163 | try { 164 | return JSON.parse(response.body) 165 | } catch (e) { 166 | return {} 167 | } 168 | }) 169 | } 170 | 171 | async function getPhoneNumber(cookie) { 172 | const opt = { 173 | url: `https://crmsam.jd.com/union/bindingPhoneNumber`, 174 | headers: { 175 | cookie: cookie, 176 | }, 177 | } 178 | 179 | return $.http.get(opt).then((response) => { 180 | try { 181 | const data = JSON.parse(response.body) 182 | if (data.code === 200) return data.data 183 | return '' 184 | } catch (e) { 185 | return '' 186 | } 187 | }) 188 | } 189 | 190 | function taskPetUrl(function_id, body = {}, cookie) { 191 | body['version'] = 2 192 | body['channel'] = 'app' 193 | return { 194 | url: `https://api.m.jd.com/client.action?functionId=${function_id}`, 195 | body: `body=${escape( 196 | JSON.stringify(body) 197 | )}&appid=wh5&loginWQBiz=pet-town&clientVersion=9.0.4`, 198 | headers: { 199 | cookie: cookie, 200 | host: 'api.m.jd.com', 201 | 'content-type': 'application/x-www-form-urlencoded', 202 | }, 203 | } 204 | } 205 | 206 | async function PetRequest(function_id, cookie, body = {}) { 207 | return $.http.post(taskPetUrl(function_id, body, cookie)).then((response) => { 208 | try { 209 | const data = JSON.parse(response.body) 210 | if (data.code === '0') return data.result.medalPercent.toFixed(1) 211 | return '' 212 | } catch (e) { 213 | return '' 214 | } 215 | }) 216 | } 217 | 218 | async function getFruit(cookie) { 219 | const option = { 220 | url: `https://api.m.jd.com/client.action?functionId=initForFarm`, 221 | body: `body=${escape( 222 | JSON.stringify({ version: 4 }) 223 | )}&appid=wh5&clientVersion=9.1.0`, 224 | headers: { 225 | accept: '*/*', 226 | cookie: cookie, 227 | origin: 'https://home.m.jd.com', 228 | referer: 'https://home.m.jd.com/myJd/newhome.action', 229 | 230 | 'content-type': 'application/x-www-form-urlencoded', 231 | }, 232 | } 233 | 234 | return $.http.post(option).then((response) => { 235 | try { 236 | const data = JSON.parse(response.body) 237 | if (data.code === '0') 238 | return ( 239 | (data.farmUserPro.treeEnergy / data.farmUserPro.treeTotalEnergy) * 240 | 100 241 | ).toFixed(2) 242 | return '' 243 | } catch (e) { 244 | return '' 245 | } 246 | }) 247 | } 248 | 249 | function ENV() { 250 | const isQX = typeof $task !== 'undefined' 251 | const isLoon = typeof $loon !== 'undefined' 252 | const isSurge = typeof $httpClient !== 'undefined' && !isLoon 253 | const isJSBox = typeof require == 'function' && typeof $jsbox != 'undefined' 254 | const isNode = typeof require == 'function' && !isJSBox 255 | const isRequest = typeof $request !== 'undefined' 256 | const isScriptable = typeof importModule !== 'undefined' 257 | return { 258 | isQX, 259 | isLoon, 260 | isSurge, 261 | isNode, 262 | isJSBox, 263 | isRequest, 264 | isScriptable, 265 | } 266 | } 267 | 268 | function HTTP( 269 | defaultOptions = { 270 | baseURL: '', 271 | } 272 | ) { 273 | const { isQX, isLoon, isSurge, isScriptable, isNode } = ENV() 274 | const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'] 275 | const URL_REGEX = 276 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/ 277 | 278 | function send(method, options) { 279 | options = 280 | typeof options === 'string' 281 | ? { 282 | url: options, 283 | } 284 | : options 285 | const baseURL = defaultOptions.baseURL 286 | if (baseURL && !URL_REGEX.test(options.url || '')) { 287 | options.url = baseURL ? baseURL + options.url : options.url 288 | } 289 | if (options.body && options.headers && !options.headers['Content-Type']) { 290 | options.headers['Content-Type'] = 'application/x-www-form-urlencoded' 291 | } 292 | options = { 293 | ...defaultOptions, 294 | ...options, 295 | } 296 | const timeout = options.timeout 297 | const events = { 298 | ...{ 299 | onRequest: () => {}, 300 | onResponse: (resp) => resp, 301 | onTimeout: () => {}, 302 | }, 303 | ...options.events, 304 | } 305 | 306 | events.onRequest(method, options) 307 | 308 | let worker 309 | if (isQX) { 310 | worker = $task.fetch({ 311 | method, 312 | ...options, 313 | }) 314 | } else if (isLoon || isSurge || isNode) { 315 | worker = new Promise((resolve, reject) => { 316 | const request = isNode ? require('request') : $httpClient 317 | request[method.toLowerCase()](options, (err, response, body) => { 318 | if (err) reject(err) 319 | else 320 | resolve({ 321 | statusCode: response.status || response.statusCode, 322 | headers: response.headers, 323 | body, 324 | }) 325 | }) 326 | }) 327 | } else if (isScriptable) { 328 | const request = new Request(options.url) 329 | request.method = method 330 | request.headers = options.headers 331 | request.body = options.body 332 | worker = new Promise((resolve, reject) => { 333 | request 334 | .loadString() 335 | .then((body) => { 336 | resolve({ 337 | statusCode: request.response.statusCode, 338 | headers: request.response.headers, 339 | body, 340 | }) 341 | }) 342 | .catch((err) => reject(err)) 343 | }) 344 | } 345 | 346 | let timeoutid 347 | const timer = timeout 348 | ? new Promise((_, reject) => { 349 | timeoutid = setTimeout(() => { 350 | events.onTimeout() 351 | return reject( 352 | `${method} URL: ${options.url} exceeds the timeout ${timeout} ms` 353 | ) 354 | }, timeout) 355 | }) 356 | : null 357 | 358 | return ( 359 | timer 360 | ? Promise.race([timer, worker]).then((res) => { 361 | clearTimeout(timeoutid) 362 | return res 363 | }) 364 | : worker 365 | ).then((resp) => events.onResponse(resp)) 366 | } 367 | 368 | const http = {} 369 | methods.forEach( 370 | (method) => 371 | (http[method.toLowerCase()] = (options) => send(method, options)) 372 | ) 373 | return http 374 | } 375 | 376 | function API(name = 'untitled', debug = false) { 377 | const { isQX, isLoon, isSurge, isNode, isJSBox, isScriptable } = ENV() 378 | return new (class { 379 | constructor(name, debug) { 380 | this.name = name 381 | this.debug = debug 382 | 383 | this.http = HTTP() 384 | this.env = ENV() 385 | 386 | this.node = (() => { 387 | if (isNode) { 388 | const fs = require('fs') 389 | 390 | return { 391 | fs, 392 | } 393 | } else { 394 | return null 395 | } 396 | })() 397 | this.initCache() 398 | 399 | const delay = (t, v) => 400 | new Promise(function (resolve) { 401 | setTimeout(resolve.bind(null, v), t) 402 | }) 403 | 404 | Promise.prototype.delay = function (t) { 405 | return this.then(function (v) { 406 | return delay(t, v) 407 | }) 408 | } 409 | } 410 | 411 | // persistence 412 | // initialize cache 413 | initCache() { 414 | if (isQX) this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}') 415 | if (isLoon || isSurge) 416 | this.cache = JSON.parse($persistentStore.read(this.name) || '{}') 417 | 418 | if (isNode) { 419 | // create a json for root cache 420 | let fpath = 'root.json' 421 | if (!this.node.fs.existsSync(fpath)) { 422 | this.node.fs.writeFileSync( 423 | fpath, 424 | JSON.stringify({}), 425 | { 426 | flag: 'wx', 427 | }, 428 | (err) => console.log(err) 429 | ) 430 | } 431 | this.root = {} 432 | 433 | // create a json file with the given name if not exists 434 | fpath = `${this.name}.json` 435 | if (!this.node.fs.existsSync(fpath)) { 436 | this.node.fs.writeFileSync( 437 | fpath, 438 | JSON.stringify({}), 439 | { 440 | flag: 'wx', 441 | }, 442 | (err) => console.log(err) 443 | ) 444 | this.cache = {} 445 | } else { 446 | this.cache = JSON.parse( 447 | this.node.fs.readFileSync(`${this.name}.json`) 448 | ) 449 | } 450 | } 451 | } 452 | 453 | // store cache 454 | persistCache() { 455 | const data = JSON.stringify(this.cache, null, 2) 456 | if (isQX) $prefs.setValueForKey(data, this.name) 457 | if (isLoon || isSurge) $persistentStore.write(data, this.name) 458 | if (isNode) { 459 | this.node.fs.writeFileSync( 460 | `${this.name}.json`, 461 | data, 462 | { 463 | flag: 'w', 464 | }, 465 | (err) => console.log(err) 466 | ) 467 | this.node.fs.writeFileSync( 468 | 'root.json', 469 | JSON.stringify(this.root, null, 2), 470 | { 471 | flag: 'w', 472 | }, 473 | (err) => console.log(err) 474 | ) 475 | } 476 | } 477 | 478 | write(data, key) { 479 | this.log(`SET ${key}`) 480 | if (key.indexOf('#') !== -1) { 481 | key = key.substr(1) 482 | if (isSurge || isLoon) { 483 | return $persistentStore.write(data, key) 484 | } 485 | if (isQX) { 486 | return $prefs.setValueForKey(data, key) 487 | } 488 | if (isNode) { 489 | this.root[key] = data 490 | } 491 | } else { 492 | this.cache[key] = data 493 | } 494 | this.persistCache() 495 | } 496 | 497 | read(key) { 498 | this.log(`READ ${key}`) 499 | if (key.indexOf('#') !== -1) { 500 | key = key.substr(1) 501 | if (isSurge || isLoon) { 502 | return $persistentStore.read(key) 503 | } 504 | if (isQX) { 505 | return $prefs.valueForKey(key) 506 | } 507 | if (isNode) { 508 | return this.root[key] 509 | } 510 | } else { 511 | return this.cache[key] 512 | } 513 | } 514 | 515 | delete(key) { 516 | this.log(`DELETE ${key}`) 517 | if (key.indexOf('#') !== -1) { 518 | key = key.substr(1) 519 | if (isSurge || isLoon) { 520 | return $persistentStore.write(null, key) 521 | } 522 | if (isQX) { 523 | return $prefs.removeValueForKey(key) 524 | } 525 | if (isNode) { 526 | delete this.root[key] 527 | } 528 | } else { 529 | delete this.cache[key] 530 | } 531 | this.persistCache() 532 | } 533 | 534 | // notification 535 | notify(title, subtitle = '', content = '', options = {}) { 536 | const openURL = options['open-url'] 537 | const mediaURL = options['media-url'] 538 | 539 | if (isQX) $notify(title, subtitle, content, options) 540 | if (isSurge) { 541 | $notification.post( 542 | title, 543 | subtitle, 544 | content + `${mediaURL ? '\n多媒体:' + mediaURL : ''}`, 545 | { 546 | url: openURL, 547 | } 548 | ) 549 | } 550 | if (isLoon) { 551 | let opts = {} 552 | if (openURL) opts['openUrl'] = openURL 553 | if (mediaURL) opts['mediaUrl'] = mediaURL 554 | if (JSON.stringify(opts) === '{}') { 555 | $notification.post(title, subtitle, content) 556 | } else { 557 | $notification.post(title, subtitle, content, opts) 558 | } 559 | } 560 | if (isNode || isScriptable) { 561 | const content_ = 562 | content + 563 | (openURL ? `\n点击跳转: ${openURL}` : '') + 564 | (mediaURL ? `\n多媒体: ${mediaURL}` : '') 565 | if (isJSBox) { 566 | const push = require('push') 567 | push.schedule({ 568 | title: title, 569 | body: (subtitle ? subtitle + '\n' : '') + content_, 570 | }) 571 | } else { 572 | console.log(`${title}\n${subtitle}\n${content_}\n\n`) 573 | } 574 | } 575 | } 576 | 577 | // other helper functions 578 | log(msg) { 579 | if (this.debug) console.log(`[${this.name}] LOG: ${this.stringify(msg)}`) 580 | } 581 | 582 | info(msg) { 583 | console.log(`[${this.name}] INFO: ${this.stringify(msg)}`) 584 | } 585 | 586 | error(msg) { 587 | console.log(`[${this.name}] ERROR: ${this.stringify(msg)}`) 588 | } 589 | 590 | wait(millisec) { 591 | return new Promise((resolve) => setTimeout(resolve, millisec)) 592 | } 593 | 594 | done(value = {}) { 595 | if (isQX || isLoon || isSurge) { 596 | $done(value) 597 | } else if (isNode && !isJSBox) { 598 | if (typeof $context !== 'undefined') { 599 | $context.headers = value.headers 600 | $context.statusCode = value.statusCode 601 | $context.body = value.body 602 | } 603 | } 604 | } 605 | 606 | stringify(obj_or_str) { 607 | if (typeof obj_or_str === 'string' || obj_or_str instanceof String) 608 | return obj_or_str 609 | else 610 | try { 611 | return JSON.stringify(obj_or_str, null, 2) 612 | } catch (err) { 613 | return '[object Object]' 614 | } 615 | } 616 | })(name, debug) 617 | } 618 | -------------------------------------------------------------------------------- /jd/ql_api.js: -------------------------------------------------------------------------------- 1 | 2 | $.ql = { 3 | type: 'api', 4 | headers: { 5 | 'Content-Type': `application/json;charset=UTF-8`, 6 | Authorization: '', 7 | }, 8 | disabled(ids) { 9 | if (!this.headers.Authorization) return; 10 | const opt = { 11 | url: `${$.ql_url}/${this.type}/envs/disable`, 12 | headers: this.headers, 13 | body: JSON.stringify(ids), 14 | }; 15 | return $.http.put(opt).then((response) => JSON.parse(response.body)); 16 | }, 17 | enabled(ids) { 18 | if (!this.headers.Authorization) return; 19 | const opt = { 20 | url: `${$.ql_url}/${this.type}/envs/enable`, 21 | headers: this.headers, 22 | body: JSON.stringify(ids), 23 | }; 24 | return $.http.put(opt).then((response) => JSON.parse(response.body)); 25 | }, 26 | delete(ids) { 27 | if (!this.headers.Authorization) return; 28 | const opt = { 29 | url: `${$.ql_url}/${this.type}/envs`, 30 | headers: this.headers, 31 | body: JSON.stringify(ids), 32 | }; 33 | return $.http.delete(opt).then((response) => JSON.parse(response.body)); 34 | }, 35 | add(records) { 36 | if (!this.headers.Authorization) return; 37 | const opt = { 38 | url: `${$.ql_url}/${this.type}/envs`, 39 | headers: this.headers, 40 | body: JSON.stringify(records), 41 | }; 42 | return $.http.post(opt).then((response) => JSON.parse(response.body)); 43 | }, 44 | edit(records) { 45 | if (!this.headers.Authorization) return; 46 | const opt = { 47 | url: `${$.ql_url}/${this.type}/envs`, 48 | headers: this.headers, 49 | body: JSON.stringify(records), 50 | }; 51 | return $.http.put(opt).then((response) => JSON.parse(response.body)); 52 | }, 53 | select(searchValue = 'JD_COOKIE') { 54 | if (!this.headers.Authorization) return; 55 | const opt = { 56 | url: `${$.ql_url}/${this.type}/envs?searchValue=${searchValue}`, 57 | headers: this.headers, 58 | }; 59 | return $.http.get(opt).then((response) => JSON.parse(response.body)); 60 | }, 61 | }; 62 | 63 | try { 64 | $.ql_config = JSON.parse($.read('#ql')); 65 | } catch (e) { 66 | $.ql_config = {}; 67 | } 68 | 69 | $.ql_url = $.ql_config.ip; 70 | if (!$.ql_url.match(/^(http|https)/)) $.ql_url = `http://${$.ql_url}`; 71 | 72 | $.application = { 73 | client_id: $.ql_config.client_id, 74 | client_secret: $.ql_config.client_secret, 75 | }; 76 | 77 | $.ql_account = { 78 | username: $.ql_config.username, 79 | password: $.ql_config.password, 80 | }; 81 | 82 | $.log(`地址:${$.ql_url}`); 83 | 84 | function noReady() { 85 | $.ql = false; 86 | $.log('请配置好相关信息'); 87 | } 88 | 89 | if ($.ql_config.is_pwd === 'true') { 90 | if ($.ql_account.username && $.ql_account.password) { 91 | $.ql.login = async () => { 92 | const options = { 93 | url: `${$.ql_url}/api/user/login`, 94 | body: JSON.stringify($.ql_account), 95 | headers: { 96 | 'Content-Type': `application/json;charset=UTF-8`, 97 | }, 98 | }; 99 | 100 | let response = await $.http.post(options); 101 | response = JSON.parse(response.body); 102 | if (response.code === 200) { 103 | $.ql.type = 'api'; 104 | $.ql.headers.Authorization = `Bearer ${response.data.token}`; 105 | $.log(`登陆成功:${response.data.lastaddr}`); 106 | $.log(`ip:${response.data.lastip}`); 107 | } else { 108 | $.log(response); 109 | $.log(`登陆失败:${response.message}`); 110 | } 111 | }; 112 | } else { 113 | noReady(); 114 | } 115 | } else { 116 | if ($.application.client_id && $.application.client_secret) { 117 | $.ql.login = async () => { 118 | const options = { 119 | url: `${$.ql_url}/open/auth/token?client_id=${$.application.client_id}&client_secret=${$.application.client_secret}`, 120 | headers: { 121 | 'Content-Type': `application/json;charset=UTF-8`, 122 | }, 123 | }; 124 | let response = await $.http.get(options); 125 | response = JSON.parse(response.body); 126 | if (response.code === 200) { 127 | $.ql.type = 'open'; 128 | $.ql.headers.Authorization = `Bearer ${response.data.token}`; 129 | $.log(`登陆成功`); 130 | } else { 131 | $.log(response); 132 | $.log(`登陆失败:${response.message}`); 133 | } 134 | }; 135 | } else { 136 | noReady(); 137 | } 138 | } -------------------------------------------------------------------------------- /jd/ql_sync_box.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 青龙 docker 每日自动同步 boxjs cookie 4 | 40 * * * https://raw.githubusercontent.com/dompling/Script/master/jd/ql_cookie_sync.js 5 | */ 6 | 7 | const $ = new API('ql', true) 8 | 9 | const title = '🐉 通知提示' 10 | const cookiesKey = '#CookiesJD' 11 | 12 | let jd_cookies = [] 13 | try { 14 | jd_cookies = JSON.parse($.read(cookiesKey) || '[]') 15 | } catch (e) { 16 | console.log(e) 17 | } 18 | 19 | function getUsername(ck) { 20 | if (!ck) return '' 21 | return decodeURIComponent(ck.match(/pin=(.+?);/)[1]) 22 | } 23 | 24 | async function getScriptUrl() { 25 | const response = await $.http.get({ 26 | url: 'https://raw.githubusercontent.com/lowking/Scripts/master/jd/ql_api.js', 27 | }) 28 | return response.body 29 | } 30 | 31 | ;(async () => { 32 | const ql_script = (await getScriptUrl()) || '' 33 | eval(ql_script) 34 | await $.ql.login() 35 | const cookiesRes = await $.ql.select() 36 | const wskeyRes = await $.ql.select('JD_WSCK') 37 | 38 | const wskey = {} 39 | wskeyRes.data.forEach((item) => { 40 | const pin = getUsername(item.value) 41 | wskey[pin] = item.value 42 | }) 43 | 44 | const cookies = cookiesRes.data.map((item) => { 45 | const key = getUsername(item.value) 46 | return { userName: key, cookie: item.value } 47 | }) 48 | const saveCookie = jd_cookies.map((item) => { 49 | const qlCk = cookies.find((ql) => ql.userName === item.userName) 50 | let temp = { ...item } 51 | if (qlCk) { 52 | temp = { ...temp, ...qlCk } 53 | if (wskey[item.userName]) temp.wskey = wskey[item.userName] 54 | } 55 | return temp 56 | }) 57 | const userNames = saveCookie.map((item) => item.userName) 58 | cookies.forEach((ql) => { 59 | if (userNames.indexOf(ql.userName) === -1) saveCookie.push(ql) 60 | }) 61 | $.write(JSON.stringify(saveCookie, null, `\t`), cookiesKey); 62 | if ($.read('mute') !== 'true') { 63 | return $.notify( 64 | title, 65 | '已同步账号', 66 | `${cookies.map((item) => item.userName).join(`\n`)}` 67 | ) 68 | } 69 | })() 70 | .catch((e) => { 71 | $.log(JSON.stringify(e)) 72 | }) 73 | .finally(() => { 74 | $.done() 75 | }) 76 | 77 | function ENV() { 78 | const isQX = typeof $task !== 'undefined' 79 | const isLoon = typeof $loon !== 'undefined' 80 | const isSurge = typeof $httpClient !== 'undefined' && !isLoon 81 | const isJSBox = typeof require == 'function' && typeof $jsbox != 'undefined' 82 | const isNode = typeof require == 'function' && !isJSBox 83 | const isRequest = typeof $request !== 'undefined' 84 | const isScriptable = typeof importModule !== 'undefined' 85 | return { isQX, isLoon, isSurge, isNode, isJSBox, isRequest, isScriptable } 86 | } 87 | 88 | function HTTP(defaultOptions = { baseURL: '' }) { 89 | const { isQX, isLoon, isSurge, isScriptable, isNode } = ENV() 90 | const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH'] 91 | const URL_REGEX = 92 | /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/ 93 | 94 | function send(method, options) { 95 | options = typeof options === 'string' ? { url: options } : options 96 | const baseURL = defaultOptions.baseURL 97 | if (baseURL && !URL_REGEX.test(options.url || '')) { 98 | options.url = baseURL ? baseURL + options.url : options.url 99 | } 100 | options = { ...defaultOptions, ...options } 101 | const timeout = options.timeout 102 | const events = { 103 | ...{ 104 | onRequest: () => {}, 105 | onResponse: (resp) => resp, 106 | onTimeout: () => {}, 107 | }, 108 | ...options.events, 109 | } 110 | 111 | events.onRequest(method, options) 112 | 113 | let worker 114 | if (isQX) { 115 | worker = $task.fetch({ method, ...options }) 116 | } else if (isLoon || isSurge || isNode) { 117 | worker = new Promise((resolve, reject) => { 118 | const request = isNode ? require('request') : $httpClient 119 | request[method.toLowerCase()](options, (err, response, body) => { 120 | if (err) reject(err) 121 | else 122 | resolve({ 123 | statusCode: response.status || response.statusCode, 124 | headers: response.headers, 125 | body, 126 | }) 127 | }) 128 | }) 129 | } else if (isScriptable) { 130 | const request = new Request(options.url) 131 | request.method = method 132 | request.headers = options.headers 133 | request.body = options.body 134 | worker = new Promise((resolve, reject) => { 135 | request 136 | .loadString() 137 | .then((body) => { 138 | resolve({ 139 | statusCode: request.response.statusCode, 140 | headers: request.response.headers, 141 | body, 142 | }) 143 | }) 144 | .catch((err) => reject(err)) 145 | }) 146 | } 147 | 148 | let timeoutid 149 | const timer = timeout 150 | ? new Promise((_, reject) => { 151 | timeoutid = setTimeout(() => { 152 | events.onTimeout() 153 | return reject( 154 | `${method} URL: ${options.url} exceeds the timeout ${timeout} ms` 155 | ) 156 | }, timeout) 157 | }) 158 | : null 159 | 160 | return ( 161 | timer 162 | ? Promise.race([timer, worker]).then((res) => { 163 | clearTimeout(timeoutid) 164 | return res 165 | }) 166 | : worker 167 | ).then((resp) => events.onResponse(resp)) 168 | } 169 | 170 | const http = {} 171 | methods.forEach( 172 | (method) => 173 | (http[method.toLowerCase()] = (options) => send(method, options)) 174 | ) 175 | return http 176 | } 177 | 178 | function API(name = 'untitled', debug = false) { 179 | const { isQX, isLoon, isSurge, isNode, isJSBox, isScriptable } = ENV() 180 | return new (class { 181 | constructor(name, debug) { 182 | this.name = name 183 | this.debug = debug 184 | 185 | this.http = HTTP() 186 | this.env = ENV() 187 | 188 | this.node = (() => { 189 | if (isNode) { 190 | const fs = require('fs') 191 | 192 | return { 193 | fs, 194 | } 195 | } else { 196 | return null 197 | } 198 | })() 199 | this.initCache() 200 | 201 | const delay = (t, v) => 202 | new Promise(function (resolve) { 203 | setTimeout(resolve.bind(null, v), t) 204 | }) 205 | 206 | Promise.prototype.delay = function (t) { 207 | return this.then(function (v) { 208 | return delay(t, v) 209 | }) 210 | } 211 | } 212 | 213 | // persistance 214 | 215 | // initialize cache 216 | initCache() { 217 | if (isQX) this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}') 218 | if (isLoon || isSurge) 219 | this.cache = JSON.parse($persistentStore.read(this.name) || '{}') 220 | 221 | if (isNode) { 222 | // create a json for root cache 223 | let fpath = 'root.json' 224 | if (!this.node.fs.existsSync(fpath)) { 225 | this.node.fs.writeFileSync( 226 | fpath, 227 | JSON.stringify({}), 228 | { flag: 'wx' }, 229 | (err) => console.log(err) 230 | ) 231 | } 232 | this.root = {} 233 | 234 | // create a json file with the given name if not exists 235 | fpath = `${this.name}.json` 236 | if (!this.node.fs.existsSync(fpath)) { 237 | this.node.fs.writeFileSync( 238 | fpath, 239 | JSON.stringify({}), 240 | { flag: 'wx' }, 241 | (err) => console.log(err) 242 | ) 243 | this.cache = {} 244 | } else { 245 | this.cache = JSON.parse( 246 | this.node.fs.readFileSync(`${this.name}.json`) 247 | ) 248 | } 249 | } 250 | } 251 | 252 | // store cache 253 | persistCache() { 254 | const data = JSON.stringify(this.cache) 255 | if (isQX) $prefs.setValueForKey(data, this.name) 256 | if (isLoon || isSurge) $persistentStore.write(data, this.name) 257 | if (isNode) { 258 | this.node.fs.writeFileSync( 259 | `${this.name}.json`, 260 | data, 261 | { flag: 'w' }, 262 | (err) => console.log(err) 263 | ) 264 | this.node.fs.writeFileSync( 265 | 'root.json', 266 | JSON.stringify(this.root), 267 | { flag: 'w' }, 268 | (err) => console.log(err) 269 | ) 270 | } 271 | } 272 | 273 | write(data, key) { 274 | this.log(`SET ${key}`) 275 | if (key.indexOf('#') !== -1) { 276 | key = key.substr(1) 277 | if (isSurge || isLoon) { 278 | return $persistentStore.write(data, key) 279 | } 280 | if (isQX) { 281 | return $prefs.setValueForKey(data, key) 282 | } 283 | if (isNode) { 284 | this.root[key] = data 285 | } 286 | } else { 287 | this.cache[key] = data 288 | } 289 | this.persistCache() 290 | } 291 | 292 | read(key) { 293 | this.log(`READ ${key}`) 294 | if (key.indexOf('#') !== -1) { 295 | key = key.substr(1) 296 | if (isSurge || isLoon) { 297 | return $persistentStore.read(key) 298 | } 299 | if (isQX) { 300 | return $prefs.valueForKey(key) 301 | } 302 | if (isNode) { 303 | return this.root[key] 304 | } 305 | } else { 306 | return this.cache[key] 307 | } 308 | } 309 | 310 | delete(key) { 311 | this.log(`DELETE ${key}`) 312 | if (key.indexOf('#') !== -1) { 313 | key = key.substr(1) 314 | if (isSurge || isLoon) { 315 | return $persistentStore.write(null, key) 316 | } 317 | if (isQX) { 318 | return $prefs.removeValueForKey(key) 319 | } 320 | if (isNode) { 321 | delete this.root[key] 322 | } 323 | } else { 324 | delete this.cache[key] 325 | } 326 | this.persistCache() 327 | } 328 | 329 | // notification 330 | notify(title, subtitle = '', content = '', options = {}) { 331 | const openURL = options['open-url'] 332 | const mediaURL = options['media-url'] 333 | 334 | if (isQX) $notify(title, subtitle, content, options) 335 | if (isSurge) { 336 | $notification.post( 337 | title, 338 | subtitle, 339 | content + `${mediaURL ? '\n多媒体:' + mediaURL : ''}`, 340 | { 341 | url: openURL, 342 | } 343 | ) 344 | } 345 | if (isLoon) { 346 | let opts = {} 347 | if (openURL) opts['openUrl'] = openURL 348 | if (mediaURL) opts['mediaUrl'] = mediaURL 349 | if (JSON.stringify(opts) == '{}') { 350 | $notification.post(title, subtitle, content) 351 | } else { 352 | $notification.post(title, subtitle, content, opts) 353 | } 354 | } 355 | if (isNode || isScriptable) { 356 | const content_ = 357 | content + 358 | (openURL ? `\n点击跳转: ${openURL}` : '') + 359 | (mediaURL ? `\n多媒体: ${mediaURL}` : '') 360 | if (isJSBox) { 361 | const push = require('push') 362 | push.schedule({ 363 | title: title, 364 | body: (subtitle ? subtitle + '\n' : '') + content_, 365 | }) 366 | } else { 367 | console.log(`${title}\n${subtitle}\n${content_}\n\n`) 368 | } 369 | } 370 | } 371 | 372 | // other helper functions 373 | log(msg) { 374 | if (this.debug) console.log(msg) 375 | } 376 | 377 | info(msg) { 378 | console.log(msg) 379 | } 380 | 381 | error(msg) { 382 | console.log('ERROR: ' + msg) 383 | } 384 | 385 | wait(millisec) { 386 | return new Promise((resolve) => setTimeout(resolve, millisec)) 387 | } 388 | 389 | done(value = {}) { 390 | if (isQX || isLoon || isSurge) { 391 | $done(value) 392 | } else if (isNode && !isJSBox) { 393 | if (typeof $context !== 'undefined') { 394 | $context.headers = value.headers 395 | $context.statusCode = value.statusCode 396 | $context.body = value.body 397 | } 398 | } 399 | } 400 | })(name, debug) 401 | } 402 | -------------------------------------------------------------------------------- /lowking.boxjs.removed.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "lowking.app.sub", 3 | "name": "lowking应用订阅", 4 | "author": "@lowking", 5 | "icon": "https://avatars0.githubusercontent.com/u/33308659?s=460&u=9223074586cb14f2ae7ee0953515f8781ded8be9&v=4", 6 | "repo": "https://github.com/lowking/Scripts/", 7 | "apps": [ 8 | { 9 | "id": "lk.weiboST", 10 | "name": "微博超话", 11 | "desc_html": "⚠️使用说明
详情【点我查看】", 12 | "keys": [ 13 | "lkWeiboSTSignHeaderKey", 14 | "lkUserFollowSTKey" 15 | ], 16 | "settings": [ 17 | { 18 | "id": "lkIsEnableLogWeiboSTSign", 19 | "name": "开启/关闭日志(签到脚本)", 20 | "val": true, 21 | "type": "boolean", 22 | "desc": "默认开启" 23 | }, 24 | { 25 | "id": "lkNotifyOnlyFailWeiboSTSign", 26 | "name": "只当执行失败才通知", 27 | "val": false, 28 | "type": "boolean", 29 | "desc": "默认关闭" 30 | }, 31 | { 32 | "id": "lkIsEnableLogWeiboSTCookie", 33 | "name": "开启/关闭日志(获取Cookie脚本)", 34 | "val": true, 35 | "type": "boolean", 36 | "desc": "默认开启" 37 | }, 38 | { 39 | "id": "lkIsClearCookie", 40 | "name": "开启/关闭删除Cookie", 41 | "val": false, 42 | "type": "boolean", 43 | "desc": "默认关闭" 44 | }, 45 | { 46 | "id": "lkIsEnableGetCookieWeiboST", 47 | "name": "开启/关闭获取Cookie", 48 | "val": true, 49 | "type": "boolean", 50 | "desc": "默认开启" 51 | }, 52 | { 53 | "id": "lkIsEnableTgNotifyWeiboSTSign", 54 | "name": "开启/关闭Telegram通知", 55 | "val": false, 56 | "type": "boolean", 57 | "desc": "默认关闭" 58 | }, 59 | { 60 | "id": "lkTgNotifyUrlWeiboSTSign", 61 | "name": "Telegram通知地址", 62 | "val": "", 63 | "type": "text", 64 | "desc": "Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text=" 65 | } 66 | ], 67 | "author": "@lowking", 68 | "repo": "https://github.com/lowking/Scripts/blob/master/weibo/weiboST.js", 69 | "script": "https://github.com/lowking/Scripts/blob/master/weibo/weiboST.js?raw=true", 70 | "icons": [ 71 | "https://raw.githubusercontent.com/Orz-3/mini/master/Alpha/weibo.png", 72 | "https://raw.githubusercontent.com/Orz-3/mini/master/Color/weibo.png" 73 | ] 74 | }, 75 | { 76 | "id": "lkDouyuYubaSign", 77 | "name": "斗鱼鱼吧签到", 78 | "desc_html": "⚠️使用说明
详情【点我查看】", 79 | "icons": [ 80 | "https://raw.githubusercontent.com/Orz-3/mini/master/Alpha/douyu.png", 81 | "https://raw.githubusercontent.com/Orz-3/mini/master/Color/douyu.png" 82 | ], 83 | "keys": [ 84 | "CookieDY", 85 | "GroupDY" 86 | ], 87 | "settings": [ 88 | { 89 | "id": "lkIsEnableLogDouyuYubaSign", 90 | "name": "开启/关闭日志", 91 | "val": true, 92 | "type": "boolean", 93 | "desc": "默认开启" 94 | }, 95 | { 96 | "id": "lkNotifyOnlyFailDouyuYubaSign", 97 | "name": "只当执行失败才通知", 98 | "val": false, 99 | "type": "boolean", 100 | "desc": "默认关闭" 101 | }, 102 | { 103 | "id": "lkIsEnableTgNotifyDouyuYubaSign", 104 | "name": "开启/关闭Telegram通知", 105 | "val": false, 106 | "type": "boolean", 107 | "desc": "默认关闭" 108 | }, 109 | { 110 | "id": "lkTgNotifyUrlDouyuYubaSign", 111 | "name": "Telegram通知地址", 112 | "val": "", 113 | "type": "text", 114 | "desc": "Tg的通知地址,如:https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text=" 115 | } 116 | ], 117 | "author": "@demo2099, @lowking", 118 | "repo": "https://github.com/lowking/Scripts", 119 | "script": "https://github.com/lowking/Scripts/blob/master/douyu/yubaSign.js?raw=true" 120 | } 121 | ] 122 | } 123 | -------------------------------------------------------------------------------- /others.boxjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "others.app.sub", 3 | "name": "其他各种脚本", 4 | "author": "@others", 5 | "icon": "https://avatars0.githubusercontent.com/u/33308659?s=460&u=9223074586cb14f2ae7ee0953515f8781ded8be9&v=4", 6 | "repo": "https://github.com/lowking/Scripts/", 7 | "apps": [ 8 | { 9 | "id": "others.lxk0301", 10 | "name": "lxk0301的脚本", 11 | "keys": [], 12 | "author": "@lxk0301", 13 | "repo": "https://github.com/lxk0301/scripts", 14 | "script": "https://raw.githubusercontent.com/lxk0301/jd_scripts/master/jd_bean_change.js", 15 | "icons": [ 16 | "https://avatars2.githubusercontent.com/u/21308593?s=460&u=c679f6e010b6c33ede1ae099c33a3a1152555630&v=4", 17 | "https://avatars2.githubusercontent.com/u/21308593?s=460&u=c679f6e010b6c33ede1ae099c33a3a1152555630&v=4" 18 | ], 19 | "scripts": [{ 20 | "name": "京东星推官", 21 | "script": "https://raw.githubusercontent.com/lxk0301/jd_scripts/master/jd_xtg.js" 22 | }, { 23 | "name": "京豆变动", 24 | "script": "https://raw.githubusercontent.com/lxk0301/jd_scripts/master/jd_bean_change.js" 25 | }] 26 | }, 27 | { 28 | "id": "others.pemgym", 29 | "name": "EPIC限免", 30 | "keys": [], 31 | "author": "@Peng-YM", 32 | "repo": "https://github.com/Peng-YM/QuanX", 33 | "script": "https://raw.githubusercontent.com/Peng-YM/QuanX/master/Tasks/epic.js", 34 | "icons": [ 35 | "https://raw.githubusercontent.com/Orz-3/mini/master/Alpha/epic.png", 36 | "https://raw.githubusercontent.com/Orz-3/mini/master/Color/epic.png" 37 | ] 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /self/MiyousheCustom.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=MiyousheCustom 2 | #!desc=该模块自用,自定义米游社App 3 | 4 | [Script] 5 | 米游社我的自定义 = requires-body=1,type=http-response,pattern=https:\/\/api-takumi-record.mihoyo.com\/game_record\/card\/api\/getGameRecordCard,script-path=miyousheCustom.js 6 | 米游社首页自定义 = requires-body=1,type=http-response,pattern=https:\/\/bbs-api.miyoushe.com\/apihub\/api\/home\/new,script-path=miyousheCustom.js 7 | 米游社首页tab自定义 = requires-body=1,type=http-response,pattern=https:\/\/bbs-api.miyoushe.com\/forum\/api\/getDiscussionByGame\?gids=8,script-path=miyousheCustom.js 8 | 米游社兑换中心过滤 = requires-body=1,type=http-response,pattern=https:\/\/bbs-api.miyoushe.com\/common\/homushop\/v1\/web\/goods\/list,script-path=miyousheCustom.js 9 | 米游社绝区零咖啡馆置顶过滤 = requires-body=1,type=http-response,pattern=https:\/\/bbs-api.miyoushe.com\/apihub\/api\/forumMain,script-path=miyousheCustom.js 10 | 11 | [MITM] 12 | hostname = %APPEND% api-takumi-record.mihoyo.com, bbs-api.miyoushe.com 13 | -------------------------------------------------------------------------------- /self/ZzzCapture.sgmodule: -------------------------------------------------------------------------------- 1 | #!name=ZzzCapture 2 | #!desc=该模块自用,获取绝区零抽卡数据 3 | 4 | [Script] 5 | ZzzCapture = type=http-request,pattern=^https?:\/\/public-operation-nap\.mihoyo\.com\/common\/gacha_record\/api\/getGachaLog\?,script-path=zzzCapture.js 6 | 7 | [MITM] 8 | hostname = %APPEND% public-operation-nap.mihoyo.com 9 | -------------------------------------------------------------------------------- /self/add2Favorite.scpt: -------------------------------------------------------------------------------- 1 | property FavoritePlaylist: "好听" 2 | tell application "Music" 3 | set thePlaylist to user playlist FavoritePlaylist 4 | 5 | set trackName to name of current track 6 | set artistName to artist of current track 7 | set dbid to database ID of current track 8 | set trackRating to rating of current track 9 | set trackPlayedCount to played count of current track 10 | try 11 | duplicate current track to source "Library" -- Add track from Apple Music to local library 12 | end try 13 | 14 | delay 4 -- Allow time for track to be registered with the local Library 15 | 16 | (* Copy track to playlist *) 17 | set foundTracks to (every track of library playlist 1 whose artist is artistName and name is trackName) 18 | repeat with theTrack in foundTracks 19 | -- setting rating 20 | if trackRating < 80 21 | set rating of theTrack to 80 22 | else if trackPlayedCount > 40 -- played times > 40, give 5 stars 23 | set rating of theTrack to 100 24 | end if 25 | if exists (some track of playlist FavoritePlaylist whose database ID is dbid) then return 26 | duplicate theTrack to thePlaylist 27 | end repeat 28 | end tell -------------------------------------------------------------------------------- /self/fadeVolume.scpt: -------------------------------------------------------------------------------- 1 | -- global fadeDuration 2 | global fadeStepDuration 3 | set fadeDuration to 1 -- in seconds 4 | set fadeStepDuration to 0.02 -- in seconds, Apple says this should be > 0.01667 5 | set min to 20 6 | set max to 60 7 | 8 | tell application "Music" to set musicState to (player state as text) 9 | tell application "Music" to set currentVolume to sound volume 10 | 11 | if musicState is equal to "playing" then 12 | if currentVolume ≠ min and currentVolume ≠ max then 13 | tell application "Music" 14 | pause 15 | delay fadeDuration+0.1 16 | set sound volume to 0 17 | end tell 18 | else if currentVolume > min then 19 | fade(min, fadeDuration, fadeStepDuration) 20 | else if currentVolume < max then 21 | fade(max, fadeDuration, fadeStepDuration) 22 | end if 23 | else 24 | tell application "Music" to play 25 | fade(max, fadeDuration, fadeStepDuration) 26 | end if 27 | 28 | on fade(target, fadeDuration, step) 29 | tell application "Music" 30 | set currentVolume to sound volume 31 | set fadeStepCount to fadeDuration / step 32 | set fadeStepSize to (currentVolume - target) / fadeStepCount 33 | set isNegative to false 34 | if fadeStepSize < 0 then 35 | set isNegative to true 36 | set fadeStepSize to fadeStepSize * -1 37 | end if 38 | 39 | repeat fadeStepCount times 40 | if isNegative then 41 | set currentVolume to currentVolume + fadeStepSize 42 | else 43 | set currentVolume to currentVolume - fadeStepSize 44 | end if 45 | set sound volume to currentVolume 46 | delay fadeStepDuration 47 | end repeat 48 | end tell 49 | end fade -------------------------------------------------------------------------------- /self/removeTrackFromAllPlaylist.scpt: -------------------------------------------------------------------------------- 1 | property FavoritePlaylist: "腻了的" 2 | tell application "Music" 3 | set thePlaylist to user playlist FavoritePlaylist 4 | set trackName to name of current track 5 | set artistName to artist of current track 6 | set dbid to database ID of current track 7 | repeat with aPlaylist in (get every playlist) 8 | set playlistName to name of aPlaylist 9 | repeat 1 times -- 这个repeat为了下面的exit repeat,实现循环的continue 10 | if {"资料库", "音乐", "音源问题", "33号远征队", "泪目", "腻了的"} contains playlistName then -- list中的资料库,音乐,音源问题(这个是我自己用来记录的)跳过 11 | exit repeat 12 | end if 13 | set foundTracks to (every track of aPlaylist whose artist is artistName and name is trackName) 14 | repeat with theTrack in foundTracks 15 | log trackName & " " & playlistName 16 | next track 17 | if not exists (some track of playlist FavoritePlaylist whose database ID is dbid) then duplicate theTrack to thePlaylist 18 | tell thePlaylist to delete contents of theTrack 19 | end repeat 20 | end repeat 21 | end repeat 22 | end tell -------------------------------------------------------------------------------- /util/README.MD: -------------------------------------------------------------------------------- 1 | # 说明 2 | - ToolKit演示 3 | 4 | 发送指定到手机之后 ➟ 5 | 6 | - ```ugtoolkit```脚本用于批量替换项目下js中文中所有引用的ToolKit,避免更新一次ToolKit手动多次修改。 7 | - 使用方法(本人mac环境,以下只针对mac环境): 8 | > - 把```ugtoolkit```和```replaceAllJs.py```复制到当前用户的bin目录下 9 | > - 注意修改```ugtoolkit```中第一行的```targetPath```为项目根目录 10 | > - 在bin目录执行```chmod 755 ugtoolkit``` 11 | > - 之后有调整ToolKit代码,只要在终端执行```ugtoolkit```即可完成代码min以及替换js中的引用 12 | - 截图 13 | ![Demo](https://github.com/lowking/Scripts/blob/master/doc/pic/ugtoolkit.jpg) 14 | -------------------------------------------------------------------------------- /util/Ssid.js: -------------------------------------------------------------------------------- 1 | const SSID = $network.wifi.ssid; 2 | const proxywifi = !$persistentStore.read("lkWifiSsids")?["ssid填这里!!!","这是第二个ssid"]:JSON.parse($persistentStore.read("lkWifiSsids")); 3 | let res = proxywifi.some(val => val === (!!SSID ? SSID : "cellular")); 4 | let lkWifiLast = !$persistentStore.read("lkWifiLast")?"abcdefghijklmnopqrstuvwxyz":$persistentStore.read("lkWifiLast"); 5 | let preMode = !$persistentStore.read("lkWifiPreMode")?"abcdefghijklmnopqrstuvwxyz":$persistentStore.read("lkWifiPreMode"); 6 | if (lkWifiLast!=(!!SSID ? SSID : "cellular")){ 7 | !$persistentStore.write((!!SSID ? SSID : "cellular"), "lkWifiLast") 8 | if (res) { 9 | $surge.setOutboundMode("direct"); 10 | notify("1"); 11 | } else { 12 | $surge.setOutboundMode("rule"); 13 | notify("0"); 14 | } 15 | } 16 | 17 | function notify(mode) { 18 | setTimeout(function () { 19 | if (preMode != mode) { 20 | (mode === "1") ? $notification.post("Wi-Fi助手", "切换到【直接连接】", `${!!SSID ? "你的Wi-Fi:【" + SSID + "】" : "你正在使用流量"}`) : $notification.post("Wi-Fi助手", "切换到【规则模式】", `${!!SSID ? "你的Wi-Fi:【" + SSID + "】" : "你正在使用流量"}`) 21 | !$persistentStore.write(mode, "lkWifiPreMode") 22 | } 23 | }) 24 | } 25 | 26 | $done(); -------------------------------------------------------------------------------- /util/ToolKit.min.js: -------------------------------------------------------------------------------- 1 | function ToolKit(scriptName,scriptId,options){class Request{constructor(tk){this.tk=tk}fetch(options,method="GET"){options=typeof options=="string"?{url:options}:options;let fetcher;switch(method){case"PUT":fetcher=this.put;break;case"POST":fetcher=this.post;break;default:fetcher=this.get}const doFetch=new Promise((resolve,reject)=>{fetcher.call(this,options,(error,resp,data)=>error?reject({error,resp,data}):resolve({error,resp,data}))}),delayFetch=(promise,timeout=5e3)=>Promise.race([promise,new Promise((_,reject)=>setTimeout(()=>reject(new Error("请求超时")),timeout))]);return options.timeout>0?delayFetch(doFetch,options.timeout):doFetch}async get(options){return this.fetch.call(this.tk,options)}async post(options){return this.fetch.call(this.tk,options,"POST")}async put(options){return this.fetch.call(this.tk,options,"PUT")}}return new class{constructor(scriptName,scriptId,options){Object.prototype.s=function(replacer,space){return typeof this=="string"?this:JSON.stringify(this,replacer,space)},Object.prototype.o=function(reviver){return JSON.parse(this,reviver)},Object.prototype.getIgnoreCase=function(key){if(!key)throw"Key required";let target=this;try{typeof this=="string"&&(target=JSON.stringify(this))}catch{throw"It's not a JSON object or string!"}const ret=Object.keys(target).reduce((obj,key)=>(obj[key.toLowerCase()]=target[key],obj),{});return ret[key]},this.userAgent=`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`,this.a=`lk`,this.name=scriptName,this.id=scriptId,this.req=new Request(this),this.data=null,this.b=this.fb(`${this.a}${this.id}.dat`),this.c=this.fb(`${this.a}${this.id}.boxjs.json`),this.d=options,this.isExecComm=!1,this.f=this.getVal(`${this.a}IsEnableLog${this.id}`),this.f=!!this.isEmpty(this.f)||this.f.o(),this.g=this.getVal(`${this.a}NotifyOnlyFail${this.id}`),this.g=!this.isEmpty(this.g)&&this.g.o(),this.h=this.getVal(`${this.a}IsEnableTgNotify${this.id}`),this.h=!this.isEmpty(this.h)&&this.h.o(),this.i=this.getVal(`${this.a}TgNotifyUrl${this.id}`),this.h=this.h?!this.isEmpty(this.i):this.h,this.j=`${this.a}CostTotalString${this.id}`,this.k=this.getVal(this.j),this.k=this.isEmpty(this.k)?`0,0`:this.k.replace('"',""),this.l=this.k.split(",")[0],this.m=this.k.split(",")[1],this.n=0,this.o=` 2 | ██`,this.p=" ",this.now=new Date,this.q=this.now.getTime(),this.node=(()=>{if(this.isNode()){const request=require("request");return{request}}return null})(),this.r=!0,this.s=[],this.t="chavy_boxjs_cur__acs",this.u="chavy_boxjs__acs",this.v={"|`|":",backQuote,"},this.w={",backQuote,":"`","%2CbackQuote%2C":"`"},this.y={"_":"\\_","*":"\\*","`":"\\`"},this.x={"_":"\\_","*":"\\*","[":"\\[","]":"\\]","(":"\\(",")":"\\)","~":"\\~","`":"\\`",">":"\\>","#":"\\#","+":"\\+","-":"\\-","=":"\\=","|":"\\|","{":"\\{","}":"\\}",".":"\\.","!":"\\!"},this.log(`${this.name}, 开始执行!`),this.fd()}fb(_a){if(!this.isNode())return _a;let _b=process.argv.slice(1,2)[0].split("/");return _b[_b.length-1]=_a,_b.join("/")}fc(_a){const _c=this.path.resolve(_a),_d=this.path.resolve(process.cwd(),_a),_e=this.fs.existsSync(_c),_f=!_e&&this.fs.existsSync(_d);return{_c,_d,_e,_f}}async fd(){if(!this.isNode())return;if(this.e=process.argv.slice(1),this.e[1]!="p")return;this.isExecComm=!0,this.log(`开始执行指令【${this.e[1]}】=> 发送到其他终端测试脚本!`);let httpApi=this.d?.httpApi,_h;if(this.isEmpty(this?.d?.httpApi))this.log(`未设置options,使用默认值`),this.isEmpty(this?.d)&&(this.d={}),this.d.httpApi=`ffff@10.0.0.6:6166`,httpApi=this.d.httpApi,_h=httpApi.split("@")[1];else{if(typeof httpApi=="object")if(_h=this.isNumeric(this.e[2])?this.e[3]||"unknown":this.e[2],httpApi[_h])httpApi=httpApi[_h];else{const keys=Object.keys(httpApi);keys[0]?(_h=keys[0],httpApi=httpApi[keys[0]]):httpApi="error"}if(!/.*?@.*?:[0-9]+/.test(httpApi)){this.log(`❌httpApi格式错误!格式: ffff@3.3.3.18:6166`),this.done();return}}this.fe(this.e[2],_h,httpApi)}fe(timeout,_h,httpApi){let _i=this.e[0];const[_j,_k]=httpApi.split("@");this.log(`获取【${_i}】内容传给【${_h||_k}】`),this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(_i);if(!_e&&!_f){lk.done();return}const _m=_e?_c:_d;let options={url:`http://${_k}/v1/scripting/evaluate`,headers:{"X-Key":_j},body:{script_text:new String(this.fs.readFileSync(_m)),mock_type:"cron",timeout:!this.isEmpty(timeout)&&timeout>5?timeout:5},json:!0};this.req.post(options).then(({error,resp,data})=>{this.log(`已将脚本【${_i}】发给【${_h||_k}】,执行结果: 3 | ${this.p}error: ${error} 4 | ${this.p}resp: ${resp?.s()} 5 | ${this.p}data: ${this.fj(data)}`),this.done()})}boxJsJsonBuilder(info,param){if(!this.isNode())return;if(!this.isJsonObject(info)||!this.isJsonObject(param)){this.log("构建BoxJsJson传入参数格式错误,请传入json对象");return}let _p=param?.targetBoxjsJsonPath||"/Users/lowking/Desktop/Scripts/lowking.boxjs.json";if(!this.fs.existsSync(_p))return;this.log("using node");let _q=["settings","keys"];const _r="https://raw.githubusercontent.com/Orz-3";let boxJsJson={},scritpUrl="#lk{script_url}";if(boxJsJson.id=`${this.a}${this.id}`,boxJsJson.name=this.name,boxJsJson.desc_html=`⚠️使用说明
详情【点我查看】`,boxJsJson.icons=[`${_r}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`,`${_r}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`],boxJsJson.keys=[],boxJsJson.settings=[{id:`${this.a}IsEnableLog${this.id}`,name:"开启/关闭日志",val:!0,type:"boolean",desc:"默认开启"},{id:`${this.a}NotifyOnlyFail${this.id}`,name:"只当执行失败才通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.a}IsEnableTgNotify${this.id}`,name:"开启/关闭Telegram通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.a}TgNotifyUrl${this.id}`,name:"Telegram通知地址",val:"",type:"text",desc:"Tg的通知地址,如: https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="}],boxJsJson.author="#lk{author}",boxJsJson.repo="#lk{repo}",boxJsJson.script=`${scritpUrl}?raw=true`,!this.isEmpty(info))for(let key of _q){if(this.isEmpty(info[key]))break;if(key==="settings")for(let i=0;iapp.id==boxJsJson.id)[0]);targetIdx>=0?boxjsJson.apps[targetIdx]=boxJsJson:boxjsJson.apps.push(boxJsJson);let ret=boxjsJson.s(null,2);if(!this.isEmpty(param))for(const key in param){let val=param[key];if(!val)switch(key){case"author":val="@lowking";break;case"repo":val="https://github.com/lowking/Scripts";break;default:continue}ret=ret.replaceAll(`#lk{${key}}`,val)}const regex=/(?:#lk\{)(.+?)(?=\})/;let m=regex.exec(ret);m!==null&&this.log(`⚠️生成BoxJs还有未配置的参数,请参考: 6 | ${this.p}https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L19 7 | ${this.p}传入参数: `);let _n=new Set;for(;(m=regex.exec(ret))!==null;)_n.add(m[1]),ret=ret.replace(`#lk{${m[1]}}`,``);_n.forEach(p=>console.log(`${this.p}${p}`)),this.fs.writeFileSync(_p,ret)}isJsonObject(obj){return typeof obj=="object"&&Object.prototype.toString.call(obj).toLowerCase()=="[object object]"&&!obj.length}appendNotifyInfo(info,type){type==1?this.s=info:this.s.push(info)}prependNotifyInfo(info){this.s.splice(0,0,info)}execFail(){this.r=!1}isRequest(){return typeof $request!="undefined"}isSurge(){return typeof $httpClient!="undefined"}isQuanX(){return typeof $task!="undefined"}isLoon(){return typeof $loon!="undefined"}isJSBox(){return typeof $app!="undefined"&&typeof $http!="undefined"}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}isNode(){return typeof require=="function"&&!this.isJSBox()}sleep(ms){return this.n+=ms,new Promise(resolve=>setTimeout(resolve,ms))}randomSleep(minMs,maxMs){return this.sleep(this.randomNumber(minMs,maxMs))}randomNumber(min,max){return Math.floor(Math.random()*(max-min+1)+min)}log(message){this.f&&console.log(`${this.o}${message}`)}logErr(message){if(this.r=!0,this.f){let msg="";this.isEmpty(message.error)||(msg=`${msg} 8 | ${this.p}${message.error.s()}`),this.isEmpty(message.message)||(msg=`${msg} 9 | ${this.p}${message.message.s()}`),msg=`${this.o}${this.name}执行异常:${this.p}${msg}`,message&&(msg=`${msg} 10 | ${this.p}${message.s()}`),console.log(msg)}}ff(mapping,message){for(let key in mapping){if(!mapping.hasOwnProperty(key))continue;message=message.replaceAll(key,mapping[key])}return message}msg(subtitle,message,openUrl,mediaUrl,copyText,disappearS){if(!this.isRequest()&&this.g&&this.r)return;if(this.isEmpty(message)&&(Array.isArray(this.s)?message=this.s.join(` 11 | `):message=this.s),this.isEmpty(message))return;if(this.h){this.log(`${this.name}Tg通知开始`);const fa=this.i&&this.i.indexOf("parse_mode=Markdown")!=-1;if(fa){message=this.ff(this.v,message);let _t=this.y;this.i.indexOf("parse_mode=MarkdownV2")!=-1&&(_t=this.x),message=this.ff(_t,message)}message=`📌${this.name} 12 | ${message}`,fa&&(message=this.ff(this.w,message));let u=`${this.i}${encodeURIComponent(message)}`;this.req.get({url:u})}else{let options={};const _u=!this.isEmpty(openUrl),_v=!this.isEmpty(mediaUrl),_w=!this.isEmpty(copyText),_x=disappearS>0;this.isSurge()||this.isLoon()||this.isStash()?(_u&&(options.url=openUrl,options.action="open-url"),_w&&(options.text=copyText,options.action="clipboard"),this.isSurge()&&_x&&(options["auto-dismiss"]=disappearS),_v&&(options["media-url"]=mediaUrl),$notification.post(this.name,subtitle,message,options)):this.isQuanX()?(_u&&(options["open-url"]=openUrl),_v&&(options["media-url"]=mediaUrl),$notify(this.name,subtitle,message,options)):this.isNode()?this.log("⭐️"+this.name+` 13 | `+subtitle+` 14 | `+message):this.isJSBox()&&$push.schedule({title:this.name,body:subtitle?subtitle+` 15 | `+message:message})}}getVal(key,defaultValue){let value;return this.isSurge()||this.isLoon()||this.isStash()?value=$persistentStore.read(key):this.isQuanX()?value=$prefs.valueForKey(key):this.isNode()?(this.data=this.fh(),value=process.env[key]||this.data[key]):value=this.data&&this.data[key]||null,value||defaultValue}fg(key,val){if(key==this.u)return;const _y=`${this.a}${this.id}`;let _z=this.getVal(this.t,"{}").o();if(!_z.hasOwnProperty(_y))return;let curSessionId=_z[_y],_aa=this.getVal(this.u,"[]").o();if(_aa.length==0)return;let _ab=[];if(_aa.forEach(_ac=>{_ac.id==curSessionId&&(_ab=_ac.datas)}),_ab.length==0)return;let _ad=!1;_ab.forEach(kv=>{kv.key==key&&(kv.val=val,_ad=!0)}),_ad||_ab.push({key,val}),_aa.forEach(_ac=>{_ac.id==curSessionId&&(_ac.datas=_ab)}),this.setVal(this.u,_aa.s())}setVal(key,val){return this.isSurge()||this.isLoon()||this.isStash()?(this.fg(key,val),$persistentStore.write(val,key)):this.isQuanX()?(this.fg(key,val),$prefs.setValueForKey(val,key)):this.isNode()?(this.data=this.fh(),this.data[key]=val,this.fi(),!0):this.data&&this.data[key]||null}fh(){if(!this.isNode())return{};this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(this.b);if(_e||_f){const _m=_e?_c:_d;return this.fs.readFileSync(_m).o()}return{}}fi(){if(!this.isNode())return;this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(this.b),_g=this.data.s();_e?this.fs.writeFileSync(_c,_g):_f?this.fs.writeFileSync(_d,_g):this.fs.writeFileSync(_c,_g)}fj(data){const _s=`${this.p}${this.p}`;let ret="";return Object.keys(data).forEach(key=>{let lines=data[key]?.s().split(` 16 | `);key=="output"&&(lines=lines.slice(0,-2)),ret=`${ret} 17 | ${_s}${key}: 18 | ${_s}${this.p}${lines?.join(` 19 | ${_s}${this.p}`)}`}),ret}fk(response){return response&&(response.status=response?.status||response?.statusCode,delete response.statusCode,response)}get(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?$httpClient.get(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="GET",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?this.node.request(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.get(options))}post(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?$httpClient.post(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="POST",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?this.node.request.post(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}put(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?(options.method="PUT",$httpClient.put(options,(error,response,body)=>{callback(error,this.fk(response),body)})):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="PUT",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?(options.method="PUT",this.node.request.put(options,(error,response,body)=>{callback(error,this.fk(response),body)})):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}sum(a,b){let aa=Array.from(a,Number),bb=Array.from(b,Number),ret=[],c=0,i=Math.max(a.length,b.length);for(;i--;)c+=(aa.pop()||0)+(bb.pop()||0),ret.unshift(c%10),c=Math.floor(c/10);for(;c;)ret.unshift(c%10),c=Math.floor(c/10);return ret.join("")}fl(){let info=`${this.name}, 执行完毕!`;this.isNode()&&this.isExecComm&&(info=`指令【${this.e[1]}】执行完毕!`);const endTime=(new Date).getTime(),ms=endTime-this.q,fl=ms/1e3,count=this.sum(this.m,"1"),total=this.sum(this.l,ms.s()),average=(Number(total)/Number(count)/1e3).toFixed(4);info=`${info} 20 | ${this.p}耗时【${fl}】秒(含休眠${this.n?(this.n/1e3).toFixed(4):0}秒)`,info=`${info} 21 | ${this.p}总共执行【${count}】次,平均耗时【${average}】秒`,info=`${info} 22 | ${this.p}ToolKit v1.4.0 build 155.`,this.log(info),this.setVal(this.j,`${total},${count}`.s())}done(value={}){this.fl(),(this.isSurge()||this.isQuanX()||this.isLoon()||this.isStash())&&$done(value)}getRequestUrl(){return $request.url}getResponseBody(){return $response.body}isMatch(reg){return!!($request.method!="OPTIONS"&&this.getRequestUrl().match(reg))}isEmpty(obj){return typeof obj=="undefined"||obj==null||obj.s()=="{}"||obj==""||obj.s()=='""'||obj.s()=="null"||obj.s()=="undefined"||obj.length===0}isNumeric(s){return!isNaN(parseFloat(s))&&isFinite(s)}randomString(len,chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"){len=len||32;let maxPos=chars.length,pwd="";for(let i=0;i {}) 74 | |参数|描述 75 | |:---|:--- 76 | |options|传入get请求体,结构可参考仓库其他脚本 77 | |callback|回调函数 78 | 79 | post(options, callback = () => {}) 80 | |参数|描述 81 | |:---|:--- 82 | |options|传入post请求体,结构可参考仓库其他脚本 83 | |callback|回调函数 84 | 85 | done(value) 86 | |参数|描述 87 | |:---|:--- 88 | |value|传入需要回传给app的数据 89 | 90 | isEmpty(obj) 91 | |参数|描述 92 | |:---|:--- 93 | |obj|传入对象,用于判断是否为空 94 | 95 | randomString(len) 96 | |参数|描述 97 | |:---|:--- 98 | |len|传入数字,用于生成len长度的随机字符串,字符串由字母和数字组成 99 | 100 | autoComplete(str, prefix, suffix, fill, len, direction, ifCode, clen, startIndex, cstr) 101 | |描述 102 | |:--- 103 | |详情查看ToolKit.js源码 104 | 105 | customReplace(str, param, prefix, suffix) 106 | |描述 107 | |:--- 108 | |详情查看ToolKit.js源码 109 | 110 | ### 部分说明 111 | 1.pc发送脚本至ios surge调试 112 | 写法参考[ToolKitDemo](https://github.com/lowking/Scripts/blob/master/util/example/useToolKitDemo.js),之后就可以在脚本所在的目录下执行```node ToolKitDemo.js p```,记得命令尾巴加空格p,如需重新设置脚本超时可以在后面再加入空格数字``` 10```,修改超时时间为10秒 113 | 114 | 2.生成boxjs配置json 115 | 执行```node ToolKitDemo.js```可在当前目录下生成BoxJs配置文件(需要脚本调用boxJsJsonBuilder方法),详细实现可以查看ToolKit中的boxJsJsonBuilder方法 116 | -------------------------------------------------------------------------------- /util/example/ToolKitDemo.js: -------------------------------------------------------------------------------- 1 | const lk = new ToolKit('工具包使用示例', 'ToolKitDemo', {"httpApi": {ipad: "ffff@10.0.0.5:6166", "15pro": "ffff@10.0.0.6:6166"}}) 2 | 3 | const main = async () => { 4 | let test = lk.getVal("test") 5 | lk.msg(``, `这是通知内容:${test}`) 6 | lk.setVal("test", "hello") 7 | test = lk.getVal("test") 8 | lk.msg(``, `这是通知内容:${test}`) 9 | 10 | // 生成boxjs配置.json 11 | lk.boxJsJsonBuilder({ 12 | "icons": [ 13 | "https://raw.githubusercontent.com/Orz-3/mini/master/toolkitdemo.png", 14 | "https://raw.githubusercontent.com/Orz-3/task/master/toolkitdemo.png" 15 | ] 16 | }, { 17 | "author": "@lowking", 18 | // "repo": "https://github.com/lowking/Scripts", 19 | // "script_url": "https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js" 20 | }) 21 | 22 | // 可以点击打开url的通知 23 | lk.msg(``, `可点击跳转`, `https://baidu.com`) 24 | 25 | // 记录通知内容 26 | lk.appendNotifyInfo(`通知1`) 27 | lk.appendNotifyInfo(`通知2`) 28 | 29 | // 插入到通知内容第一行 30 | lk.prependNotifyInfo(`通知0`) 31 | 32 | // 复制文本 33 | lk.msg(``, 34 | `可点击复制`, 35 | 'https://baidu.com', 36 | 'https://raw.githubusercontent.com/chavyleung/scripts/master/box/icons/BoxSetting.png', 37 | '要复制的文本') 38 | 39 | // 通知显示媒体信息,10秒后通知自动消失(仅限surge) 40 | lk.msg(``, 41 | `显示媒体信息5秒后消失`, 42 | '', 43 | 'https://raw.githubusercontent.com/chavyleung/scripts/master/box/icons/BoxSetting.png', 44 | '', 45 | 5) 46 | await lk.req.get(`http://timor.tech/api/holiday/info/${lk.formatDate(lk.now, "yyyy-MM-dd")}`) 47 | .then(({ error, resp, data }) => { 48 | lk.log(`error: ${error}\nresp: ${resp.s()}\ndata: ${data}`) 49 | }) 50 | 51 | // 最后一次性输出 52 | lk.msg(``) 53 | let obj = { 54 | test: "test" 55 | } 56 | lk.log(obj.s(null, 2)) 57 | await lk.randomSleep(1000, 5000) 58 | } 59 | 60 | // 支持surge的通过httpApi直接在手机测试脚本:node ToolKitDemo.js p,即可; 61 | // 如果在命令行下,则根据配置生成boxjs的配置json 62 | if (!lk.isExecComm) main().catch((err) => { 63 | lk.logErr(err) 64 | lk.execFail() 65 | lk.msg(``, err) 66 | }).finally(() => { 67 | lk.done() 68 | }) 69 | 70 | // * ToolKit v1.4.0 build 155 71 | function ToolKit(scriptName,scriptId,options){class Request{constructor(tk){this.tk=tk}fetch(options,method="GET"){options=typeof options=="string"?{url:options}:options;let fetcher;switch(method){case"PUT":fetcher=this.put;break;case"POST":fetcher=this.post;break;default:fetcher=this.get}const doFetch=new Promise((resolve,reject)=>{fetcher.call(this,options,(error,resp,data)=>error?reject({error,resp,data}):resolve({error,resp,data}))}),delayFetch=(promise,timeout=5e3)=>Promise.race([promise,new Promise((_,reject)=>setTimeout(()=>reject(new Error("请求超时")),timeout))]);return options.timeout>0?delayFetch(doFetch,options.timeout):doFetch}async get(options){return this.fetch.call(this.tk,options)}async post(options){return this.fetch.call(this.tk,options,"POST")}async put(options){return this.fetch.call(this.tk,options,"PUT")}}return new class{constructor(scriptName,scriptId,options){Object.prototype.s=function(replacer,space){return typeof this=="string"?this:JSON.stringify(this,replacer,space)},Object.prototype.o=function(reviver){return JSON.parse(this,reviver)},Object.prototype.getIgnoreCase=function(key){if(!key)throw"Key required";let target=this;try{typeof this=="string"&&(target=JSON.stringify(this))}catch{throw"It's not a JSON object or string!"}const ret=Object.keys(target).reduce((obj,key)=>(obj[key.toLowerCase()]=target[key],obj),{});return ret[key]},this.userAgent=`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15`,this.a=`lk`,this.name=scriptName,this.id=scriptId,this.req=new Request(this),this.data=null,this.b=this.fb(`${this.a}${this.id}.dat`),this.c=this.fb(`${this.a}${this.id}.boxjs.json`),this.d=options,this.isExecComm=!1,this.f=this.getVal(`${this.a}IsEnableLog${this.id}`),this.f=!!this.isEmpty(this.f)||this.f.o(),this.g=this.getVal(`${this.a}NotifyOnlyFail${this.id}`),this.g=!this.isEmpty(this.g)&&this.g.o(),this.h=this.getVal(`${this.a}IsEnableTgNotify${this.id}`),this.h=!this.isEmpty(this.h)&&this.h.o(),this.i=this.getVal(`${this.a}TgNotifyUrl${this.id}`),this.h=this.h?!this.isEmpty(this.i):this.h,this.j=`${this.a}CostTotalString${this.id}`,this.k=this.getVal(this.j),this.k=this.isEmpty(this.k)?`0,0`:this.k.replace('"',""),this.l=this.k.split(",")[0],this.m=this.k.split(",")[1],this.n=0,this.o=` 72 | ██`,this.p=" ",this.now=new Date,this.q=this.now.getTime(),this.node=(()=>{if(this.isNode()){const request=require("request");return{request}}return null})(),this.r=!0,this.s=[],this.t="chavy_boxjs_cur__acs",this.u="chavy_boxjs__acs",this.v={"|`|":",backQuote,"},this.w={",backQuote,":"`","%2CbackQuote%2C":"`"},this.y={"_":"\\_","*":"\\*","`":"\\`"},this.x={"_":"\\_","*":"\\*","[":"\\[","]":"\\]","(":"\\(",")":"\\)","~":"\\~","`":"\\`",">":"\\>","#":"\\#","+":"\\+","-":"\\-","=":"\\=","|":"\\|","{":"\\{","}":"\\}",".":"\\.","!":"\\!"},this.log(`${this.name}, 开始执行!`),this.fd()}fb(_a){if(!this.isNode())return _a;let _b=process.argv.slice(1,2)[0].split("/");return _b[_b.length-1]=_a,_b.join("/")}fc(_a){const _c=this.path.resolve(_a),_d=this.path.resolve(process.cwd(),_a),_e=this.fs.existsSync(_c),_f=!_e&&this.fs.existsSync(_d);return{_c,_d,_e,_f}}async fd(){if(!this.isNode())return;if(this.e=process.argv.slice(1),this.e[1]!="p")return;this.isExecComm=!0,this.log(`开始执行指令【${this.e[1]}】=> 发送到其他终端测试脚本!`);let httpApi=this.d?.httpApi,_h;if(this.isEmpty(this?.d?.httpApi))this.log(`未设置options,使用默认值`),this.isEmpty(this?.d)&&(this.d={}),this.d.httpApi=`ffff@10.0.0.6:6166`,httpApi=this.d.httpApi,_h=httpApi.split("@")[1];else{if(typeof httpApi=="object")if(_h=this.isNumeric(this.e[2])?this.e[3]||"unknown":this.e[2],httpApi[_h])httpApi=httpApi[_h];else{const keys=Object.keys(httpApi);keys[0]?(_h=keys[0],httpApi=httpApi[keys[0]]):httpApi="error"}if(!/.*?@.*?:[0-9]+/.test(httpApi)){this.log(`❌httpApi格式错误!格式: ffff@3.3.3.18:6166`),this.done();return}}this.fe(this.e[2],_h,httpApi)}fe(timeout,_h,httpApi){let _i=this.e[0];const[_j,_k]=httpApi.split("@");this.log(`获取【${_i}】内容传给【${_h||_k}】`),this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(_i);if(!_e&&!_f){lk.done();return}const _m=_e?_c:_d;let options={url:`http://${_k}/v1/scripting/evaluate`,headers:{"X-Key":_j},body:{script_text:String(this.fs.readFileSync(_m)),mock_type:"cron",timeout:!this.isEmpty(timeout)&&timeout>5?timeout:5},json:!0};this.req.post(options).then(({error,resp,data})=>{this.log(`已将脚本【${_i}】发给【${_h||_k}】,执行结果: 73 | ${this.p}error: ${error} 74 | ${this.p}resp: ${resp?.s()} 75 | ${this.p}data: ${this.fj(data)}`),this.done()})}boxJsJsonBuilder(info,param){if(!this.isNode())return;if(!this.isJsonObject(info)||!this.isJsonObject(param)){this.log("构建BoxJsJson传入参数格式错误,请传入json对象");return}let _p=param?.targetBoxjsJsonPath||"/Users/lowking/Desktop/Scripts/lowking.boxjs.json";if(!this.fs.existsSync(_p))return;this.log("using node");let _q=["settings","keys"];const _r="https://raw.githubusercontent.com/Orz-3";let boxJsJson={},scritpUrl="#lk{script_url}";if(boxJsJson.id=`${this.a}${this.id}`,boxJsJson.name=this.name,boxJsJson.desc_html=`⚠️使用说明
详情【点我查看】`,boxJsJson.icons=[`${_r}/mini/master/Alpha/${this.id.toLocaleLowerCase()}.png`,`${_r}/mini/master/Color/${this.id.toLocaleLowerCase()}.png`],boxJsJson.keys=[],boxJsJson.settings=[{id:`${this.a}IsEnableLog${this.id}`,name:"开启/关闭日志",val:!0,type:"boolean",desc:"默认开启"},{id:`${this.a}NotifyOnlyFail${this.id}`,name:"只当执行失败才通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.a}IsEnableTgNotify${this.id}`,name:"开启/关闭Telegram通知",val:!1,type:"boolean",desc:"默认关闭"},{id:`${this.a}TgNotifyUrl${this.id}`,name:"Telegram通知地址",val:"",type:"text",desc:"Tg的通知地址,如: https://api.telegram.org/bot-token/sendMessage?chat_id=-100140&parse_mode=Markdown&text="}],boxJsJson.author="#lk{author}",boxJsJson.repo="#lk{repo}",boxJsJson.script=`${scritpUrl}?raw=true`,!this.isEmpty(info))for(let key of _q){if(this.isEmpty(info[key]))break;if(key==="settings")for(let i=0;iapp.id==boxJsJson.id)[0]);targetIdx>=0?boxjsJson.apps[targetIdx]=boxJsJson:boxjsJson.apps.push(boxJsJson);let ret=boxjsJson.s(null,2);if(!this.isEmpty(param))for(const key in param){let val=param[key];if(!val)switch(key){case"author":val="@lowking";break;case"repo":val="https://github.com/lowking/Scripts";break;default:continue}ret=ret.replaceAll(`#lk{${key}}`,val)}const regex=/(?:#lk\{)(.+?)(?=\})/;let m=regex.exec(ret);m!==null&&this.log(`⚠️生成BoxJs还有未配置的参数,请参考: 76 | ${this.p}https://github.com/lowking/Scripts/blob/master/util/example/ToolKitDemo.js#L17-L19 77 | ${this.p}传入参数: `);let _n=new Set;for(;(m=regex.exec(ret))!==null;)_n.add(m[1]),ret=ret.replace(`#lk{${m[1]}}`,``);_n.forEach(p=>console.log(`${this.p}${p}`)),this.fs.writeFileSync(_p,ret)}isJsonObject(obj){return typeof obj=="object"&&Object.prototype.toString.call(obj).toLowerCase()=="[object object]"&&!obj.length}appendNotifyInfo(info,type){type==1?this.s=info:this.s.push(info)}prependNotifyInfo(info){this.s.splice(0,0,info)}execFail(){this.r=!1}isRequest(){return typeof $request!="undefined"}isSurge(){return typeof $httpClient!="undefined"}isQuanX(){return typeof $task!="undefined"}isLoon(){return typeof $loon!="undefined"}isJSBox(){return typeof $app!="undefined"&&typeof $http!="undefined"}isStash(){return"undefined"!=typeof $environment&&$environment["stash-version"]}isNode(){return typeof require=="function"&&!this.isJSBox()}sleep(ms){return this.n+=ms,new Promise(resolve=>setTimeout(resolve,ms))}randomSleep(minMs,maxMs){return this.sleep(this.randomNumber(minMs,maxMs))}randomNumber(min,max){return Math.floor(Math.random()*(max-min+1)+min)}log(message){this.f&&console.log(`${this.o}${message}`)}logErr(message){if(this.r=!0,this.f){let msg="";this.isEmpty(message.error)||(msg=`${msg} 78 | ${this.p}${message.error.s()}`),this.isEmpty(message.message)||(msg=`${msg} 79 | ${this.p}${message.message.s()}`),msg=`${this.o}${this.name}执行异常:${this.p}${msg}`,message&&(msg=`${msg} 80 | ${this.p}${message.s()}`),console.log(msg)}}ff(mapping,message){for(let key in mapping){if(!mapping.hasOwnProperty(key))continue;message=message.replaceAll(key,mapping[key])}return message}msg(subtitle,message,openUrl,mediaUrl,copyText,disappearS){if(!this.isRequest()&&this.g&&this.r)return;if(this.isEmpty(message)&&(Array.isArray(this.s)?message=this.s.join(` 81 | `):message=this.s),this.isEmpty(message))return;if(this.h){this.log(`${this.name}Tg通知开始`);const fa=this.i&&this.i.indexOf("parse_mode=Markdown")!=-1;if(fa){message=this.ff(this.v,message);let _t=this.y;this.i.indexOf("parse_mode=MarkdownV2")!=-1&&(_t=this.x),message=this.ff(_t,message)}message=`📌${this.name} 82 | ${message}`,fa&&(message=this.ff(this.w,message));let u=`${this.i}${encodeURIComponent(message)}`;this.req.get({url:u})}else{let options={};const _u=!this.isEmpty(openUrl),_v=!this.isEmpty(mediaUrl),_w=!this.isEmpty(copyText),_x=disappearS>0;this.isSurge()||this.isLoon()||this.isStash()?(_u&&(options.url=openUrl,options.action="open-url"),_w&&(options.text=copyText,options.action="clipboard"),this.isSurge()&&_x&&(options["auto-dismiss"]=disappearS),_v&&(options["media-url"]=mediaUrl),$notification.post(this.name,subtitle,message,options)):this.isQuanX()?(_u&&(options["open-url"]=openUrl),_v&&(options["media-url"]=mediaUrl),$notify(this.name,subtitle,message,options)):this.isNode()?this.log("⭐️"+this.name+` 83 | `+subtitle+` 84 | `+message):this.isJSBox()&&$push.schedule({title:this.name,body:subtitle?subtitle+` 85 | `+message:message})}}getVal(key,defaultValue){let value;return this.isSurge()||this.isLoon()||this.isStash()?value=$persistentStore.read(key):this.isQuanX()?value=$prefs.valueForKey(key):this.isNode()?(this.data=this.fh(),value=process.env[key]||this.data[key]):value=this.data&&this.data[key]||null,value||defaultValue}fg(key,val){if(key==this.u)return;const _y=`${this.a}${this.id}`;let _z=this.getVal(this.t,"{}").o();if(!_z.hasOwnProperty(_y))return;let curSessionId=_z[_y],_aa=this.getVal(this.u,"[]").o();if(_aa.length==0)return;let _ab=[];if(_aa.forEach(_ac=>{_ac.id==curSessionId&&(_ab=_ac.datas)}),_ab.length==0)return;let _ad=!1;_ab.forEach(kv=>{kv.key==key&&(kv.val=val,_ad=!0)}),_ad||_ab.push({key,val}),_aa.forEach(_ac=>{_ac.id==curSessionId&&(_ac.datas=_ab)}),this.setVal(this.u,_aa.s())}setVal(key,val){return this.isSurge()||this.isLoon()||this.isStash()?(this.fg(key,val),$persistentStore.write(val,key)):this.isQuanX()?(this.fg(key,val),$prefs.setValueForKey(val,key)):this.isNode()?(this.data=this.fh(),this.data[key]=val,this.fi(),!0):this.data&&this.data[key]||null}fh(){if(!this.isNode())return{};this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(this.b);if(_e||_f){const _m=_e?_c:_d;return this.fs.readFileSync(_m).o()}return{}}fi(){if(!this.isNode())return;this.fs=this.fs?this.fs:require("fs"),this.path=this.path?this.path:require("path");const{_c,_d,_e,_f}=this.fc(this.b),_g=this.data.s();_e?this.fs.writeFileSync(_c,_g):_f?this.fs.writeFileSync(_d,_g):this.fs.writeFileSync(_c,_g)}fj(data){const _s=`${this.p}${this.p}`;let ret="";return Object.keys(data).forEach(key=>{let lines=data[key]?.s().split(` 86 | `);key=="output"&&(lines=lines.slice(0,-2)),ret=`${ret} 87 | ${_s}${key}: 88 | ${_s}${this.p}${lines?.join(` 89 | ${_s}${this.p}`)}`}),ret}fk(response){return response&&(response.status=response?.status||response?.statusCode,delete response.statusCode,response)}get(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?$httpClient.get(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="GET",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?this.node.request(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.get(options))}post(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?$httpClient.post(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="POST",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?this.node.request.post(options,(error,response,body)=>{callback(error,this.fk(response),body)}):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}put(options,callback=()=>{}){this.isSurge()||this.isLoon()||this.isStash()?(options.method="PUT",$httpClient.put(options,(error,response,body)=>{callback(error,this.fk(response),body)})):this.isQuanX()?(typeof options=="string"&&(options={url:options}),options.method="PUT",$task.fetch(options).then(response=>{callback(null,this.fk(response),response.body)},reason=>callback(reason.error,null,null))):this.isNode()?(options.method="PUT",this.node.request.put(options,(error,response,body)=>{callback(error,this.fk(response),body)})):this.isJSBox()&&(typeof options=="string"&&(options={url:options}),options.header=options.headers,options.handler=function(resp){let error=resp.error;error&&(error=resp.error.s());let body=resp.data;typeof body=="object"&&(body=resp.data.s()),callback(error,this.adapterStatus(resp.response),body)},$http.post(options))}sum(a,b){let aa=Array.from(a,Number),bb=Array.from(b,Number),ret=[],c=0,i=Math.max(a.length,b.length);for(;i--;)c+=(aa.pop()||0)+(bb.pop()||0),ret.unshift(c%10),c=Math.floor(c/10);for(;c;)ret.unshift(c%10),c=Math.floor(c/10);return ret.join("")}fl(){let info=`${this.name}, 执行完毕!`;this.isNode()&&this.isExecComm&&(info=`指令【${this.e[1]}】执行完毕!`);const endTime=(new Date).getTime(),ms=endTime-this.q,fl=ms/1e3,count=this.sum(this.m,"1"),total=this.sum(this.l,ms.s()),average=(Number(total)/Number(count)/1e3).toFixed(4);info=`${info} 90 | ${this.p}耗时【${fl}】秒(含休眠${this.n?(this.n/1e3).toFixed(4):0}秒)`,info=`${info} 91 | ${this.p}总共执行【${count}】次,平均耗时【${average}】秒`,info=`${info} 92 | ${this.p}ToolKit v1.4.0 build 155.`,this.log(info),this.setVal(this.j,`${total},${count}`.s())}done(value={}){this.fl(),(this.isSurge()||this.isQuanX()||this.isLoon()||this.isStash())&&$done(value)}getRequestUrl(){return $request.url}getResponseBody(){return $response.body}isMatch(reg){return!!($request.method!="OPTIONS"&&this.getRequestUrl().match(reg))}isEmpty(obj){return typeof obj=="undefined"||obj==null||obj.s()=="{}"||obj==""||obj.s()=='""'||obj.s()=="null"||obj.s()=="undefined"||obj.length===0}isNumeric(s){return!isNaN(parseFloat(s))&&isFinite(s)}randomString(len,chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"){len=len||32;let maxPos=chars.length,pwd="";for(let i=0;i i; ++i) 6 | e += (e << 5) + t.charCodeAt(i); 7 | return 2147483647 & e 8 | } 9 | 10 | function getCSRFToken(skeyz) { 11 | var t = '5381'; 12 | var n = 'tencentQQVIP123443safde&!%^%1282'; 13 | var r = skeyz; 14 | var i = [], 15 | o; 16 | i.push(t << 5); 17 | for (var a = 0, s = r.length; a < s; ++a) { 18 | o = r.charAt(a).charCodeAt(0); 19 | i.push((t << 5) + o); 20 | t = o 21 | } 22 | return md5z(i.join("") + n) 23 | } 24 | 25 | function md5z(e) { 26 | var t = 0; 27 | var n = ""; 28 | var r = 8; 29 | var i = 32; 30 | 31 | function o(e) { 32 | return T(f(x(e), e.length * r)) 33 | } 34 | 35 | function a(e) { 36 | return j(f(x(e), e.length * r)) 37 | } 38 | 39 | function s(e) { 40 | return w(f(x(e), e.length * r)) 41 | } 42 | 43 | function l(e, t) { 44 | return T(y(e, t)) 45 | } 46 | 47 | function u(e, t) { 48 | return j(y(e, t)) 49 | } 50 | 51 | function c(e, t) { 52 | return w(y(e, t)) 53 | } 54 | 55 | function f(e, t) { 56 | e[t >> 5] |= 128 << t % 32; 57 | e[(t + 64 >>> 9 << 4) + 14] = t; 58 | var n = 1732584193; 59 | var r = -271733879; 60 | var o = -1732584194; 61 | var a = 271733878; 62 | for (var s = 0; s < e.length; s += 16) { 63 | var l = n; 64 | var u = r; 65 | var c = o; 66 | var f = a; 67 | n = p(n, r, o, a, e[s + 0], 7, -680876936); 68 | a = p(a, n, r, o, e[s + 1], 12, -389564586); 69 | o = p(o, a, n, r, e[s + 2], 17, 606105819); 70 | r = p(r, o, a, n, e[s + 3], 22, -1044525330); 71 | n = p(n, r, o, a, e[s + 4], 7, -176418897); 72 | a = p(a, n, r, o, e[s + 5], 12, 1200080426); 73 | o = p(o, a, n, r, e[s + 6], 17, -1473231341); 74 | r = p(r, o, a, n, e[s + 7], 22, -45705983); 75 | n = p(n, r, o, a, e[s + 8], 7, 1770035416); 76 | a = p(a, n, r, o, e[s + 9], 12, -1958414417); 77 | o = p(o, a, n, r, e[s + 10], 17, -42063); 78 | r = p(r, o, a, n, e[s + 11], 22, -1990404162); 79 | n = p(n, r, o, a, e[s + 12], 7, 1804603682); 80 | a = p(a, n, r, o, e[s + 13], 12, -40341101); 81 | o = p(o, a, n, r, e[s + 14], 17, -1502002290); 82 | r = p(r, o, a, n, e[s + 15], 22, 1236535329); 83 | n = h(n, r, o, a, e[s + 1], 5, -165796510); 84 | a = h(a, n, r, o, e[s + 6], 9, -1069501632); 85 | o = h(o, a, n, r, e[s + 11], 14, 643717713); 86 | r = h(r, o, a, n, e[s + 0], 20, -373897302); 87 | n = h(n, r, o, a, e[s + 5], 5, -701558691); 88 | a = h(a, n, r, o, e[s + 10], 9, 38016083); 89 | o = h(o, a, n, r, e[s + 15], 14, -660478335); 90 | r = h(r, o, a, n, e[s + 4], 20, -405537848); 91 | n = h(n, r, o, a, e[s + 9], 5, 568446438); 92 | a = h(a, n, r, o, e[s + 14], 9, -1019803690); 93 | o = h(o, a, n, r, e[s + 3], 14, -187363961); 94 | r = h(r, o, a, n, e[s + 8], 20, 1163531501); 95 | n = h(n, r, o, a, e[s + 13], 5, -1444681467); 96 | a = h(a, n, r, o, e[s + 2], 9, -51403784); 97 | o = h(o, a, n, r, e[s + 7], 14, 1735328473); 98 | r = h(r, o, a, n, e[s + 12], 20, -1926607734); 99 | n = g(n, r, o, a, e[s + 5], 4, -378558); 100 | a = g(a, n, r, o, e[s + 8], 11, -2022574463); 101 | o = g(o, a, n, r, e[s + 11], 16, 1839030562); 102 | r = g(r, o, a, n, e[s + 14], 23, -35309556); 103 | n = g(n, r, o, a, e[s + 1], 4, -1530992060); 104 | a = g(a, n, r, o, e[s + 4], 11, 1272893353); 105 | o = g(o, a, n, r, e[s + 7], 16, -155497632); 106 | r = g(r, o, a, n, e[s + 10], 23, -1094730640); 107 | n = g(n, r, o, a, e[s + 13], 4, 681279174); 108 | a = g(a, n, r, o, e[s + 0], 11, -358537222); 109 | o = g(o, a, n, r, e[s + 3], 16, -722521979); 110 | r = g(r, o, a, n, e[s + 6], 23, 76029189); 111 | n = g(n, r, o, a, e[s + 9], 4, -640364487); 112 | a = g(a, n, r, o, e[s + 12], 11, -421815835); 113 | o = g(o, a, n, r, e[s + 15], 16, 530742520); 114 | r = g(r, o, a, n, e[s + 2], 23, -995338651); 115 | n = m(n, r, o, a, e[s + 0], 6, -198630844); 116 | a = m(a, n, r, o, e[s + 7], 10, 1126891415); 117 | o = m(o, a, n, r, e[s + 14], 15, -1416354905); 118 | r = m(r, o, a, n, e[s + 5], 21, -57434055); 119 | n = m(n, r, o, a, e[s + 12], 6, 1700485571); 120 | a = m(a, n, r, o, e[s + 3], 10, -1894986606); 121 | o = m(o, a, n, r, e[s + 10], 15, -1051523); 122 | r = m(r, o, a, n, e[s + 1], 21, -2054922799); 123 | n = m(n, r, o, a, e[s + 8], 6, 1873313359); 124 | a = m(a, n, r, o, e[s + 15], 10, -30611744); 125 | o = m(o, a, n, r, e[s + 6], 15, -1560198380); 126 | r = m(r, o, a, n, e[s + 13], 21, 1309151649); 127 | n = m(n, r, o, a, e[s + 4], 6, -145523070); 128 | a = m(a, n, r, o, e[s + 11], 10, -1120210379); 129 | o = m(o, a, n, r, e[s + 2], 15, 718787259); 130 | r = m(r, o, a, n, e[s + 9], 21, -343485551); 131 | n = v(n, l); 132 | r = v(r, u); 133 | o = v(o, c); 134 | a = v(a, f) 135 | } 136 | if (i == 16) { 137 | return Array(r, o) 138 | } else { 139 | return Array(n, r, o, a) 140 | } 141 | } 142 | 143 | function d(e, t, n, r, i, o) { 144 | return v(b(v(v(t, e), v(r, o)), i), n) 145 | } 146 | 147 | function p(e, t, n, r, i, o, a) { 148 | return d(t & n | ~t & r, e, t, i, o, a) 149 | } 150 | 151 | function h(e, t, n, r, i, o, a) { 152 | return d(t & r | n & ~r, e, t, i, o, a) 153 | } 154 | 155 | function g(e, t, n, r, i, o, a) { 156 | return d(t ^ n ^ r, e, t, i, o, a) 157 | } 158 | 159 | function m(e, t, n, r, i, o, a) { 160 | return d(n ^ (t | ~r), e, t, i, o, a) 161 | } 162 | 163 | function y(e, t) { 164 | var n = x(e); 165 | if (n.length > 16) 166 | n = f(n, e.length * r); 167 | var i = Array(16), 168 | o = Array(16); 169 | for (var a = 0; a < 16; a++) { 170 | i[a] = n[a] ^ 909522486; 171 | o[a] = n[a] ^ 1549556828 172 | } 173 | var s = f(i.concat(x(t)), 512 + t.length * r); 174 | return f(o.concat(s), 512 + 128) 175 | } 176 | 177 | function v(e, t) { 178 | var n = (e & 65535) + (t & 65535); 179 | var r = (e >> 16) + (t >> 16) + (n >> 16); 180 | return r << 16 | n & 65535 181 | } 182 | 183 | function b(e, t) { 184 | return e << t | e >>> 32 - t 185 | } 186 | 187 | function x(e) { 188 | var t = Array(); 189 | var n = (1 << r) - 1; 190 | for (var i = 0; i < e.length * r; i += r) 191 | t[i >> 5] |= (e.charCodeAt(i / r) & n) << i % 32; 192 | return t 193 | } 194 | 195 | function w(e) { 196 | var t = ""; 197 | var n = (1 << r) - 1; 198 | for (var i = 0; i < e.length * 32; i += r) 199 | t += String.fromCharCode(e[i >> 5] >>> i % 32 & n); 200 | return t 201 | } 202 | 203 | function T(e) { 204 | var n = t ? "0123456789ABCDEF" : "0123456789abcdef"; 205 | var r = ""; 206 | for (var i = 0; i < e.length * 4; i++) { 207 | r += n.charAt(e[i >> 2] >> i % 4 * 8 + 4 & 15) + n.charAt(e[i >> 2] >> i % 4 * 8 & 15) 208 | } 209 | return r 210 | } 211 | 212 | function j(e) { 213 | var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 214 | var r = ""; 215 | for (var i = 0; i < e.length * 4; i += 3) { 216 | var o = (e[i >> 2] >> 8 * (i % 4) & 255) << 16 | (e[i + 1 >> 2] >> 8 * ((i + 1) % 4) & 255) << 8 | e[i + 2 >> 2] >> 8 * ((i + 2) % 4) & 255; 217 | for (var a = 0; a < 4; a++) { 218 | if (i * 8 + a * 6 > e.length * 32) 219 | r += n; 220 | else 221 | r += t.charAt(o >> 6 * (3 - a) & 63) 222 | } 223 | } 224 | return r 225 | } 226 | 227 | return o(e) 228 | } -------------------------------------------------------------------------------- /util/qqUtil.min.js: -------------------------------------------------------------------------------- 1 | function getPstk(r){for(var n=5381,t=0,a=r.length;a>t;++t)n+=(n<<5)+r.charCodeAt(t);return 2147483647&n}function getCSRFToken(r){var n="5381";var t="tencentQQVIP123443safde&!%^%1282";var a=r;var e=[],u;e.push(n<<5);for(var o=0,v=a.length;o>5]|=128<>>9<<4)+14]=n;var t=1732584193;var a=-271733879;var u=-1732584194;var o=271733878;for(var v=0;v16)t=h(t,r.length*a);var e=Array(16),u=Array(16);for(var o=0;o<16;o++){e[o]=t[o]^909522486;u[o]=t[o]^1549556828}var v=h(e.concat(k(n)),512+n.length*a);return h(u.concat(v),512+128)}function y(r,n){var t=(r&65535)+(n&65535);var a=(r>>16)+(n>>16)+(t>>16);return a<<16|t&65535}function m(r,n){return r<>>32-n}function k(r){var n=Array();var t=(1<>5]|=(r.charCodeAt(e/a)&t)<>5]>>>e%32&t);return n}function z(r){var t=n?"0123456789ABCDEF":"0123456789abcdef";var a="";for(var e=0;e>2]>>e%4*8+4&15)+t.charAt(r[e>>2]>>e%4*8&15)}return a}function F(r){var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var a="";for(var e=0;e>2]>>8*(e%4)&255)<<16|(r[e+1>>2]>>8*((e+1)%4)&255)<<8|r[e+2>>2]>>8*((e+2)%4)&255;for(var o=0;o<4;o++){if(e*8+o*6>r.length*32)a+=t;else a+=n.charAt(u>>6*(3-o)&63)}}return a}return u(r)} -------------------------------------------------------------------------------- /util/ugtoolkit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | isDev=$1 3 | targetPath="/Users/lowking/Desktop/Scripts" 4 | #获取版本号和buildId 5 | tkVersion=$(grep -n "v\d*\.\d*\.\d*" "${targetPath}/util/ToolKit.js") 6 | buildId=0 7 | buildInfo="" 8 | if [ -n "$tkVersion" ]; then 9 | line=() 10 | IFS=" " read -r -a line <<< "$tkVersion" 11 | tkVersion=${line[2]} 12 | buildId=${line[4]} 13 | buildInfo="${tkVersion} build $(( buildId + 1 ))" 14 | # 更新js中buildId 15 | newVersion="${tkVersion} build $(( buildId + 1 ))" 16 | sed -i "" "s/${tkVersion} build ${buildId}/${newVersion}/g" "${targetPath}/util/ToolKit.js" 17 | fi 18 | # 执行min 19 | if [ "$isDev" == "dev" ]; then 20 | cp -f "${targetPath}/util/ToolKit.js" "${targetPath}/util/ToolKit.min.js" 21 | else 22 | minify --js-keep-var-names -o "${targetPath}/util/ToolKit.min.js" "${targetPath}/util/ToolKit.js" 23 | fi 24 | sed -i "" "s/ToolKit\./ToolKit ${newVersion}\./g" "${targetPath}/util/ToolKit.min.js" 25 | batchReplacement() { 26 | local tarr=$1 27 | local prefix=$2 28 | for var in ${tarr[@]}; 29 | do 30 | val=`echo ${var##*,}` 31 | key=`echo ${var%%,*}` 32 | sed -i "" "s/${prefix}${key}/${prefix}${val}/g" "${targetPath}/util/ToolKit.min.js" 33 | done 34 | } 35 | # 根据配置批量替换 36 | funcArr=( 37 | isMarkdown,fa 38 | getRealPath,fb 39 | checkPath,fc 40 | execComm,fd 41 | callApi,fe 42 | replaceUseMap,ff 43 | updateBoxjsSessions,fg 44 | loadData,fh 45 | writeData,fi 46 | responseDataAdapter,fj 47 | statusAdapter,fk 48 | costTime,fl 49 | ) 50 | if [ "$isDev" != "dev" ]; then 51 | echo "压缩方法名..." 52 | batchReplacement "${funcArr[*]}" 53 | fi 54 | varArr=( 55 | prefix,a 56 | dataFile,b 57 | boxJsJsonFile,c 58 | options,d 59 | comm,e 60 | isEnableLog,f 61 | isNotifyOnlyFail,g 62 | isEnableTgNotify,h 63 | tgNotifyUrl,i 64 | costTotalStringKey,j 65 | costTotalString,k 66 | costTotalMs,l 67 | execCount,m 68 | sleepTotalMs,n 69 | logSeparator,o 70 | twoSpace,p 71 | startTime,q 72 | execStatus,r 73 | notifyInfo,s 74 | boxjsCurSessionKey,t 75 | boxjsSessionsKey,u 76 | preTgEscapeCharMapping,v 77 | finalTgEscapeCharMapping,w 78 | tgEscapeCharMappingV2,x 79 | tgEscapeCharMapping,y 80 | execComm,z 81 | ) 82 | if [ "$isDev" != "dev" ]; then 83 | echo "压缩this.相关变量名..." 84 | batchReplacement "${varArr[*]}" "this\." 85 | batchReplacement "${varArr[*]}" "this?\." 86 | echo "压缩常规变量名..." 87 | fi 88 | parameterArr=( 89 | fileName,_a 90 | targetPath,_b 91 | curDirDataFilePath,_c 92 | rootDirDataFilePath,_d 93 | isCurDirDataFile,_e 94 | isRootDirDataFile,_f 95 | jsondata,_g 96 | targetDevice,_h 97 | fname,_i 98 | xKey,_j 99 | httpApiHost,_k 100 | datPath,_m 101 | loseParamSet,_n 102 | isMarkdown,_o 103 | boxjsJsonPath,_p 104 | needAppendKeys,_q 105 | domain,_r 106 | tabString,_s 107 | targetMapping,_t 108 | hasOpenUrl,_u 109 | hasMediaUrl,_v 110 | hasCopyText,_w 111 | hasAutoDismiss,_x 112 | boxJsId,_y 113 | boxjsCurSession,_z 114 | boxjsSessions,_aa 115 | curSessionDatas,_ab 116 | session,_ac 117 | isExists,_ad 118 | ) 119 | if [ "$isDev" != "dev" ]; then 120 | batchReplacement "${parameterArr[*]}" 121 | fi 122 | # 复制min文件内容 123 | pbcopy < "${targetPath}/util/ToolKit.min.js" 124 | # 从剪辑版复制内容 125 | pasteStr=$(pbpaste -Prefer text) 126 | # 替换到所有脚本// * ToolKit v所在行和下一行的内容 127 | echo $buildInfo 128 | echo "批量替换.js中ToolKit开始" 129 | count=0 130 | while IFS= read -r -d '' i 131 | do 132 | lineno=$(grep -n "// \* ToolKit v" "$i") 133 | if [ -n "$lineno" ]; then 134 | array=() 135 | IFS=":" read -r -a array <<< "$lineno" 136 | lineno=$(( array[0] )) 137 | if [ $lineno -ge 1 ]; then 138 | # 在注释行下新建一行 139 | sed -i '' $lineno"G" "$i" 140 | # 删除注释行 141 | sed -i '' $lineno"d" "$i" 142 | # 在注释行上一行末尾追加新的注释 143 | sed -i '' "$(( lineno - 1)) a\\ 144 | // * ToolKit $tkVersion build $(( buildId + 1 ))" "$i" 145 | # 删除注释行下一行到下100行,也就是文件末尾 146 | sed -i '' $(( lineno + 1 ))","$(( lineno + 9999 ))"d" "$i" 147 | # 在注释行后追加文件的内容 148 | sed -i '' "$(( lineno )) r $targetPath/util/ToolKit.min.js" "$i" 149 | printf "%-20s %s\n" "替换行:$lineno" "$i" 150 | count=$(( count + 1)) 151 | fi 152 | fi 153 | done < <(find $targetPath -name '*.js' -print0 ! -path '*node_modules') 154 | echo "批量替换.js中ToolKit完成。共替换${count}个js" 155 | -------------------------------------------------------------------------------- /util/util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 转自nobyda,修改压缩之后自用 3 | */ 4 | function nobyda() { 5 | const start = Date.now() 6 | const isRequest = typeof $request != "undefined" 7 | const isSurge = typeof $httpClient != "undefined" 8 | const isQuanX = typeof $task != "undefined" 9 | const isLoon = typeof $loon != "undefined" 10 | const isJSBox = typeof $app != "undefined" && typeof $http != "undefined" 11 | const isNode = typeof require == "function" && !isJSBox; 12 | const node = (() => { 13 | if (isNode) { 14 | const request = require('request'); 15 | return ({request}) 16 | } else { 17 | return (null) 18 | } 19 | })() 20 | const getRequestUrl = () => { 21 | if (isQuanX) return $resource.link 22 | if (isSurge) return $request.url 23 | return "" 24 | } 25 | const getResponseBody = () => { 26 | if (isQuanX) return $resource.content 27 | if (isSurge) return $response.body 28 | return "" 29 | } 30 | const msg = (title, subtitle, message) => { 31 | if (isQuanX) $notify(title, subtitle, message) 32 | if (isSurge) $notification.post(title, subtitle, message) 33 | if (isNode) log(title + subtitle + message) 34 | if (isJSBox) $push.schedule({ 35 | title: title, 36 | body: subtitle ? subtitle + "\n" + message : message 37 | }) 38 | } 39 | const setValueForKey = (key, value) => { 40 | if (isQuanX) return $prefs.setValueForKey(value, key) 41 | if (isSurge) return $persistentStore.write(value, key) 42 | } 43 | const getVal = (key) => { 44 | if (isQuanX) return $prefs.valueForKey(key) 45 | if (isSurge) return $persistentStore.read(key) 46 | } 47 | const adapterStatus = (response) => { 48 | if (response) { 49 | if (response.status) { 50 | response["statusCode"] = response.status 51 | } else if (response.statusCode) { 52 | response["status"] = response.statusCode 53 | } 54 | } 55 | return response 56 | } 57 | const get = (options, callback) => { 58 | if (isQuanX) { 59 | if (typeof options == "string") options = { 60 | url: options 61 | } 62 | options["method"] = "GET" 63 | $task.fetch(options).then(response => { 64 | callback(null, adapterStatus(response), response.body) 65 | }, reason => callback(reason.error, null, null)) 66 | } 67 | if (isSurge) $httpClient.get(options, (error, response, body) => { 68 | callback(error, adapterStatus(response), body) 69 | }) 70 | if (isNode) { 71 | node.request(options, (error, response, body) => { 72 | callback(error, adapterStatus(response), body) 73 | }) 74 | } 75 | if (isJSBox) { 76 | if (typeof options == "string") options = { 77 | url: options 78 | } 79 | options["header"] = options["headers"] 80 | options["handler"] = function (resp) { 81 | let error = resp.error; 82 | if (error) error = JSON.stringify(resp.error) 83 | let body = resp.data; 84 | if (typeof body == "object") body = JSON.stringify(resp.data); 85 | callback(error, adapterStatus(resp.response), body) 86 | }; 87 | $http.get(options); 88 | } 89 | } 90 | const post = (options, callback) => { 91 | if (isQuanX) { 92 | if (typeof options == "string") options = { 93 | url: options 94 | } 95 | options["method"] = "POST" 96 | $task.fetch(options).then(response => { 97 | callback(null, adapterStatus(response), response.body) 98 | }, reason => callback(reason.error, null, null)) 99 | } 100 | if (isSurge) { 101 | $httpClient.post(options, (error, response, body) => { 102 | callback(error, adapterStatus(response), body) 103 | }) 104 | } 105 | if (isNode) { 106 | node.request.post(options, (error, response, body) => { 107 | callback(error, adapterStatus(response), body) 108 | }) 109 | } 110 | if (isJSBox) { 111 | if (typeof options == "string") options = { 112 | url: options 113 | } 114 | options["header"] = options["headers"] 115 | options["handler"] = function (resp) { 116 | let error = resp.error; 117 | if (error) error = JSON.stringify(resp.error) 118 | let body = resp.data; 119 | if (typeof body == "object") body = JSON.stringify(resp.data) 120 | callback(error, adapterStatus(resp.response), body) 121 | } 122 | $http.post(options); 123 | } 124 | } 125 | const log = (message) => { 126 | if (isEnableLog) console.log(`\n██${message}`) 127 | } 128 | const time = () => { 129 | const end = ((Date.now() - start) / 1000).toFixed(2) 130 | return console.log(`\n██用时:${end}秒`) 131 | } 132 | const done = (value) => { 133 | let key = `body` 134 | if(isRequest){ 135 | if (isQuanX) key = `content` 136 | if (isSurge) key = `body` 137 | } 138 | let obj = {} 139 | obj[key] = value 140 | if (isQuanX) isRequest ? $done(obj) : null 141 | if (isSurge) isRequest ? $done(obj) : $done() 142 | if (isNode) log(JSON.stringify(obj)) 143 | } 144 | const isEmpty = (obj) => { 145 | if(typeof obj == "undefined" || obj == null || obj == ""){ 146 | return true; 147 | }else{ 148 | return false; 149 | } 150 | } 151 | const wait = (time) => { 152 | return new Promise((resolve) => setTimeout(resolve, time)) 153 | } 154 | return {isRequest, isJSBox, isSurge, isQuanX, isLoon, isNode, getRequestUrl, getResponseBody, msg, setValueForKey, getVal, get, post, log, time, done, isEmpty, wait} 155 | } -------------------------------------------------------------------------------- /util/util.min.js: -------------------------------------------------------------------------------- 1 | function nobyda(){const e=Date.now();const t=typeof $request!="undefined";const n=typeof $httpClient!="undefined";const o=typeof $task!="undefined";const r=typeof $loon!="undefined";const s=typeof $app!="undefined"&&typeof $http!="undefined";const i=typeof require=="function"&&!s;const f=(()=>{if(i){const e=require("request");return{request:e}}else{return null}})();const u=()=>{if(o)return $resource.link;if(n)return $request.url;return""};const l=()=>{if(o)return $resource.content;if(n)return $response.body;return""};const d=(e,t,r)=>{if(o)$notify(e,t,r);if(n)$notification.post(e,t,r);if(i)g(e+t+r);if(s)$push.schedule({title:e,body:t?t+"\n"+r:r})};const c=(e,t)=>{if(o)return $prefs.setValueForKey(t,e);if(n)return $persistentStore.write(t,e)};const a=e=>{if(o)return $prefs.valueForKey(e);if(n)return $persistentStore.read(e)};const p=e=>{if(e){if(e.status){e["statusCode"]=e.status}else if(e.statusCode){e["status"]=e.statusCode}}return e};const y=(e,t)=>{if(o){if(typeof e=="string")e={url:e};e["method"]="GET";$task.fetch(e).then(e=>{t(null,p(e),e.body)},e=>t(e.error,null,null))}if(n)$httpClient.get(e,(e,n,o)=>{t(e,p(n),o)});if(i){f.request(e,(e,n,o)=>{t(e,p(n),o)})}if(s){if(typeof e=="string")e={url:e};e["header"]=e["headers"];e["handler"]=function(e){let n=e.error;if(n)n=JSON.stringify(e.error);let o=e.data;if(typeof o=="object")o=JSON.stringify(e.data);t(n,p(e.response),o)};$http.get(e)}};const $=(e,t)=>{if(o){if(typeof e=="string")e={url:e};e["method"]="POST";$task.fetch(e).then(e=>{t(null,p(e),e.body)},e=>t(e.error,null,null))}if(n){$httpClient.post(e,(e,n,o)=>{t(e,p(n),o)})}if(i){f.request.post(e,(e,n,o)=>{t(e,p(n),o)})}if(s){if(typeof e=="string")e={url:e};e["header"]=e["headers"];e["handler"]=function(e){let n=e.error;if(n)n=JSON.stringify(e.error);let o=e.data;if(typeof o=="object")o=JSON.stringify(e.data);t(n,p(e.response),o)};$http.post(e)}};const g=e=>{if(isEnableLog)console.log(`\n██${e}`)};const h=()=>{const t=((Date.now()-e)/1e3).toFixed(2);return console.log(`\n██用时:${t}秒`)};const b=e=>{let r=`body`;if(t){if(o)r=`content`;if(n)r=`body`}let s={};s[r]=e;if(o)t?$done(s):null;if(n)t?$done(s):$done();if(i)g(JSON.stringify(s))};const q=e=>{if(typeof e=="undefined"||e==null||e==""){return true}else{return false}};const S=e=>{return new Promise(t=>setTimeout(t,e))};return{isRequest:t,isJSBox:s,isSurge:n,isQuanX:o,isLoon:r,isNode:i,getRequestUrl:u,getResponseBody:l,msg:d,setValueForKey:c,getVal:a,get:y,post:$,log:g,time:h,done:b,isEmpty:q,wait:S}} --------------------------------------------------------------------------------