├── .github └── workflows │ ├── Auto.yml │ └── anker1209.yml ├── .gitignore ├── Jd_unbindCard.conf ├── README.md ├── faq.json ├── image ├── IMG_7050.PNG ├── IMG_7065.PNG ├── IMG_7143.PNG ├── IMG_7144.png ├── IMG_7145.png ├── IMG_7146.png ├── IMG_7147.png ├── IMG_7150.png ├── IMG_7151.png ├── IMG_8927.PNG ├── anker.JPG ├── bg_head_new.png ├── none.cpp ├── screenzy-1614423457282-yd.png ├── screenzy-1614423524222-dx.png └── screenzy-1614423603123-lt.png ├── install.json ├── package.json ├── scripts ├── Birthday.js ├── ChinaMobile_2021.js ├── ChinaTelecom_2021.js ├── ChinaUnicom_2021.js ├── Chinaunicom.js ├── JD-in-one-v2.js └── none.cpp └── upcoming.json /.github/workflows/Auto.yml: -------------------------------------------------------------------------------- 1 | # 工作流名称 2 | name: Auto master 3 | # 触发工作流事件 4 | on: 5 | schedule: 6 | # cron 表达式,格林威治时间 (GMT) 每小时0分钟开始执行 7 | - cron: '0 */1 * * *' 8 | # 手动触发 9 | workflow_dispatch: 10 | watch: 11 | types: started # Star触发 12 | repository_dispatch: 13 | types: anker1209-Scriptable # 上游作者库名 14 | 15 | # 工作流运行由可以顺序或并行运行的一个或多个作业组成 16 | jobs: 17 | # 此工作流程包含一个名为"repo-sync"的作业 18 | repo-sync: 19 | env: 20 | PAT: ${{ secrets.PAT }} 21 | runs-on: ubuntu-latest 22 | if: github.event.repository.owner.id == github.event.sender.id 23 | # 步骤代表将作为工作一部分执行的一系列任务 24 | steps: 25 | # 在$ GITHUB_WORKSPACE下签出您的存储库,以便您的工作可以访问它 26 | - uses: actions/checkout@v2 27 | with: 28 | persist-credentials: false 29 | # 使用sync 同步 30 | - name: sync anker1209-Scriptable # 需要同步的作者库名 31 | uses: repo-sync/github-sync@v2 32 | if: env.PAT 33 | with: 34 | # 需要 clone 的上游仓库地址 35 | source_repo: "https://github.com/anker1209/Scriptable.git" 36 | # 需要clone上游作者的分支名 37 | source_branch: "main" 38 | # 需要clone到自己项目的分支名 39 | destination_branch: "main" 40 | github_token: ${{ secrets.PAT }} -------------------------------------------------------------------------------- /.github/workflows/anker1209.yml: -------------------------------------------------------------------------------- 1 | name: Scriptable-sync 2 | on: 3 | push: 4 | branches: 5 | - main 6 | schedule: 7 | # 格林威治时间 (GMT) 每日01时开始执行 8 | - cron: '0 1 * * *' 9 | workflow_dispatch: 10 | watch: 11 | types: started 12 | repository_dispatch: 13 | types: sync-Scriptable/Scriptable 14 | jobs: 15 | repo-sync: 16 | env: 17 | PAT: ${{ secrets.PAT }} 18 | runs-on: ubuntu-latest 19 | if: github.event.repository.owner.id == github.event.sender.id 20 | steps: 21 | - uses: actions/checkout@v2 22 | with: 23 | persist-credentials: false 24 | 25 | - name: sync Scriptable/Scriptable 26 | uses: repo-sync/github-sync@v2 27 | if: env.PAT 28 | with: 29 | source_repo: "https://github.com/anker1209/Scriptable.git" 30 | source_branch: "main" 31 | destination_branch: "Scriptable" 32 | github_token: ${{ secrets.PAT }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /.history 12 | /.vscode 13 | /scripts/DmYY.js 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | /.idea 26 | -------------------------------------------------------------------------------- /Jd_unbindCard.conf: -------------------------------------------------------------------------------- 1 | // @ScriptName 取消绑定京东店铺会员 2 | // @Author @anker1209 3 | // @Function 点击会员卡直达注销页面 4 | // @ScriptURL https://raw.githubusercontent.com/anker1209/Scriptable/main/Jd_unbindCard.conf 5 | 6 | hostname = shopmember.m.jd.com 7 | 8 | ^https:\/\/shopmember\.m\.jd\.com\/shopcard\/\?(venderId=[0-9]+).* url 302 https:\/\/shopmember\.m\.jd\.com\/member\/memberCloseAccount\?$1 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scriptable 2 | 3 | [电报群](https://t.me/Scriptable_JS):https://t.me/Scriptable_JS 4 | 5 | ## 中国联通 6 | 7 | ![中国联通 图片](https://raw.githubusercontent.com/anker1209/Scriptable/main/image/screenzy-1614423603123-lt.png "联通小组件") 8 | 9 | 推荐使用Boxjs代理缓存: 10 | 11 | - [BoxJs 使用教程](https://chavyleung.gitbook.io/boxjs/) 12 | 13 | - [BoxJs 教程视频](https://youtu.be/eIpBrRxiy0w) 14 | 15 | cookie获取方法: 16 | 17 | #### QuanX: 18 | 19 | ```ini 20 | [mitm] 21 | hostname = act.10010.com, m.client.10010.com 22 | 23 | [rewrite_local] 24 | # 获取联通cookie 25 | ^https:\/\/m\.client\.10010\.com\/mobileserviceimportant\/smart\/smartwisdomCommon url script-request-header https://raw.githubusercontent.com/dompling/Script/master/10010/index.js 26 | ``` 27 | 28 | #### Surge: 29 | 30 | ```ini 31 | [MITM] 32 | hostname = act.10010.com, m.client.10010.com 33 | 34 | [Script] 35 | Rewrite: 获取联通cookie = type=http-request,pattern=^https:\/\/m\.client\.10010\.com\/mobileserviceimportant\/smart\/smartwisdomCommon,requires-body=1,max-size=0,script-path=https://raw.githubusercontent.com/dompling/Script/master/10010/index.js,script-update-interval=0 36 | ``` 37 | 38 | > Boxjs添加YaYa美女订阅链接(感谢YaYa辛苦付出~): 39 | https://raw.githubusercontent.com/dompling/Script/master/dompling.boxjs.json 40 | 41 | > 打开中国联通app --> 首页的流量查询获取Cookie; 42 | 43 | > 运行脚本,点击基础设置-->BoxJS域名,设置为你自己的BoxJS域名,再次运行脚本,选择代理缓存,获取缓存cookie; 44 | 45 | ## 中国移动(暂时不可用) 46 | 47 | ![中国移动 图片](https://raw.githubusercontent.com/anker1209/Scriptable/main/image/screenzy-1614423457282-yd.png "移动小组件") 48 | 49 | 推荐使用Boxjs代理缓存: 50 | 51 | - [BoxJs 使用教程](https://chavyleung.gitbook.io/boxjs/) 52 | 53 | - [BoxJs 教程视频](https://youtu.be/eIpBrRxiy0w) 54 | 55 | cookie获取方法: 56 | 57 | #### QuanX: 58 | 59 | ```ini 60 | [mitm] 61 | hostname = clientaccess.10086.cn 62 | 63 | [rewrite_local] 64 | ^https:\/\/clientaccess.10086.cn\/biz-orange\/LN\/uamrandcodelogin\/autoLogin url script-request-body https://raw.githubusercontent.com/chavyleung/scripts/master/10086/10086.fee.cookie.js 65 | ^https:\/\/clientaccess.10086.cn\/biz-orange\/BN\/realFeeQuery\/getRealFee url script-request-body https://raw.githubusercontent.com/chavyleung/scripts/master/10086/10086.fee.cookie.js 66 | ``` 67 | 68 | #### Surge: 69 | 70 | ```ini 71 | [mitm] 72 | hostname = clientaccess.10086.cn 73 | 74 | [Script] 75 | Rewrite: CMCC = 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 76 | Rewrite: CMCC = 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 77 | ``` 78 | 79 | > Boxjs添加chavy大佬订阅链接: 80 | https://raw.githubusercontent.com/chavyleung/scripts/master/box/chavy.boxjs.json 81 | 82 | > 打开中国移动APP(非10086),获取一次cookie,点击话费余额再获取一次cookie,若打开app时没有提示获取会话,请在 “我的” --> “设置” --> “登陆设置” 中关闭指纹/faceID登陆,打开自动登录,登陆以后关闭后台,重新打开app获取cookie(注:中国移动app以短信验证码方式登录,本机一键登录可能获取不到cookie); 83 | 84 | > 运行脚本,点击基础设置-->BoxJS域名,设置为你自己的BoxJS域名,再次运行脚本,选择代理缓存,获取缓存cookie 85 | 86 | ## 中国电信 87 | 88 | ![中国电信 图片](https://raw.githubusercontent.com/anker1209/Scriptable/main/image/screenzy-1614423524222-dx.png "电信小组件") 89 | 90 | 推荐使用Boxjs代理缓存: 91 | 92 | - [BoxJs 使用教程](https://chavyleung.gitbook.io/boxjs/) 93 | 94 | - [BoxJs 教程视频](https://youtu.be/eIpBrRxiy0w) 95 | 96 | cookie获取方法(也可以参考[作者Sunert的教程](https://github.com/Sunert/Script/tree/master/TaskConf/dianxin)): 97 | 98 | #### QuanX: 99 | 100 | ```ini 101 | [mitm] 102 | hostname = e.189.cn, mkt.21cn.com 103 | 104 | [rewrite_local] 105 | ^https?:\/\/e\.189\.cn\/store\/user\/package_detail\.do url script-request-header https://raw.githubusercontent.com/Sunert/Script/master/Task/telecomSky.js 106 | https:\/\/mkt\.21cn\.com\/mkt\/api\/user\/queryActivityInfo\.do\?activityId=\d+ url script-request-header https://raw.githubusercontent.com/Sunert/Script/master/Task/telecomSky.js 107 | ``` 108 | 109 | #### Surge: 110 | 111 | ```ini 112 | [mitm] 113 | hostname = e.189.cn, mkt.21cn.com 114 | 115 | [Script] 116 | 电信天翼套餐 = type=http-request,pattern=https:\/\/mkt\.21cn\.com\/mkt\/api\/user\/queryActivityInfo\.do\?activityId=\d+,script-path=https://raw.githubusercontent.com/Sunert/Script/master/Task/telecomSky.js 117 | 电信天翼套餐 = type=http-request,pattern=^https?:\/\/e\.189\.cn\/store\/user\/package_detail\.do,script-path=https://raw.githubusercontent.com/Sunert/Script/master/Task/telecomSky.js 118 | ``` 119 | > Boxjs添加Sunert大佬订阅链接: 120 | https://raw.githubusercontent.com/Sunert/Script/master/Task/sunert.boxjs.json 121 | 122 | > 打开天翼账号中心,获取cookie; 123 | 124 | > 运行脚本,点击基础设置-->BoxJS域名,设置为你自己的BoxJS域名,再次运行脚本,选择代理缓存,获取缓存cookie; 125 | 126 | > 无代理缓存的,请使用Stream类抓包APP进行手动抓包,获取cookie后填入脚本内注释位置或运行脚本——>账户设置——>手动输入; 127 | 128 | > 脚本内提供网站登录获取cookie,无代理缓存的可尝试网站登录获取cookie 129 | 130 | ## JD_in_one 131 | 132 | ![JD_in_one 图片](https://raw.githubusercontent.com/anker1209/Scriptable/main/image/IMG_7150.png "JD_in_one") 133 | ![JD_in_one 图片](https://raw.githubusercontent.com/anker1209/Scriptable/main/image/IMG_7151.png "JD_in_one") 134 | 135 | # 赞赏码 136 | 137 | -------------------------------------------------------------------------------- /faq.json: -------------------------------------------------------------------------------- 1 | { 2 | "update": "更新日期:2021.04.13", 3 | "desc": "随时更新,遇到问题请先在此查询。如无解决办法,请及时向我反馈。", 4 | "height": "59", 5 | "data": [{ 6 | "name": "设置问题", 7 | "item": [{ 8 | "question": "▶ Scriptable如何显示行号?", 9 | "answer": "点击scriptable左上角齿轮 —> Editor —> show line numbers。", 10 | "height": "80" 11 | }, 12 | { 13 | "question": "▶ 头像没有显示/没有更新", 14 | "answer": "1、京东APP上传头像;2、[重置设置][重置缓存];3、运行脚本。", 15 | "height": "80" 16 | }, 17 | { 18 | "question": "▶ 头像下方PLUS标志显示不正常", 19 | "answer": "[重置设置][重置缓存]后重新运行脚本。", 20 | "height": "66" 21 | }, 22 | { 23 | "question": "▶ 参数设置如何保留?", 24 | "answer": "参数设置绑定脚本文件名,只要文件名未变,参数将一直作用于该文件。请勿随意更改脚本文件名,更改文件名后,之前设置的所有参数将重置。", 25 | "height": "94" 26 | } 27 | ] 28 | }, { 29 | "name": "数据问题", 30 | "item": [{ 31 | "question": "▶ 为什么京豆总数、京享值都变成0了?", 32 | "answer": "若出现京豆总数、京享值为0,未知用户,执行以下操作:“文件”app —> iCloud 云盘 —> scriptable,删除里面的JD_in_one文件夹。tips:请勿使用多设备在该组件里使用同一cookie。", 33 | "height": "108" 34 | }, 35 | { 36 | "question": "▶ 提示“请登录您的京东账户/no login”", 37 | "answer": "如果出现此类提示,你的cookie过期了,重新抓cookie。cookie恢复后如果还出现此类提示,请重置缓存,或者将缓存时间临时改为0后运行脚本即可。", 38 | "height": "94" 39 | }, 40 | { 41 | "question": "", 42 | "answer": "", 43 | "height": "66" 44 | } 45 | ] 46 | }] 47 | } 48 | -------------------------------------------------------------------------------- /image/IMG_7050.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7050.PNG -------------------------------------------------------------------------------- /image/IMG_7065.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7065.PNG -------------------------------------------------------------------------------- /image/IMG_7143.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7143.PNG -------------------------------------------------------------------------------- /image/IMG_7144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7144.png -------------------------------------------------------------------------------- /image/IMG_7145.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7145.png -------------------------------------------------------------------------------- /image/IMG_7146.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7146.png -------------------------------------------------------------------------------- /image/IMG_7147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7147.png -------------------------------------------------------------------------------- /image/IMG_7150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7150.png -------------------------------------------------------------------------------- /image/IMG_7151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_7151.png -------------------------------------------------------------------------------- /image/IMG_8927.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/IMG_8927.PNG -------------------------------------------------------------------------------- /image/anker.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/anker.JPG -------------------------------------------------------------------------------- /image/bg_head_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/bg_head_new.png -------------------------------------------------------------------------------- /image/none.cpp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /image/screenzy-1614423457282-yd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/screenzy-1614423457282-yd.png -------------------------------------------------------------------------------- /image/screenzy-1614423524222-dx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/screenzy-1614423524222-dx.png -------------------------------------------------------------------------------- /image/screenzy-1614423603123-lt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Semporia/Scriptable/53bc0fdac648e2a2894db986448da33c62506f23/image/screenzy-1614423603123-lt.png -------------------------------------------------------------------------------- /install.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "脑瓜", 3 | "scriptable": true, 4 | "icon": "https://avatars2.githubusercontent.com/u/76637082?s=460&u=32c8666ab3e3e7e0aa5c2c570c9e8342f262a925&v=4", 5 | "repo": "https://github.com/anker1209/Scriptable", 6 | "apps": [ 7 | { 8 | "version": "2.2.7", 9 | "author": "脑瓜", 10 | "description": "显示京东用户信息、物流信息、白条还款信息、京豆K线图、金豆收支、钢镚、金贴、红包信息", 11 | "scriptURL": "https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/JD-in-one-v2.js", 12 | "thumb": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/e0d872b9-4866-4abf-87f4-a681083e6e04.png", 13 | "name": "JD-in-one-v2", 14 | "title": "JD-in-one-v2", 15 | "depend": [ 16 | { 17 | "name": "DmYY", 18 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" 19 | } 20 | ], 21 | "html": [ 22 | "作者:@anker1209", 23 | "
", 24 | "
", 25 | "

更新说明

", 26 | "v2.2.7", 27 | "
  • 默认账号设置成功时,若无参数输入则缓存
  • ", 28 | "v2.2.6", 29 | "
  • 脚本调整为直接读取BoxJS内的CK,多账号用户无需点击代理缓存读取CK
  • ", 30 | "v2.2.5", 31 | "
  • 增加东东农场种植进度
  • ", 32 | "
  • 缓存目录默认为Local,可选iCloud
  • ", 33 | "
  • 修复京享值显示
  • ", 34 | "
  • 红包修改为显示所有红包
  • ", 35 | "
  • 修复图标不显示
  • ", 36 | "
  • 取消过期京豆显示
  • ", 37 | "
    ", 38 | "

    使用说明

    " 39 | ], 40 | "images": [ 41 | "https://raw.githubusercontent.com/anker1209/Scriptable/main/image/IMG_7150.png", 42 | "https://raw.githubusercontent.com/anker1209/Scriptable/main/image/IMG_7151.png" 43 | ] 44 | }, 45 | { 46 | "version": "1.0.3", 47 | "author": "2Ya", 48 | "description": "脚本来自2Ya破壳日,修复农历生日显示错误,有任何问题@anker1209", 49 | "scriptURL": "https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/Birthday.js", 50 | "thumb": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/d0eee765-d320-490d-a611-5d1b49590441.png", 51 | "name": "Birthday", 52 | "title": "破壳日", 53 | "depend": [ 54 | { 55 | "name": "DmYY", 56 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" 57 | }, 58 | { 59 | "name": "Calendar", 60 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Calendar.js" 61 | } 62 | ], 63 | "html": [ 64 | "原作者:@dompling", 65 | "
    ", 66 | "
    ", 67 | "

    更新说明

    ", 68 | "
  • v1.0.3 修改年龄/相识显示
  • ", 69 | "
  • v1.0.2 修复当月显示bug
  • ", 70 | "
  • v1.0.1 增加年、月、天显示
  • ", 71 | "
    ", 72 | "

    日期填写说明

    ", 73 | "

    生日配置

    ", 74 | "
  • 如果过阳历生日,第二行填写阳历生日,第三行填写false或者不填
  • ", 75 | "
  • 如果过农历生日,第二行填写农历日期,第三行填写true
  • ", 76 | "
  • 无论阳历还是农历,日期格式均为年-月-日,如:2020-01-01
  • ", 77 | "
  • 第四行填写阳历日期
  • " 78 | ], 79 | "images": [ 80 | "https://raw.githubusercontent.com/anker1209/Scriptable/main/image/IMG_7065.PNG" 81 | ] 82 | }, 83 | { 84 | "version": "2.1.0", 85 | "author": "脑瓜", 86 | "description": "中国联通小组件,两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式", 87 | "scriptURL": "https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/ChinaUnicom_2021.js", 88 | "thumb": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/34ebdddc-90a8-4eb7-828b-f934c7386e12.png", 89 | "name": "Chinaunicom_2021", 90 | "title": "中国联通", 91 | "depend": [ 92 | { 93 | "name": "DmYY", 94 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" 95 | } 96 | ], 97 | "html": [ 98 | "作者:@anker1209", 99 | "
    ", 100 | "教程:https://github.com/anker1209/Scriptable", 101 | "
    ", 102 | "
    ", 103 | "

    更新说明

    ", 104 | "v2.1.0", 105 | "
  • 修改cookie获取方式
  • ", 106 | "
    " 107 | ], 108 | "images": [ 109 | "https://raw.githubusercontent.com/anker1209/Scriptable/main/image/screenzy-1614423603123-lt.png" 110 | ] 111 | }, 112 | { 113 | "version": "1.1.0", 114 | "author": "脑瓜", 115 | "description": "中国电信小组件,两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式", 116 | "scriptURL": "https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/ChinaTelecom_2021.js", 117 | "thumb": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/f12609b2-bf20-4993-be0f-99572d70d1b2.png", 118 | "name": "ChinaTelecom_2021", 119 | "title": "中国电信", 120 | "depend": [ 121 | { 122 | "name": "DmYY", 123 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" 124 | } 125 | ], 126 | "html": [ 127 | "作者:@anker1209", 128 | "
    ", 129 | "教程:https://github.com/anker1209/Scriptable", 130 | "
    ", 131 | "
    ", 132 | "

    更新说明

    ", 133 | "v1.1.0", 134 | "
  • 更新设置页面图标
  • ", 135 | "
    " 136 | ], 137 | "images": [ 138 | "https://raw.githubusercontent.com/anker1209/Scriptable/main/image/screenzy-1614423524222-dx.png" 139 | ] 140 | }, 141 | { 142 | "version": "1.0.1", 143 | "author": "脑瓜", 144 | "description": "中国移动小组件,两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式", 145 | "scriptURL": "https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/ChinaMobile_2021.js", 146 | "thumb": "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/3270c85e-2904-44cc-85cd-8de325b05743.png", 147 | "name": "ChinaMobile_2021", 148 | "title": "中国移动", 149 | "depend": [ 150 | { 151 | "name": "DmYY", 152 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" 153 | }, 154 | { 155 | "name": "crypto-js", 156 | "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/crypto-js.min.js" 157 | } 158 | ], 159 | "html": [ 160 | "作者:@anker1209", 161 | "
    ", 162 | "教程:https://github.com/anker1209/Scriptable", 163 | "
    ", 164 | "
    ", 165 | "

    更新说明

    ", 166 | "v1.0.1", 167 | "
  • 更新渐变色数组
  • " 168 | ], 169 | "images": [ 170 | "https://raw.githubusercontent.com/anker1209/Scriptable/main/image/screenzy-1614423457282-yd.png" 171 | ] 172 | } 173 | ] 174 | } 175 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scriptable-types", 3 | "version": "1.0.0", 4 | "author": "2Ya", 5 | "description": "", 6 | "main": "index.js", 7 | "keywords": [ 8 | "scriptable", 9 | "ios", 10 | "widget" 11 | ], 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@types/scriptable-ios": "^1.6.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/Birthday.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: pink; icon-glyph: birthday-cake; 4 | // Author: 2Ya UI: 脑瓜 https://t.me/Scriptable_JS 5 | // 该脚本依赖DmYY及Calendar: https://github.com/dompling/Scriptable/tree/master/Scripts 6 | // version:1.0.3 7 | // update:2021/03/14 8 | 9 | // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 10 | if (typeof require === "undefined") require = importModule; 11 | const { DmYY, Runing } = require("./DmYY"); 12 | const { Calendar } = require("./Calendar"); 13 | const $ = new Calendar(); 14 | // #####################设置##################### 15 | const extraTextColor = "fc8ac3" //环形进度条中心背景颜色及名字、meetDay颜色 16 | const ringColor = "fc5ead" //环形进度条颜色 17 | const nameTextSize = 15 // 名字大小 18 | const meetDayTextSize = 25 // meetDay文字大小 19 | const ringSize = 60 // 环形进度条大小 20 | const mainTextSize = 13 // 倒数、农历、生日文字大小 21 | const lineHeight = 8 // 倒数、农历、生日文字行间距大小 22 | const leftImageSize = 180 // 左侧图片宽度 23 | //############################################## 24 | let candle = new Request("https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/ff304168-6cd3-4b5c-bb26-05ae2538f147.png") 25 | let ringIcon = await candle.loadImage() 26 | let countIcon = SFSymbol.named("hourglass.bottomhalf.fill").image; 27 | let lunarIcon = SFSymbol.named("25.square.fill").image; // 农历图标数字 28 | let birthIcon = SFSymbol.named("app.gift.fill").image; 29 | 30 | class Widget extends DmYY { 31 | constructor(arg) { 32 | super(arg); 33 | this.name = "破壳日"; 34 | this.en = "birthday"; 35 | this.logo =""; 36 | this.LEFT_IMG_KEY = this.FILE_MGR_LOCAL.joinPath( 37 | this.FILE_MGR_LOCAL.documentsDirectory(), 38 | `left_image_${this.SETTING_KEY}.jpg`, 39 | ); 40 | this.defaultData = { ...this.defaultData, ...this.settings[this.en] }; 41 | if (config.runsInApp) { 42 | this.registerAction("基础设置",this.setWidgetConfig); 43 | this.registerAction("生日配置", this.setWidgetInitConfig); 44 | this.registerAction("头像设置", this.setLeftWidgetImage); 45 | this.registerAction("代理缓存", this.setWidgetBoxJSConfig); 46 | } 47 | } 48 | 49 | defaultData = { 50 | username: "", // 姓名 51 | time: "", // 生日日期 52 | nongli: "", // 农历生日 53 | eday: "", //相识 54 | isLeapMonth: false, //如果是农历闰月第四个参数赋值true即可 55 | }; 56 | 57 | contentText = {}; 58 | 59 | init = async () => { 60 | try { 61 | this.getCalendarData(); 62 | } catch (e) { 63 | console.log(e); 64 | } 65 | }; 66 | 67 | getAge = (beginStr) => { 68 | let tmpBirth = {}; 69 | tmpBirth.year = 0; 70 | tmpBirth.month = 0; 71 | tmpBirth.day = 0; 72 | 73 | if(beginStr == null || beginStr == '') { 74 | return; 75 | } 76 | let startDate = new Date(beginStr.replace(/-/g, "/")); 77 | let today = new Date(); 78 | 79 | let startYear = startDate.getFullYear(); 80 | let endYear = today.getFullYear(); 81 | let startMonth = startDate.getMonth() + 1; 82 | let endMonth = today.getMonth() + 1; 83 | let startDay = startDate.getDate(); 84 | let endDay = today.getDate(); 85 | let allDays = 0; 86 | //准确的每个月天数数组,判断闰年平年。 87 | let allDayArr = []; 88 | //当月计算 89 | if(startYear == endYear && startMonth == endMonth) { 90 | tmpBirth.day = endDay - startDay; 91 | return tmpBirth; 92 | } 93 | //正常计算 94 | for(let i = startYear; i <= endYear; i++) { 95 | let currYear = 365 96 | let yearMonth = 12; 97 | if(((i % 4 == 0 && i % 100 !== 0) || i % 400 == 0)) { 98 | allDays += 366; 99 | currYear = 366; 100 | } 101 | let currMonth = 1; 102 | if(i == startYear){ 103 | currMonth = startMonth; 104 | } 105 | if(i == endYear){ 106 | yearMonth = endMonth; 107 | } 108 | //闰年计算 109 | for(let m = currMonth; m <= yearMonth; m++) { 110 | let fullDays = 30; 111 | 112 | if(m == 1 || m == 3 || m == 8 || m == 10 || m == 12) { 113 | fullDays = 31 114 | } else if(m == 2) { 115 | if(((i % 4 == 0 && i % 100 !== 0) || i % 400 == 0)){ 116 | fullDays = 29; 117 | } else { 118 | fullDays = 28; 119 | } 120 | } 121 | let dayObj = { 122 | fullDays:fullDays, 123 | currDays:fullDays 124 | }; 125 | if(m == startMonth && i == startYear){ 126 | dayObj = { 127 | fullDays:fullDays, 128 | currDays:fullDays-startDay 129 | }; 130 | }else 131 | if(m == endMonth && i == endYear){ 132 | dayObj = { 133 | fullDays:fullDays, 134 | currDays:endDay 135 | }; 136 | } 137 | allDayArr.push(dayObj); 138 | } 139 | } 140 | 141 | if(allDayArr.length == 1) { 142 | tmpBirth.day = allDayArr[0].currDays; 143 | } else if(allDayArr.length == 2) { 144 | var d1 = allDayArr[0].currDays; 145 | var d2 = allDayArr[1].currDays; 146 | //月份天数浮动因子决定准确性 147 | let cfDay = allDayArr[0].fullDays > allDayArr[allDayArr.length - 1].fullDays ? allDayArr[allDayArr.length - 1].fullDays : allDayArr[0].fullDays; 148 | if((d1 + d2) >= cfDay) { 149 | tmpBirth.day = (d1 + d2) - cfDay; 150 | tmpBirth.month += 1; 151 | } else { 152 | tmpBirth.day = d1 + d2; 153 | } 154 | } else { 155 | let d1 = allDayArr[0].currDays; 156 | let d2 = allDayArr[allDayArr.length - 1].currDays; 157 | let sumFullDay = 0; 158 | for(let i = 0; i < allDayArr.length; i++){ 159 | sumFullDay += allDayArr[i].fullDays; 160 | } 161 | //月份天数浮动因子决定准确性 162 | let cfDay = allDayArr[0].fullDays > allDayArr[allDayArr.length - 1].fullDays ? allDayArr[allDayArr.length - 1].fullDays : allDayArr[0].fullDays; 163 | if((d1 + d2) >= cfDay){ 164 | tmpBirth.day = (d1 + d2) - cfDay; 165 | tmpBirth.month += 1; 166 | } else { 167 | tmpBirth.day = d1 + d2; 168 | } 169 | tmpBirth.month += allDayArr.length - 2; 170 | 171 | if(tmpBirth.month >= 12){ 172 | tmpBirth.year += Math.floor(tmpBirth.month / 12); 173 | tmpBirth.month = tmpBirth.month - (tmpBirth.year * 12); 174 | } 175 | } 176 | return tmpBirth; 177 | }; 178 | 179 | getEdayNumber = (date) => { 180 | var initDay = date.split("-"); 181 | var obj = { 182 | cYear: parseInt(initDay[0]), 183 | cMonth: parseInt(initDay[1]), 184 | cDay: parseInt(initDay[2]), 185 | }; 186 | return Math.abs(this.$.daysBetween(obj)); 187 | }; 188 | 189 | getCalendarData = () => { 190 | const { time, nongli, isLeapMonth, eday } = this.defaultData; 191 | const _data = time.split("-"); 192 | const opt = { 193 | year: parseInt(_data[0]), 194 | month: parseInt(_data[1]), 195 | day: parseInt(_data[2]), 196 | nongli, 197 | isLeapMonth, 198 | }; 199 | 200 | const response = {}; 201 | response.birthdayText = this.$.birthday(opt); 202 | response.nextBirthday = response.birthdayText[0]; 203 | 204 | var solarData 205 | if (nongli) { 206 | solarData = this.$.lunar2solar(opt.year, opt.month, opt.day, isLeapMonth) 207 | } else { 208 | solarData = this.$.solar2lunar(opt.year, opt.month, opt.day); 209 | } 210 | response.gregorian = solarData; 211 | response.animal = `${this.$.getAnimalZodiacToEmoji(solarData.Animal)}-${ 212 | solarData.Animal 213 | }`; 214 | response.astro = `${this.$.getAstroToEmoji(solarData.astro)}-${solarData.astro}`; 215 | if (this.$.verifyTime(eday)) { 216 | response.meetDay = this.getEdayNumber(eday); 217 | } 218 | this.contentText = response; 219 | }; 220 | 221 | setRightCell = (rowCell, icon, iconColor, title, value, dayImage = false) => { 222 | const subWidget = rowCell.addStack(); 223 | subWidget.centerAlignContent(); 224 | const subImg = subWidget.addImage(icon); 225 | subImg.tintColor = new Color(iconColor); 226 | subImg.imageSize = new Size(mainTextSize, mainTextSize); 227 | subWidget.addSpacer(4); 228 | const subTitle = subWidget.addText(title); 229 | subTitle.font = Font.systemFont(mainTextSize); 230 | subTitle.textColor = this.widgetColor; 231 | subWidget.addSpacer(); 232 | const subValue = subWidget.addText(value); 233 | subValue.font = Font.systemFont(mainTextSize); 234 | subValue.textColor = this.widgetColor; 235 | subValue.lineLimit = 1; 236 | if (dayImage) { 237 | subWidget.addSpacer(2); 238 | let dayIcon = subWidget.addImage(dayImage.image); 239 | dayIcon.imageSize = new Size(mainTextSize + 1, mainTextSize + 1); 240 | dayIcon.tintColor = new Color('1ab6f8');; 241 | } 242 | }; 243 | 244 | setLeftView = (w) => { 245 | const leftImg = this.getLeftImage(); 246 | const left = w.addStack(); 247 | left.size = new Size(leftImageSize, 0); 248 | left.cornerRadius = 10; 249 | if (leftImg) { 250 | left.backgroundImage = leftImg; 251 | } 252 | left.addSpacer(); 253 | left.layoutVertically(); 254 | const leftAdd = left.addStack(); 255 | leftAdd.centerAlignContent(); 256 | if(this.defaultData.bless) { 257 | leftAdd.size = new Size(leftImageSize, 26); 258 | leftAdd.backgroundColor = new Color(extraTextColor, 0.8); 259 | const bless = leftAdd.addText("✿ "+this.defaultData.bless+" ✿"); 260 | bless.textColor = new Color("ffffff", 0.8); 261 | bless.font = Font.mediumSystemFont(mainTextSize); 262 | } 263 | return w; 264 | }; 265 | 266 | setRightView = (right) => { 267 | const { time, nongli, isLeapMonth} = this.defaultData; 268 | const _data = time.split("-"); 269 | const opt = { 270 | year: parseInt(_data[0]), 271 | month: parseInt(_data[1]), 272 | day: parseInt(_data[2]), 273 | nongli, 274 | isLeapMonth 275 | }; 276 | const { 277 | animal, 278 | astro, 279 | gregorian, 280 | nextBirthday, 281 | meetDay, 282 | birthdayText, 283 | } = this.contentText; 284 | const { IMonthCn, IDayCn } = gregorian; 285 | const _birth = `${nextBirthday.cYear}-${nextBirthday.cMonth}-${nextBirthday.cDay}`; 286 | right.layoutVertically(); 287 | 288 | const rightTop = right.addStack(); 289 | const textContent = rightTop.addStack(); 290 | textContent.layoutVertically(); 291 | const babyName = textContent.addText(this.defaultData.username); 292 | babyName.font = Font.boldSystemFont(nameTextSize); 293 | babyName.textColor = new Color(extraTextColor); 294 | babyName.lineLimit = 1; 295 | textContent.addSpacer(); 296 | if (meetDay) { 297 | const babyDays = textContent.addText(`${meetDay}`); 298 | babyDays.font = Font.boldRoundedSystemFont(meetDayTextSize); 299 | babyDays.textColor = new Color(extraTextColor); 300 | textContent.addSpacer(8); 301 | } 302 | 303 | var preData 304 | if (nongli) { 305 | preData = this.$.lunar2solar(`${nextBirthday.lYear}`-1, opt.month, opt.day, isLeapMonth) 306 | log(preData) 307 | } else { 308 | preData = this.$.solar2lunar(`${nextBirthday.cYear}`-1, opt.month, opt.day) 309 | log(preData) 310 | } 311 | const today = new Date(); 312 | const thenDate = new Date(`${nextBirthday.cYear}`, `${nextBirthday.cMonth}`-1, `${nextBirthday.cDay}`); 313 | log(thenDate) 314 | const passDate = new Date(preData.cYear, preData.cMonth-1, preData.cDay); 315 | log(passDate) 316 | const canvSize = 172; 317 | const canvTextSize = 45; 318 | const canvas = new DrawContext(); 319 | const canvWidth = 12; 320 | const canvRadius = 80; 321 | const cbgColor = new Color(ringColor, 0.2); 322 | const cfgColor = new Color(ringColor); 323 | const centerColor = new Color(extraTextColor) 324 | const cfontColor = new Color("ffffff"); 325 | canvas.size = new Size(canvSize, canvSize); 326 | canvas.opaque = false; 327 | canvas.respectScreenScale = true; 328 | 329 | const gap = today.getTime() - passDate.getTime(); 330 | const gap2 = thenDate.getTime() - passDate.getTime(); 331 | const deg = Math.floor(gap/gap2 * 100 * 3.6); 332 | 333 | let ctr = new Point(canvSize / 2, canvSize / 2); 334 | const bgx = ctr.x - canvRadius; 335 | const bgy = ctr.y - canvRadius; 336 | const bgd = 2 * canvRadius; 337 | const bgr = new Rect(bgx, bgy, bgd, bgd); 338 | 339 | canvas.setFillColor(cfgColor); 340 | canvas.setStrokeColor(cbgColor); 341 | canvas.setLineWidth(canvWidth); 342 | canvas.strokeEllipse(bgr); 343 | 344 | for (let t = 0; t < deg; t++) { 345 | const rect_x = ctr.x + canvRadius * Math.sin((t * Math.PI) / 180) - canvWidth / 2; 346 | const rect_y = ctr.y - canvRadius * Math.cos((t * Math.PI) / 180) - canvWidth / 2; 347 | const rect_r = new Rect(rect_x, rect_y, canvWidth, canvWidth); 348 | canvas.fillEllipse(rect_r); 349 | }; 350 | 351 | const ringBG = new Rect(bgx + canvWidth / 2 + 8, bgy + canvWidth / 2 + 8, canvRadius * 2 - canvWidth -16, canvRadius * 2 - canvWidth - 16); 352 | canvas.setFillColor(centerColor); 353 | canvas.setLineWidth(0); 354 | canvas.fillEllipse(ringBG); 355 | canvas.drawImageInRect(ringIcon, ringBG); 356 | 357 | const canvTextRect = new Rect(0, 100 - canvTextSize / 2 - 10, canvSize, canvTextSize); 358 | canvas.setTextAlignedCenter(); 359 | canvas.setTextColor(cfontColor); 360 | canvas.setFont(Font.mediumRoundedSystemFont(canvTextSize)); 361 | canvas.drawTextInRect(`${birthdayText[1]}`, canvTextRect); 362 | 363 | const imageContent = rightTop.addStack(); 364 | imageContent.addSpacer(); 365 | imageContent.size = new Size(0, ringSize); 366 | imageContent.addImage(canvas.getImage()); 367 | 368 | const tmpBirth = this.getAge(this.defaultData.eday); 369 | let ageYear = tmpBirth.year > 0 ? `${tmpBirth.year}岁` : ''; 370 | let ageMonth = tmpBirth.month > 0 ? `${tmpBirth.month}月` : ''; 371 | let ageDay = tmpBirth.day > 0 ? `${tmpBirth.day}天` : ''; 372 | const age = ageYear + ageMonth + ageDay; 373 | const dayIcon = SFSymbol.named(tmpBirth.day + '.circle.fill'); 374 | 375 | if (tmpBirth.year > 0 && tmpBirth.month > 0 && tmpBirth.day > 0) { 376 | this.setRightCell(right, countIcon, '1ab6f8', '年龄', ageYear + ageMonth, dayIcon); 377 | } else { 378 | this.setRightCell(right, countIcon, '1ab6f8', '年龄', age); 379 | } 380 | right.addSpacer(lineHeight); 381 | this.setRightCell(right, lunarIcon, '30d15b', '农历', `${IMonthCn}${IDayCn}`); 382 | right.addSpacer(lineHeight); 383 | this.setRightCell(right, birthIcon, 'fc6d6d', '生日', _birth); 384 | return right; 385 | }; 386 | 387 | fetch = async () => { 388 | const response = await this.$request.get( 389 | "https://api.uomg.com/api/rand.qinghua?format=json", 390 | ); 391 | return response.content; 392 | }; 393 | 394 | renderSmall = async (w) => { 395 | this.setRightView(w.addStack()); 396 | return w; 397 | }; 398 | 399 | renderLarge = async (w) => { 400 | w.addSpacer(20); 401 | const body = w.addStack(); 402 | const left = body.addStack(); 403 | this.setLeftView(left); 404 | body.addSpacer(10); 405 | const right = body.addStack(); 406 | this.setRightView(right); 407 | 408 | w.addSpacer(); 409 | const footer = w.addStack(); 410 | const text = await this.fetch(); 411 | const subContent = footer.addText(text); 412 | subContent.font = Font.lightSystemFont(16); 413 | subContent.textColor = this.widgetColor; 414 | w.addSpacer(); 415 | return w; 416 | }; 417 | 418 | renderMedium = async (w) => { 419 | const body = w.addStack(); 420 | const left = body.addStack(); 421 | w.setPadding(16, 16, 16, 16); 422 | this.setLeftView(left); 423 | body.addSpacer(10); 424 | const right = body.addStack(); 425 | this.setRightView(right); 426 | return w; 427 | }; 428 | 429 | /** 430 | * 渲染函数,函数名固定 431 | * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 432 | */ 433 | async render() { 434 | await this.init(); 435 | const widget = new ListWidget(); 436 | await this.getWidgetBackgroundImage(widget); 437 | const header = widget.addStack(); 438 | if (this.widgetFamily === "medium") { 439 | await this.renderMedium(widget); 440 | } else if (this.widgetFamily === "large") { 441 | await this.renderLarge(widget); 442 | } else { 443 | await this.renderSmall(widget); 444 | } 445 | return widget; 446 | } 447 | 448 | renderMoreHeader = async (header) => { 449 | header.centerAlignContent(); 450 | await this.renderHeader(header, this.logo, this.name, this.widgetColor); 451 | header.addSpacer(); 452 | const headerMore = header.addStack(); 453 | headerMore.setPadding(1, 10, 1, 10); 454 | headerMore.cornerRadius = 10; 455 | const textItem = headerMore.addText(this.defaultData.username); 456 | textItem.font = Font.boldSystemFont(12); 457 | textItem.textColor = this.widgetColor; 458 | textItem.lineLimit = 1; 459 | textItem.rightAlignText(); 460 | return header; 461 | }; 462 | 463 | /** 464 | * 获取当前插件是否有自定义背景图片 465 | * @reutrn img | false 466 | */ 467 | getLeftImage() { 468 | let result = null; 469 | if (this.FILE_MGR_LOCAL.fileExists(this.LEFT_IMG_KEY)) { 470 | result = Image.fromFile(this.LEFT_IMG_KEY); 471 | } 472 | return result; 473 | } 474 | 475 | /** 476 | * 设置当前组件的背景图片 477 | * @param {image} img 478 | */ 479 | setLeftImage(img, notify = true) { 480 | if (!img) { 481 | // 移除背景 482 | if (this.FILE_MGR_LOCAL.fileExists(this.LEFT_IMG_KEY)) { 483 | this.FILE_MGR_LOCAL.remove(this.LEFT_IMG_KEY); 484 | } 485 | if (notify) this.notify("移除成功", "小组件图片已移除,稍后刷新生效"); 486 | } else { 487 | // 设置背景 488 | // 全部设置一遍, 489 | this.FILE_MGR_LOCAL.writeImage(this.LEFT_IMG_KEY, img); 490 | if (notify) this.notify("设置成功", "小组件图片已设置!稍后刷新生效"); 491 | } 492 | } 493 | 494 | setLeftWidgetImage = async () => { 495 | const alert = new Alert(); 496 | alert.title = "设置左侧图"; 497 | alert.message = "显示左侧图片"; 498 | alert.addAction("设置新图"); 499 | alert.addAction("清空图片"); 500 | alert.addCancelAction("取消"); 501 | const actions = [ 502 | async () => { 503 | const backImage = await this.chooseImg(); 504 | if (!await this.verifyImage(backImage)) return; 505 | await this.setLeftImage(backImage, true); 506 | }, 507 | () => { 508 | this.setLeftImage(false, true); 509 | }, 510 | ]; 511 | const id = await alert.presentAlert(); 512 | if (id === -1) return; 513 | actions[id] && actions[id].call(this); 514 | }; 515 | 516 | setWidgetInitConfig = async () => { 517 | const a = new Alert(); 518 | a.title = "🐣破壳日配置"; 519 | a.message = "配置破壳日的基础信息"; 520 | a.addTextField("昵称", this.defaultData.username); 521 | a.addTextField("生日/ 年-月-日", this.defaultData.time); 522 | a.addTextField("农历/ true | false", `${this.defaultData.nongli || ""}`); 523 | a.addTextField("相识/ 年-月-日", this.defaultData.eday); 524 | a.addTextField("寄语", this.defaultData.bless); 525 | a.addAction("确定"); 526 | a.addCancelAction("取消"); 527 | const id = await a.presentAlert(); 528 | if (id === -1) return; 529 | this.defaultData.username = a.textFieldValue(0); 530 | this.defaultData.time = a.textFieldValue(1); 531 | this.defaultData.nongli = a.textFieldValue(2) === "true"; 532 | this.defaultData.eday = a.textFieldValue(3); 533 | this.defaultData.bless = a.textFieldValue(4); 534 | // 保存到本地 535 | this.settings[this.en] = this.defaultData; 536 | this.saveSettings(); 537 | }; 538 | 539 | setWidgetBoxJSConfig = async () => { 540 | try { 541 | const datas = await this.getCache(); 542 | Object.keys(this.defaultData).forEach((key) => { 543 | this.defaultData[key] = datas[`@${this.en}.${key}`]; 544 | }); 545 | this.settings[this.en] = this.defaultData; 546 | this.saveSettings(); 547 | } catch (e) { 548 | this.notify(this.name, "", "BoxJS 数据读取失败,请点击通知查看教程", "https://chavyleung.gitbook.io/boxjs/awesome/videos"); 549 | } 550 | }; 551 | } 552 | 553 | Runing(Widget, "", false, { $ }); 554 | -------------------------------------------------------------------------------- /scripts/ChinaMobile_2021.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: deep-green; icon-glyph: mobile-alt; 4 | // Script: 2Ya & 脑瓜 5 | // 电报群:https://t.me/Scriptable_JS @anker1209 6 | // 该脚本小尺寸组件支持两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式 7 | // 渐变进度条为试验性功能,默认关闭 8 | // version:1.0.1 9 | // update:2021/03/12 10 | 11 | if (typeof require === 'undefined') require = importModule; 12 | const {DmYY, Runing} = require('./DmYY'); 13 | const CryptoJS = require('./crypto-js'); 14 | 15 | class Widget extends DmYY { 16 | constructor(arg) { 17 | super(arg); 18 | this.name = '中国移动'; 19 | this.en = 'ChinaMobile_2021'; 20 | this.logo = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/855b2333-0c1b-4bc1-9178-aac0c24119a0.png'; 21 | this.smallLogo = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/b219c861-0b33-4fee-8245-e304275fc2ef.png'; 22 | this.Run(); 23 | } 24 | widgetParam = args.widgetParameter; 25 | getfee = {}; // 推荐使用Boxjs代理缓存,若无请自行手动抓包后在此输入中国移动getfee数据。 26 | autologin = {}; // 推荐使用Boxjs代理缓存,若无请自行手动抓包后在此输入中国移动autologin数据。 27 | cookie = ''; 28 | 29 | gradient = false; 30 | 31 | flowColorHex = '80BA26'; 32 | voiceColorHex = 'F18F00'; 33 | 34 | ringStackSize = 61; 35 | ringTextSize = 14; 36 | feeTextSize = 21; 37 | textSize = 13; 38 | smallPadding = 16; 39 | padding = 10; 40 | logoScale = 0.24; 41 | 42 | canvSize = 178; 43 | canvWidth = 18; 44 | canvRadius = 80; 45 | 46 | format = (str) => { 47 | return parseInt(str) >= 10 ? str : `0${str}`; 48 | }; 49 | 50 | date = new Date(); 51 | arrUpdateTime = [ 52 | this.format(this.date.getMonth() + 1), 53 | this.format(this.date.getDate()), 54 | this.format(this.date.getHours()), 55 | this.format(this.date.getMinutes()), 56 | ]; 57 | 58 | fee = { 59 | title: '话费剩余', 60 | number: 0, 61 | unit: '元', 62 | en: '¥', 63 | }; 64 | 65 | flow = { 66 | percent: 0, 67 | title: '流量剩余', 68 | number: 0, 69 | unit: 'MB', 70 | en: 'MB', 71 | icon: 'antenna.radiowaves.left.and.right', 72 | iconColor: new Color('1ab6f8'), 73 | FGColor: new Color(this.flowColorHex), 74 | BGColor: new Color(this.flowColorHex, 0.2), 75 | colors: [], 76 | }; 77 | 78 | voice = { 79 | percent: 0, 80 | title: '语音剩余', 81 | number: 0, 82 | unit: '分钟', 83 | en: 'MIN', 84 | icon: 'phone.fill', 85 | iconColor: new Color('30d15b'), 86 | FGColor: new Color(this.voiceColorHex), 87 | BGColor: new Color(this.voiceColorHex, 0.2), 88 | colors: [], 89 | }; 90 | 91 | point = { 92 | title: '更新时间', 93 | number: `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, 94 | unit: '', 95 | icon: 'arrow.2.circlepath', 96 | iconColor: new Color('fc6d6d'), 97 | } 98 | 99 | init = async () => { 100 | try { 101 | await this.login(); 102 | await this.queryFee(); 103 | await this.queryFlow(); 104 | } catch (e) { 105 | console.log(e); 106 | } 107 | }; 108 | 109 | async login() { 110 | try { 111 | const options = this.autologin; 112 | const request = new Request(options.url); 113 | Object.keys(options).forEach((key) => { 114 | request[key] = options[key]; 115 | }); 116 | request.method = 'POST'; 117 | await request.loadString(); 118 | this.cookie = request.response.headers['Set-Cookie']; 119 | if (this.cookie) { 120 | console.log('✅登陆成功'); 121 | } else { 122 | console.log('❌登陆失败'); 123 | } 124 | } catch (e) { 125 | console.log('❌登陆失败,请检查 Ck:' + e); 126 | } 127 | } 128 | 129 | async queryFee() { 130 | try { 131 | const options = this.getfee; 132 | const body = JSON.parse(this.decrypt(options.body, 'bAIgvwAuA4tbDr9d')); 133 | const cellNum = body.reqBody.cellNum; 134 | const bodystr = `{"t":"${CryptoJS.MD5( 135 | this.cookie, 136 | ).toString()}","cv":"9.9.9","reqBody":{"cellNum":"${cellNum}"}}`; 137 | options.body = this.encrypt(bodystr, 'bAIgvwAuA4tbDr9d'); 138 | options.headers['Cookie'] = this.cookie; 139 | options.headers['xs'] = CryptoJS.MD5( 140 | options.url + '_' + bodystr + '_Leadeon/SecurityOrganization', 141 | ).toString(); 142 | const request = new Request(options.url); 143 | request.method = 'POST'; 144 | request.headers = options.headers; 145 | request.body = options.body; 146 | const webView = new WebView(); 147 | await webView.loadRequest(request); 148 | const response = await webView.evaluateJavaScript( 149 | 'completion(document.body.innerText);', 150 | true, 151 | ); 152 | const data = JSON.parse(this.decrypt(response, 'GS7VelkJl5IT1uwQ')); 153 | if (data.retCode === '000000') { 154 | console.log('✅费用信息获取成功'); 155 | const {rspBody} = data; 156 | this.fee.number = rspBody.curFee; 157 | } else { 158 | console.log('❌费用信息获取失败,请检查 Ck 配置' + data.retDesc); 159 | } 160 | } catch (e) { 161 | console.log('❌费用信息获取失败:' + e); 162 | } 163 | } 164 | 165 | async queryFlow() { 166 | try { 167 | const options = this.getfee; 168 | const body = JSON.parse(this.decrypt(options.body, 'bAIgvwAuA4tbDr9d')); 169 | const cellNum = body.reqBody.cellNum; 170 | options.url = 171 | 'https://clientaccess.10086.cn/biz-orange/BN/newComboMealResouceUnite/getNewComboMealResource'; 172 | const bodystr = `{"t":"${CryptoJS.MD5( 173 | this.cookie, 174 | ). 175 | toString()}","cv":"9.9.9","reqBody":{"cellNum":"${cellNum}","tag":"3"}}`; 176 | options.body = this.encrypt(bodystr, 'bAIgvwAuA4tbDr9d'); 177 | options.headers['Cookie'] = this.cookie; 178 | options.headers['xs'] = CryptoJS.MD5( 179 | options.url + '_' + bodystr + '_Leadeon/SecurityOrganization', 180 | ).toString(); 181 | 182 | const request = new Request(options.url); 183 | request.method = 'POST'; 184 | request.headers = options.headers; 185 | request.body = options.body; 186 | const webView = new WebView(); 187 | await webView.loadRequest(request); 188 | const response = await webView.evaluateJavaScript( 189 | 'completion(document.body.innerText);', 190 | true, 191 | ); 192 | 193 | const data = JSON.parse(this.decrypt(response, 'GS7VelkJl5IT1uwQ')); 194 | if (data.retCode === '000000') { 195 | console.log('✅套餐信息获取成功'); 196 | const res = data.rspBody.qryInfoRsp[0].resourcesTotal; 197 | const flowRes = res.find((r) => r.resourcesCode === '04'); 198 | const voiceRes = res.find((r) => r.resourcesCode === '01'); 199 | var flowResValue = '未开通', 200 | voiceResValue = ''; 201 | if (flowRes) { 202 | const total = this.translateFlow({ 203 | value: flowRes.allTotalRes, 204 | code: flowRes.allUnit, 205 | }); 206 | const remain = this.translateFlow({ 207 | value: flowRes.allRemainRes, 208 | code: flowRes.remUnit, 209 | }); 210 | 211 | this.flow.percent = ((remain.value / (total.value || 1)) * 100).toFixed(2); 212 | this.flow.number = flowRes.allRemainRes; 213 | this.flow.unit = `${remain.unit}B`; 214 | this.flow.en = `${remain.unit}B`; 215 | flowResValue = `${flowRes.allRemainRes}${remain.unit}`; 216 | } 217 | if (voiceRes) { 218 | this.voice.percent = ((voiceRes.allRemainRes / (voiceRes.allTotalRes || 1)) * 100).toFixed(2); 219 | this.voice.number = voiceRes.allRemainRes; 220 | voiceResValue = voiceRes.allRemainRes; 221 | } 222 | console.log(`✅流量:` + flowResValue + '\n ✅语音:' + voiceResValue); 223 | } else { 224 | console.log('❌流量信息获取失败,请检查 Ck 配置' + data.retDesc); 225 | } 226 | } catch (e) { 227 | console.log('❌流量信息获取失败:' + e); 228 | } 229 | } 230 | 231 | encrypt(str, key) { 232 | return CryptoJS.AES.encrypt( 233 | CryptoJS.enc.Utf8.parse(str), 234 | CryptoJS.enc.Utf8.parse(key), 235 | { 236 | iv: CryptoJS.enc.Utf8.parse('9791027341711819'), 237 | mode: CryptoJS.mode.CBC, 238 | padding: CryptoJS.pad.Pkcs7, 239 | }, 240 | ).toString(); 241 | } 242 | 243 | decrypt(str, key) { 244 | return CryptoJS.AES.decrypt(str, CryptoJS.enc.Utf8.parse(key), { 245 | iv: CryptoJS.enc.Utf8.parse('9791027341711819'), 246 | mode: CryptoJS.mode.CBC, 247 | padding: CryptoJS.pad.Pkcs7, 248 | }).toString(CryptoJS.enc.Utf8); 249 | } 250 | 251 | translateFlow(value) { 252 | const unit = [ 253 | {unit: 'G', value: 1024, code: '05'}, 254 | {unit: 'M', value: 1, code: '04'}, 255 | ]; 256 | const data = {unit: '', ...value}; 257 | unit.forEach((item) => { 258 | if (value.code === item.code) { 259 | data.unit = item.unit; 260 | data.value = 261 | Math.floor(parseFloat(data.value) * item.value * 100) / 100; 262 | } 263 | }); 264 | data.value = parseInt(data.value); 265 | return data; 266 | } 267 | 268 | async smallHeader(stack) { 269 | const headerStack = stack.addStack(); 270 | headerStack.addSpacer(); 271 | const logo = headerStack.addImage(await this.$request.get(this.logo, 'IMG')); 272 | logo.imageSize = new Size(402 * this.logoScale, 125 * this.logoScale); 273 | headerStack.addSpacer(); 274 | stack.addSpacer(); 275 | 276 | const feeStack = stack.addStack(); 277 | feeStack.centerAlignContent(); 278 | feeStack.addSpacer(); 279 | const feeValue = feeStack.addText(`${this.fee.number}`); 280 | feeValue.font = Font.mediumRoundedSystemFont(this.feeTextSize); 281 | feeValue.textColor = this.widgetColor; 282 | feeStack.addSpacer(); 283 | stack.addSpacer(); 284 | } 285 | 286 | textLayout(stack, data) { 287 | const rowStack = stack.addStack(); 288 | rowStack.centerAlignContent(); 289 | const icon = SFSymbol.named(data.icon); 290 | icon.applyHeavyWeight(); 291 | let iconElement = rowStack.addImage(icon.image); 292 | iconElement.imageSize = new Size(this.textSize, this.textSize); 293 | iconElement.tintColor = data.iconColor; 294 | rowStack.addSpacer(4); 295 | let title = rowStack.addText(data.title); 296 | rowStack.addSpacer(); 297 | let number = rowStack.addText(data.number + data.unit); 298 | ;[title, number].map(t => t.textColor = this.widgetColor); 299 | ;[title, number].map(t => t.font = Font.systemFont(this.textSize)); 300 | } 301 | 302 | async mediumCell(canvas, stack, data, color, fee = false, percent) { 303 | const bg = new LinearGradient() 304 | bg.locations = [0, 1] 305 | bg.colors = [ 306 | new Color(color, 0.03), 307 | new Color(color, 0.1) 308 | ] 309 | const dataStack = stack.addStack(); 310 | dataStack.backgroundGradient = bg; 311 | dataStack.cornerRadius = 20; 312 | dataStack.layoutVertically(); 313 | dataStack.addSpacer(); 314 | 315 | const topStack = dataStack.addStack(); 316 | topStack.addSpacer(); 317 | await this.imageCell(canvas, topStack, data, fee, percent); 318 | topStack.addSpacer(); 319 | 320 | if (fee) { 321 | dataStack.addSpacer(10); 322 | const updateStack = dataStack.addStack(); 323 | updateStack.addSpacer(); 324 | updateStack.centerAlignContent(); 325 | const updataIcon = SFSymbol.named('arrow.2.circlepath'); 326 | updataIcon.applyHeavyWeight(); 327 | const updateImg = updateStack.addImage(updataIcon.image); 328 | updateImg.tintColor = new Color(color, 0.6); 329 | updateImg.imageSize = new Size(10, 10); 330 | updateStack.addSpacer(3); 331 | const updateText = updateStack.addText(`${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`) 332 | updateText.font = Font.mediumSystemFont(10); 333 | updateText.textColor = new Color(color, 0.6); 334 | updateStack.addSpacer(); 335 | } 336 | 337 | dataStack.addSpacer(); 338 | 339 | const numberStack = dataStack.addStack(); 340 | numberStack.addSpacer(); 341 | const number = numberStack.addText(`${data.number} ${data.en}`); 342 | number.font = Font.semiboldSystemFont(15); 343 | numberStack.addSpacer(); 344 | 345 | dataStack.addSpacer(3); 346 | 347 | const titleStack = dataStack.addStack(); 348 | titleStack.addSpacer(); 349 | const title = titleStack.addText(data.title); 350 | title.font = Font.mediumSystemFont(11); 351 | title.textOpacity = 0.7; 352 | titleStack.addSpacer(); 353 | 354 | dataStack.addSpacer(15); 355 | ;[title, number].map(t => t.textColor = new Color(color)); 356 | } 357 | 358 | async imageCell(canvas, stack, data, fee, percent) { 359 | const canvaStack = stack.addStack(); 360 | canvaStack.layoutVertically(); 361 | if (!fee) { 362 | this.drawArc(canvas, data.percent * 3.6, data.FGColor, data.BGColor); 363 | canvaStack.size = new Size(this.ringStackSize, this.ringStackSize); 364 | canvaStack.backgroundImage = canvas.getImage(); 365 | this.ringContent(canvaStack, data, percent); 366 | } else { 367 | canvaStack.addSpacer(10); 368 | const smallLogo = await this.$request.get(this.smallLogo, 'IMG'); 369 | const logoStack = canvaStack.addStack(); 370 | logoStack.size = new Size(35, 35); 371 | logoStack.backgroundImage = smallLogo; 372 | } 373 | } 374 | 375 | ringContent(stack, data, percent = false) { 376 | const rowIcon = stack.addStack(); 377 | rowIcon.addSpacer(); 378 | const icon = SFSymbol.named(data.icon); 379 | icon.applyHeavyWeight(); 380 | const iconElement = rowIcon.addImage(icon.image); 381 | iconElement.tintColor = this.gradient ? new Color(data.colors[1]) : data.FGColor; 382 | iconElement.imageSize = new Size(12, 12); 383 | iconElement.imageOpacity = 0.7; 384 | rowIcon.addSpacer(); 385 | 386 | stack.addSpacer(1); 387 | 388 | const rowNumber = stack.addStack(); 389 | rowNumber.addSpacer(); 390 | const number = rowNumber.addText(percent ? `${data.percent}` : `${data.number}`); 391 | number.font = percent ? Font.systemFont(this.ringTextSize - 2) : Font.mediumSystemFont(this.ringTextSize); 392 | rowNumber.addSpacer(); 393 | 394 | const rowUnit = stack.addStack(); 395 | rowUnit.addSpacer(); 396 | const unit = rowUnit.addText(percent ? '%' : data.unit); 397 | unit.font = Font.boldSystemFont(8); 398 | unit.textOpacity = 0.5; 399 | rowUnit.addSpacer(); 400 | 401 | if (percent) { 402 | if (this.gradient) { 403 | ;[unit, number].map(t => t.textColor = new Color(data.colors[1])); 404 | } else { 405 | ;[unit, number].map(t => t.textColor = data.FGColor); 406 | } 407 | } else { 408 | ;[unit, number].map(t => t.textColor = this.widgetColor); 409 | } 410 | } 411 | 412 | makeCanvas() { 413 | const canvas = new DrawContext(); 414 | canvas.opaque = false; 415 | canvas.respectScreenScale = true; 416 | canvas.size = new Size(this.canvSize, this.canvSize); 417 | return canvas; 418 | } 419 | 420 | sinDeg(deg) { 421 | return Math.sin((deg * Math.PI) / 180); 422 | } 423 | 424 | cosDeg(deg) { 425 | return Math.cos((deg * Math.PI) / 180); 426 | } 427 | 428 | drawArc(canvas, deg, fillColor, strokeColor) { 429 | let ctr = new Point(this.canvSize / 2, this.canvSize / 2); 430 | let bgx = ctr.x - this.canvRadius; 431 | let bgy = ctr.y - this.canvRadius; 432 | let bgd = 2 * this.canvRadius; 433 | let bgr = new Rect(bgx, bgy, bgd, bgd) 434 | 435 | canvas.setStrokeColor(strokeColor); 436 | canvas.setLineWidth(this.canvWidth); 437 | canvas.strokeEllipse(bgr); 438 | 439 | for (let t = 0; t < deg; t++) { 440 | let rect_x = ctr.x + this.canvRadius * this.sinDeg(t) - this.canvWidth / 2; 441 | let rect_y = ctr.y - this.canvRadius * this.cosDeg(t) - this.canvWidth / 2; 442 | let rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); 443 | 444 | canvas.setFillColor(this.gradient ? new Color(fillColor[t]) : fillColor); 445 | canvas.setStrokeColor(strokeColor) 446 | canvas.fillEllipse(rect_r); 447 | } 448 | } 449 | 450 | arrColor() { 451 | let colorArr = [['#FFF000', '#E62490'], ['#FDEB71', '#F8D800'], ['#ABDCFF', '#0396FF'], ['#FEB692', '#EA5455'], ['#FEB692', '#EA5455'], ['#CE9FFC', '#7367F0'], ['#90F7EC', '#32CCBC'], ['#FFF6B7', '#F6416C'], ['#E2B0FF', '#9F44D3'], ['#F97794', '#F072B6'], ['#FCCF31', '#F55555'], ['#5EFCE8', '#736EFE'], ['#FAD7A1', '#E96D71'], ['#FFFF1C', '#00C3FF'], ['#FEC163', '#DE4313'], ['#F6CEEC', '#D939CD'], ['#FDD819', '#E80505'], ['#FFF3B0', '#CA26FF'], ['#2AFADF', '#4C83FF'], ['#EECDA3', '#EF629F'], ['#C2E59C', '#64B3F4'], ['#FFF886', '#F072B6'], ['#F5CBFF', '#C346C2'], ['#FFF720', '#3CD500'], ['#EE9AE5', '#5961F9'], ['#FFC371', '#FF5F6D'], ['#FFD3A5', '#FD6585'], ['#C2FFD8', '#465EFB'], ['#FFC600', '#FD6E6A'], ['#FFC600', '#FD6E6A'], ['#92FE9D', '#00C9FF'], ['#FFDDE1', '#EE9CA7'], ['#F0FF00', '#58CFFB'], ['#FFE985', '#FA742B'], ['#72EDF2', '#5151E5'], ['#F6D242', '#FF52E5'], ['#F9D423', '#FF4E50'], ['#3C8CE7', '#00EAFF'], ['#FCFF00', '#FFA8A8'], ['#FF96F9', '#C32BAC'], ['#D0E6A5', '#FFDD94'], ['#FFDD94', '#FA897B'], ['#FFCC4B', '#FF7D58'], ['#D0E6A5', '#86E3CE'], ['#F0D5B6', '#F16238'], ['#F8EC70', '#F9C708'], ['#C4E86B', '#00BCB4'], ['#FFC446', '#FA0874'], ['#E1EE32', '#FFB547'], ['#FFD804', '#2ACCC8'], ['#E9A6D2', '#E9037B'], ['#F8EC70', '#49E2F6'], ['#A2F8CD', '#A2F852'], ['#49E2F6', '#A2F8CD'], ['#FDEFE2', '#FE214F'], ['#F8EC70', '#A2F8CD'], ['#F8EC70', '#49E2F6'], ['#B7FFE4', '#E4B7FF'], ['#FFB7D1', '#E4B7FF'], ['#D0E6A5', '#86E3CE'], ['#E8E965', '#64C5C7']]; 452 | let colors = colorArr[Math.floor(Math.random() * colorArr.length)]; 453 | return colors; 454 | } 455 | 456 | gradientColor(colors, step) { 457 | var startRGB = this.colorToRgb(colors[0]), 458 | startR = startRGB[0], 459 | startG = startRGB[1], 460 | startB = startRGB[2]; 461 | 462 | var endRGB = this.colorToRgb(colors[1]), 463 | endR = endRGB[0], 464 | endG = endRGB[1], 465 | endB = endRGB[2]; 466 | 467 | var sR = (endR - startR) / step, 468 | sG = (endG - startG) / step, 469 | sB = (endB - startB) / step; 470 | 471 | var colorArr = []; 472 | for (var i = 0;i < step; i++) { 473 | var hex = this.colorToHex('rgb(' + parseInt((sR * i + startR)) + ',' + parseInt((sG * i + startG)) + ',' + parseInt((sB * i + startB)) + ')'); 474 | colorArr.push(hex); 475 | } 476 | return colorArr; 477 | } 478 | 479 | colorToRgb(sColor) { 480 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; 481 | var sColor = sColor.toLowerCase(); 482 | if (sColor && reg.test(sColor)) { 483 | if (sColor.length === 4) { 484 | var sColorNew = "#"; 485 | for (var i = 1; i < 4; i += 1) { 486 | sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); 487 | } 488 | sColor = sColorNew; 489 | } 490 | var sColorChange = []; 491 | for (var i = 1; i < 7; i += 2) { 492 | sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2))); 493 | } 494 | return sColorChange; 495 | } else { 496 | return sColor; 497 | } 498 | }; 499 | 500 | colorToHex(rgb) { 501 | var _this = rgb; 502 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; 503 | if (/^(rgb|RGB)/.test(_this)) { 504 | var aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(","); 505 | var strHex = "#"; 506 | for (var i = 0; i < aColor.length; i++) { 507 | var hex = Number(aColor[i]).toString(16); 508 | hex = hex.length < 2 ? 0 + '' + hex : hex; 509 | if (hex === "0") { 510 | hex += hex; 511 | } 512 | strHex += hex; 513 | } 514 | if (strHex.length !== 7) { 515 | strHex = _this; 516 | } 517 | 518 | return strHex; 519 | } else if (reg.test(_this)) { 520 | var aNum = _this.replace(/#/,"").split(""); 521 | if (aNum.length === 6) { 522 | return _this; 523 | } else if (aNum.length === 3) { 524 | var numHex = "#"; 525 | for (var i = 0; i < aNum.length; i+=1) { 526 | numHex += (aNum[i] + aNum[i]); 527 | } 528 | return numHex; 529 | } 530 | } else { 531 | return _this; 532 | } 533 | } 534 | 535 | renderSmall = async (w) => { 536 | w.setPadding(this.smallPadding, this.smallPadding, this.smallPadding, this.smallPadding); 537 | await this.smallHeader(w); 538 | const bodyStack = w.addStack(); 539 | bodyStack.layoutVertically(); 540 | if (this.widgetParam == "1"){ 541 | this.textLayout(bodyStack, this.flow); 542 | bodyStack.addSpacer(7); 543 | this.textLayout(bodyStack, this.voice); 544 | bodyStack.addSpacer(7); 545 | this.textLayout(bodyStack, this.point); 546 | } else { 547 | const canvas = this.makeCanvas(); 548 | const ringStack = bodyStack.addStack(); 549 | this.imageCell(canvas, ringStack, this.flow); 550 | ringStack.addSpacer(); 551 | this.imageCell(canvas, ringStack, this.voice); 552 | } 553 | return w; 554 | }; 555 | 556 | renderMedium = async (w) => { 557 | w.setPadding(this.padding, this.padding, this.padding, this.padding); 558 | const canvas = this.makeCanvas(); 559 | const bodyStack = w.addStack(); 560 | await this.mediumCell(canvas, bodyStack, this.fee, '0080CB', true); 561 | bodyStack.addSpacer(this.padding); 562 | await this.mediumCell(canvas, bodyStack, this.flow, this.flowColorHex, false, true); 563 | bodyStack.addSpacer(this.padding); 564 | await this.mediumCell(canvas, bodyStack, this.voice, this.voiceColorHex, false,true); 565 | return w; 566 | }; 567 | 568 | renderLarge = async (w) => { 569 | w.addText('暂不支持') 570 | return w; 571 | }; 572 | 573 | Run() { 574 | if (config.runsInApp) { 575 | const widgetInitConfig = { 576 | getfee: 'chavy_getfee_cmcc', 577 | autologin: 'chavy_autologin_cmcc', 578 | }; 579 | this.registerAction('颜色配置', async () => { 580 | await this.setAlertInput( 581 | `${this.name}颜色配置`, 582 | '进度条颜色|底圈颜色\n底圈颜色留空将跟随进度条颜色并淡显', 583 | { 584 | gradient: '开启渐变进度条?缺省:false', 585 | step1: '流量进度条颜色', 586 | step2: '语音进度条颜色', 587 | inner1: '流量进度条底圈颜色', 588 | inner2: '语音进度条底圈颜色', 589 | }, 590 | ); 591 | }); 592 | this.registerAction('尺寸设置', async () => { 593 | await this.setAlertInput( 594 | `${this.name}尺寸设置`, 595 | '进度条大小|文字大小', 596 | { 597 | logoScale: '小组件logo缩放,缺省:0.24', 598 | ringStackSize: '圆环大小,缺省:61', 599 | ringTextSize: '圆环中心文字大小,缺省:14', 600 | feeTextSize: '话费文字大小,缺省:21', 601 | textSize: '文字模式下文字大小,缺省:13', 602 | smallPadding: '小尺寸组件边距,缺省:16', 603 | padding: '中尺寸组件边距,缺省:10', 604 | }, 605 | ); 606 | }); 607 | this.registerAction('账号设置', async () => { 608 | await this.setAlertInput( 609 | `${this.name}账号`, 610 | '读取 BoxJS 缓存信息', 611 | widgetInitConfig, 612 | ); 613 | }); 614 | this.registerAction('代理缓存', async () => { 615 | await this.setCacheBoxJSData(widgetInitConfig); 616 | }); 617 | this.registerAction('基础设置', this.setWidgetConfig); 618 | } 619 | 620 | try { 621 | const { 622 | getfee, 623 | autologin, 624 | step1, 625 | step2, 626 | inner1, 627 | inner2, 628 | logoScale, 629 | ringStackSize, 630 | ringTextSize, 631 | feeTextSize, 632 | textSize, 633 | smallPadding, 634 | padding, 635 | gradient, 636 | } = this.settings; 637 | this.getfee = JSON.parse(getfee || this.getfee); 638 | this.autologin = JSON.parse(autologin || this.autologin); 639 | 640 | this.gradient = gradient === 'true' ? true : this.gradient; 641 | this.flowColorHex = step1 ? step1 : this.flowColorHex; 642 | this.voiceColorHex = step2 ? step2 : this.voiceColorHex; 643 | this.flow.BGColor = inner1 ? new Color(inner1) : new Color(this.flowColorHex, 0.2); 644 | this.voice.BGColor = inner2 ? new Color(inner2) : new Color(this.voiceColorHex, 0.2); 645 | this.flow.FGColor = new Color(this.flowColorHex); 646 | this.voice.FGColor = new Color(this.voiceColorHex); 647 | 648 | this.logoScale = logoScale ? parseFloat(logoScale) : this.logoScale; 649 | this.ringStackSize = ringStackSize ? parseFloat(ringStackSize) : this.ringStackSize; 650 | this.ringTextSize = ringTextSize ? parseFloat(ringTextSize) : this.ringTextSize; 651 | this.feeTextSize = feeTextSize ? parseFloat(feeTextSize) : this.feeTextSize; 652 | this.textSize = textSize ? parseFloat(textSize) : this.textSize; 653 | this.smallPadding = smallPadding ? parseFloat(smallPadding) : this.smallPadding; 654 | this.padding = padding ? parseFloat(padding) : this.padding; 655 | 656 | if (this.gradient) { 657 | this.flow.colors = this.arrColor(); 658 | this.voice.colors = this.arrColor(); 659 | this.flow.BGColor = inner1 ? new Color(inner1) : new Color(this.flow.colors[1], 0.2); 660 | this.voice.BGColor = inner2 ? new Color(inner2) : new Color(this.voice.colors[1], 0.2); 661 | this.flow.FGColor = this.gradientColor(this.flow.colors, 360); 662 | this.voice.FGColor = this.gradientColor(this.voice.colors, 360); 663 | this.flowColorHex = this.flow.colors[1]; 664 | this.voiceColorHex = this.voice.colors[1]; 665 | } 666 | 667 | } catch (e) { 668 | console.log(e); 669 | } 670 | } 671 | 672 | async render() { 673 | await this.init(); 674 | const widget = new ListWidget(); 675 | await this.getWidgetBackgroundImage(widget); 676 | if (this.widgetFamily === 'medium') { 677 | return await this.renderMedium(widget); 678 | } else if (this.widgetFamily === 'large') { 679 | return await this.renderLarge(widget); 680 | } else { 681 | return await this.renderSmall(widget); 682 | } 683 | } 684 | } 685 | 686 | await Runing(Widget, args.widgetParameter, false); 687 | -------------------------------------------------------------------------------- /scripts/ChinaTelecom_2021.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: deep-blue; icon-glyph: mobile-alt; 4 | // Script: 2Ya & 脑瓜 5 | // 电报群:https://t.me/Scriptable_JS @anker1209 6 | // 该脚本小尺寸组件支持两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式 7 | // 渐变进度条为试验性功能,默认关闭 8 | // version:1.1.0 9 | // update:2021/04/02 10 | 11 | if (typeof require === 'undefined') require = importModule; 12 | const {DmYY, Runing} = require('./DmYY'); 13 | 14 | class Widget extends DmYY { 15 | constructor(arg) { 16 | super(arg); 17 | this.name = '中国电信'; 18 | this.en = 'ChinaTelecom_2021'; 19 | this.logo = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/fe69a7a3-a0e2-4bf4-bab2-f11fd4b91d7d.png'; 20 | this.smallLogo = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/0b27cf6d-6b15-42bd-9f66-1b2c668ec5eb.png'; 21 | this.Run(); 22 | } 23 | cookie = ''; // 推荐使用Boxjs代理缓存,若无请自行手动抓包后在此输入中国电信cookie数据或运行脚本-->账号设置-->手动输入。 24 | widgetParam = args.widgetParameter; 25 | 26 | gradient = false; 27 | usedFlow = false; 28 | 29 | flowColorHex = 'FF6620'; 30 | voiceColorHex = '78C100'; 31 | 32 | ringStackSize = 61; 33 | ringTextSize = 14; 34 | feeTextSize = 21; 35 | textSize = 13; 36 | smallPadding = 16; 37 | padding = 10; 38 | logoScale = 0.24; 39 | 40 | canvSize = 178; 41 | canvWidth = 18; 42 | canvRadius = 80; 43 | 44 | format = (str) => { 45 | return parseInt(str) >= 10 ? str : `0${str}`; 46 | }; 47 | 48 | date = new Date(); 49 | arrUpdateTime = [ 50 | this.format(this.date.getMonth() + 1), 51 | this.format(this.date.getDate()), 52 | this.format(this.date.getHours()), 53 | this.format(this.date.getMinutes()), 54 | ]; 55 | 56 | fee = { 57 | title: '话费余额', 58 | number: 0, 59 | unit: '元', 60 | en: '¥', 61 | }; 62 | 63 | flow = { 64 | percent: 0, 65 | max: 40, 66 | title: '可用流量', 67 | number: 0, 68 | unit: 'MB', 69 | en: 'MB', 70 | icon: 'antenna.radiowaves.left.and.right', 71 | iconColor: new Color('1ab6f8'), 72 | FGColor: new Color(this.flowColorHex), 73 | BGColor: new Color(this.flowColorHex, 0.2), 74 | colors: [], 75 | }; 76 | 77 | voice = { 78 | percent: 0, 79 | title: '语音剩余', 80 | number: 0, 81 | unit: '分钟', 82 | en: 'MIN', 83 | icon: 'phone.fill', 84 | iconColor: new Color('30d15b'), 85 | FGColor: new Color(this.voiceColorHex), 86 | BGColor: new Color(this.voiceColorHex, 0.2), 87 | colors: [], 88 | }; 89 | 90 | point = { 91 | title: '更新时间', 92 | number: `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, 93 | unit: '', 94 | icon: 'arrow.2.circlepath', 95 | iconColor: new Color('fc6d6d'), 96 | } 97 | 98 | options = { 99 | headers: { 100 | cookie: '', 101 | }, 102 | method: 'POST', 103 | }; 104 | 105 | fetchUrl = { 106 | detail: 'https://e.189.cn/store/user/package_detail.do', 107 | balance: 'https://e.189.cn/store/user/balance_new.do', 108 | }; 109 | 110 | init = async () => { 111 | try { 112 | await this.getData(); 113 | } catch (e) { 114 | console.log(e); 115 | } 116 | }; 117 | 118 | formatFlow(number) { 119 | const n = number / 1024; 120 | if (n < 1024) { 121 | return {count: n.toFixed(2), unit: 'MB'}; 122 | } 123 | return {count: (n / 1024).toFixed(2), unit: 'GB'}; 124 | } 125 | 126 | unlimitUser(flow) { 127 | const usedFlow = this.formatFlow(flow); 128 | this.flow.title = '已用流量'; 129 | this.flow.number = usedFlow.count; 130 | this.flow.unit = usedFlow.unit; 131 | this.flow.en = usedFlow.unit; 132 | if (this.flow.unit === 'GB') { 133 | this.flow.percent = (100 - (this.flow.number / (this.flow.max || 40)) * 100).toFixed(2); 134 | } else { 135 | this.flow.percent = (100 - (this.flow.number / ((this.flow.max || 40) * 1024)) * 100).toFixed(2); 136 | } 137 | } 138 | 139 | getData = async () => { 140 | const detail = await this.http({ 141 | url: this.fetchUrl.detail, 142 | ...this.options, 143 | }); 144 | console.log(detail); 145 | const balance = await this.http({ 146 | url: this.fetchUrl.balance, 147 | ...this.options, 148 | }); 149 | 150 | if (detail.result === 0) { 151 | // 套餐分钟数 152 | if (detail.voiceBalance && detail.voiceAmount) { 153 | this.voice.percent = ((Number(detail.voiceBalance) / Number(detail.voiceAmount)) * 100).toFixed(2); 154 | this.voice.number = detail.voiceBalance; 155 | } else { 156 | detail.items.forEach((data) => { 157 | if (data.offerType == 21) { 158 | data.items.forEach((item) => { 159 | if (item.unitTypeId === '1') { 160 | if (item.ratableAmount !== '0' && item.balanceAmount !== '0') { 161 | this.voice.percent = ((Number(item.balanceAmount) / Number(item.ratableAmount)) * 100).toFixed(2); 162 | this.voice.number = item.balanceAmount; 163 | } 164 | } 165 | }); 166 | } 167 | }); 168 | } 169 | if (!detail.number && !detail.total) { 170 | detail.items.forEach((data) => { 171 | if (data.offerType !== 19) { 172 | data.items.forEach((item) => { 173 | if (item.unitTypeId === '3') { 174 | if (item.usageAmount !== '0' && item.balanceAmount !== '0') { 175 | this.flow.percent = ((item.balanceAmount / (item.ratableAmount || 1)) * 100).toFixed(2); 176 | const flow = this.formatFlow(item.balanceAmount); 177 | this.flow.number = flow.count; 178 | this.flow.unit = flow.unit; 179 | this.flow.en = flow.unit; 180 | } 181 | if (data.offerType == 21 && item.ratableAmount == '0') { 182 | this.unlimitUser(item.usageAmount); 183 | } 184 | } 185 | }); 186 | } 187 | }); 188 | } else { 189 | if (this.usedFlow) { 190 | this.unlimitUser(detail.used); 191 | } else { 192 | this.flow.percent = ((detail.balance / (detail.total || 1)) * 100).toFixed(2); 193 | const flow = this.formatFlow(detail.balance); 194 | this.flow.number = flow.count; 195 | this.flow.unit = flow.unit; 196 | this.flow.en = flow.unit; 197 | } 198 | } 199 | 200 | } 201 | if (balance.result === 0) { 202 | // 余额 203 | this.fee.number = parseFloat(parseInt(balance.totalBalanceAvailable) / 100).toFixed(2)}; 204 | }; 205 | 206 | async smallHeader(stack) { 207 | const headerStack = stack.addStack(); 208 | headerStack.addSpacer(); 209 | const logo = headerStack.addImage(await this.$request.get(this.logo, 'IMG')); 210 | logo.imageSize = new Size(455 * this.logoScale, 125 * this.logoScale); 211 | headerStack.addSpacer(); 212 | stack.addSpacer(); 213 | 214 | const feeStack = stack.addStack(); 215 | feeStack.centerAlignContent(); 216 | feeStack.addSpacer(); 217 | const feeValue = feeStack.addText(`${this.fee.number}`); 218 | feeValue.font = Font.mediumRoundedSystemFont(this.feeTextSize); 219 | feeValue.textColor = this.widgetColor; 220 | feeStack.addSpacer(); 221 | stack.addSpacer(); 222 | } 223 | 224 | textLayout(stack, data) { 225 | const rowStack = stack.addStack(); 226 | rowStack.centerAlignContent(); 227 | const icon = SFSymbol.named(data.icon); 228 | icon.applyHeavyWeight(); 229 | let iconElement = rowStack.addImage(icon.image); 230 | iconElement.imageSize = new Size(this.textSize, this.textSize); 231 | iconElement.tintColor = data.iconColor; 232 | rowStack.addSpacer(4); 233 | let title = rowStack.addText(data.title); 234 | rowStack.addSpacer(); 235 | let number = rowStack.addText(data.number + data.unit); 236 | ;[title, number].map(t => t.textColor = this.widgetColor); 237 | ;[title, number].map(t => t.font = Font.systemFont(this.textSize)); 238 | } 239 | 240 | async mediumCell(canvas, stack, data, color, fee = false, percent) { 241 | const bg = new LinearGradient() 242 | bg.locations = [0, 1] 243 | bg.colors = [ 244 | new Color(color, 0.03), 245 | new Color(color, 0.1) 246 | ] 247 | const dataStack = stack.addStack(); 248 | dataStack.backgroundGradient = bg; 249 | dataStack.cornerRadius = 20; 250 | dataStack.layoutVertically(); 251 | dataStack.addSpacer(); 252 | 253 | const topStack = dataStack.addStack(); 254 | topStack.addSpacer(); 255 | await this.imageCell(canvas, topStack, data, fee, percent); 256 | topStack.addSpacer(); 257 | 258 | if (fee) { 259 | dataStack.addSpacer(10); 260 | const updateStack = dataStack.addStack(); 261 | updateStack.addSpacer(); 262 | updateStack.centerAlignContent(); 263 | const updataIcon = SFSymbol.named('arrow.2.circlepath'); 264 | updataIcon.applyHeavyWeight(); 265 | const updateImg = updateStack.addImage(updataIcon.image); 266 | updateImg.tintColor = new Color(color, 0.6); 267 | updateImg.imageSize = new Size(10, 10); 268 | updateStack.addSpacer(3); 269 | const updateText = updateStack.addText(`${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`) 270 | updateText.font = Font.mediumSystemFont(10); 271 | updateText.textColor = new Color(color, 0.6); 272 | updateStack.addSpacer(); 273 | } 274 | 275 | dataStack.addSpacer(); 276 | 277 | const numberStack = dataStack.addStack(); 278 | numberStack.addSpacer(); 279 | const number = numberStack.addText(`${data.number} ${data.en}`); 280 | number.font = Font.semiboldSystemFont(15); 281 | numberStack.addSpacer(); 282 | 283 | dataStack.addSpacer(3); 284 | 285 | const titleStack = dataStack.addStack(); 286 | titleStack.addSpacer(); 287 | const title = titleStack.addText(data.title); 288 | title.font = Font.mediumSystemFont(11); 289 | title.textOpacity = 0.7; 290 | titleStack.addSpacer(); 291 | 292 | dataStack.addSpacer(15); 293 | ;[title, number].map(t => t.textColor = new Color(color)); 294 | } 295 | 296 | async imageCell(canvas, stack, data, fee, percent) { 297 | const canvaStack = stack.addStack(); 298 | canvaStack.layoutVertically(); 299 | if (!fee) { 300 | this.drawArc(canvas, data.percent * 3.6, data.FGColor, data.BGColor); 301 | canvaStack.size = new Size(this.ringStackSize, this.ringStackSize); 302 | canvaStack.backgroundImage = canvas.getImage(); 303 | this.ringContent(canvaStack, data, percent); 304 | } else { 305 | canvaStack.addSpacer(10); 306 | const smallLogo = await this.$request.get(this.smallLogo, 'IMG'); 307 | const logoStack = canvaStack.addStack(); 308 | logoStack.size = new Size(30, 30); 309 | logoStack.backgroundImage = smallLogo; 310 | } 311 | } 312 | 313 | ringContent(stack, data, percent = false) { 314 | const rowIcon = stack.addStack(); 315 | rowIcon.addSpacer(); 316 | const icon = SFSymbol.named(data.icon); 317 | icon.applyHeavyWeight(); 318 | const iconElement = rowIcon.addImage(icon.image); 319 | iconElement.tintColor = this.gradient ? new Color(data.colors[1]) : data.FGColor; 320 | iconElement.imageSize = new Size(12, 12); 321 | iconElement.imageOpacity = 0.7; 322 | rowIcon.addSpacer(); 323 | 324 | stack.addSpacer(1); 325 | 326 | const rowNumber = stack.addStack(); 327 | rowNumber.addSpacer(); 328 | const number = rowNumber.addText(percent ? `${data.percent}` : `${data.number}`); 329 | number.font = percent ? Font.systemFont(this.ringTextSize - 2) : Font.mediumSystemFont(this.ringTextSize); 330 | rowNumber.addSpacer(); 331 | 332 | const rowUnit = stack.addStack(); 333 | rowUnit.addSpacer(); 334 | const unit = rowUnit.addText(percent ? '%' : data.unit); 335 | unit.font = Font.boldSystemFont(8); 336 | unit.textOpacity = 0.5; 337 | rowUnit.addSpacer(); 338 | 339 | if (percent) { 340 | if (this.gradient) { 341 | ;[unit, number].map(t => t.textColor = new Color(data.colors[1])); 342 | } else { 343 | ;[unit, number].map(t => t.textColor = data.FGColor); 344 | } 345 | } else { 346 | ;[unit, number].map(t => t.textColor = this.widgetColor); 347 | } 348 | } 349 | 350 | makeCanvas() { 351 | const canvas = new DrawContext(); 352 | canvas.opaque = false; 353 | canvas.respectScreenScale = true; 354 | canvas.size = new Size(this.canvSize, this.canvSize); 355 | return canvas; 356 | } 357 | 358 | sinDeg(deg) { 359 | return Math.sin((deg * Math.PI) / 180); 360 | } 361 | 362 | cosDeg(deg) { 363 | return Math.cos((deg * Math.PI) / 180); 364 | } 365 | 366 | drawArc(canvas, deg, fillColor, strokeColor) { 367 | let ctr = new Point(this.canvSize / 2, this.canvSize / 2); 368 | let bgx = ctr.x - this.canvRadius; 369 | let bgy = ctr.y - this.canvRadius; 370 | let bgd = 2 * this.canvRadius; 371 | let bgr = new Rect(bgx, bgy, bgd, bgd) 372 | 373 | canvas.setStrokeColor(strokeColor); 374 | canvas.setLineWidth(this.canvWidth); 375 | canvas.strokeEllipse(bgr); 376 | 377 | for (let t = 0; t < deg; t++) { 378 | let rect_x = ctr.x + this.canvRadius * this.sinDeg(t) - this.canvWidth / 2; 379 | let rect_y = ctr.y - this.canvRadius * this.cosDeg(t) - this.canvWidth / 2; 380 | let rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); 381 | 382 | canvas.setFillColor(this.gradient ? new Color(fillColor[t]) : fillColor); 383 | canvas.setStrokeColor(strokeColor) 384 | canvas.fillEllipse(rect_r); 385 | } 386 | } 387 | 388 | arrColor() { 389 | let colorArr = [['#FFF000', '#E62490'], ['#FDEB71', '#F8D800'], ['#ABDCFF', '#0396FF'], ['#FEB692', '#EA5455'], ['#FEB692', '#EA5455'], ['#CE9FFC', '#7367F0'], ['#90F7EC', '#32CCBC'], ['#FFF6B7', '#F6416C'], ['#E2B0FF', '#9F44D3'], ['#F97794', '#F072B6'], ['#FCCF31', '#F55555'], ['#5EFCE8', '#736EFE'], ['#FAD7A1', '#E96D71'], ['#FFFF1C', '#00C3FF'], ['#FEC163', '#DE4313'], ['#F6CEEC', '#D939CD'], ['#FDD819', '#E80505'], ['#FFF3B0', '#CA26FF'], ['#2AFADF', '#4C83FF'], ['#EECDA3', '#EF629F'], ['#C2E59C', '#64B3F4'], ['#FFF886', '#F072B6'], ['#F5CBFF', '#C346C2'], ['#FFF720', '#3CD500'], ['#EE9AE5', '#5961F9'], ['#FFC371', '#FF5F6D'], ['#FFD3A5', '#FD6585'], ['#C2FFD8', '#465EFB'], ['#FFC600', '#FD6E6A'], ['#FFC600', '#FD6E6A'], ['#92FE9D', '#00C9FF'], ['#FFDDE1', '#EE9CA7'], ['#F0FF00', '#58CFFB'], ['#FFE985', '#FA742B'], ['#72EDF2', '#5151E5'], ['#F6D242', '#FF52E5'], ['#F9D423', '#FF4E50'], ['#3C8CE7', '#00EAFF'], ['#FCFF00', '#FFA8A8'], ['#FF96F9', '#C32BAC'], ['#D0E6A5', '#FFDD94'], ['#FFDD94', '#FA897B'], ['#FFCC4B', '#FF7D58'], ['#D0E6A5', '#86E3CE'], ['#F0D5B6', '#F16238'], ['#F8EC70', '#F9C708'], ['#C4E86B', '#00BCB4'], ['#FFC446', '#FA0874'], ['#E1EE32', '#FFB547'], ['#FFD804', '#2ACCC8'], ['#E9A6D2', '#E9037B'], ['#F8EC70', '#49E2F6'], ['#A2F8CD', '#A2F852'], ['#49E2F6', '#A2F8CD'], ['#FDEFE2', '#FE214F'], ['#F8EC70', '#A2F8CD'], ['#F8EC70', '#49E2F6'], ['#B7FFE4', '#E4B7FF'], ['#FFB7D1', '#E4B7FF'], ['#D0E6A5', '#86E3CE'], ['#E8E965', '#64C5C7']]; 390 | let colors = colorArr[Math.floor(Math.random() * colorArr.length)]; 391 | return colors; 392 | } 393 | 394 | gradientColor(colors, step) { 395 | var startRGB = this.colorToRgb(colors[0]), 396 | startR = startRGB[0], 397 | startG = startRGB[1], 398 | startB = startRGB[2]; 399 | 400 | var endRGB = this.colorToRgb(colors[1]), 401 | endR = endRGB[0], 402 | endG = endRGB[1], 403 | endB = endRGB[2]; 404 | 405 | var sR = (endR - startR) / step, 406 | sG = (endG - startG) / step, 407 | sB = (endB - startB) / step; 408 | 409 | var colorArr = []; 410 | for (var i = 0;i < step; i++) { 411 | var hex = this.colorToHex('rgb(' + parseInt((sR * i + startR)) + ',' + parseInt((sG * i + startG)) + ',' + parseInt((sB * i + startB)) + ')'); 412 | colorArr.push(hex); 413 | } 414 | return colorArr; 415 | } 416 | 417 | colorToRgb(sColor) { 418 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; 419 | var sColor = sColor.toLowerCase(); 420 | if (sColor && reg.test(sColor)) { 421 | if (sColor.length === 4) { 422 | var sColorNew = "#"; 423 | for (var i = 1; i < 4; i += 1) { 424 | sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); 425 | } 426 | sColor = sColorNew; 427 | } 428 | var sColorChange = []; 429 | for (var i = 1; i < 7; i += 2) { 430 | sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2))); 431 | } 432 | return sColorChange; 433 | } else { 434 | return sColor; 435 | } 436 | }; 437 | 438 | colorToHex(rgb) { 439 | var _this = rgb; 440 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; 441 | if (/^(rgb|RGB)/.test(_this)) { 442 | var aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(","); 443 | var strHex = "#"; 444 | for (var i = 0; i < aColor.length; i++) { 445 | var hex = Number(aColor[i]).toString(16); 446 | hex = hex.length < 2 ? 0 + '' + hex : hex; 447 | if (hex === "0") { 448 | hex += hex; 449 | } 450 | strHex += hex; 451 | } 452 | if (strHex.length !== 7) { 453 | strHex = _this; 454 | } 455 | 456 | return strHex; 457 | } else if (reg.test(_this)) { 458 | var aNum = _this.replace(/#/,"").split(""); 459 | if (aNum.length === 6) { 460 | return _this; 461 | } else if (aNum.length === 3) { 462 | var numHex = "#"; 463 | for (var i = 0; i < aNum.length; i+=1) { 464 | numHex += (aNum[i] + aNum[i]); 465 | } 466 | return numHex; 467 | } 468 | } else { 469 | return _this; 470 | } 471 | } 472 | 473 | renderSmall = async (w) => { 474 | w.setPadding(this.smallPadding, this.smallPadding, this.smallPadding, this.smallPadding); 475 | await this.smallHeader(w); 476 | const bodyStack = w.addStack(); 477 | bodyStack.layoutVertically(); 478 | if (this.widgetParam == "1"){ 479 | this.textLayout(bodyStack, this.flow); 480 | bodyStack.addSpacer(7); 481 | this.textLayout(bodyStack, this.voice); 482 | bodyStack.addSpacer(7); 483 | this.textLayout(bodyStack, this.point); 484 | } else { 485 | const canvas = this.makeCanvas(); 486 | const ringStack = bodyStack.addStack(); 487 | this.imageCell(canvas, ringStack, this.flow); 488 | ringStack.addSpacer(); 489 | this.imageCell(canvas, ringStack, this.voice); 490 | } 491 | return w; 492 | }; 493 | 494 | renderMedium = async (w) => { 495 | w.setPadding(this.padding, this.padding, this.padding, this.padding); 496 | const canvas = this.makeCanvas(); 497 | const bodyStack = w.addStack(); 498 | await this.mediumCell(canvas, bodyStack, this.fee, '0A4B9D', true); 499 | bodyStack.addSpacer(this.padding); 500 | await this.mediumCell(canvas, bodyStack, this.flow, this.flowColorHex, false, true); 501 | bodyStack.addSpacer(this.padding); 502 | await this.mediumCell(canvas, bodyStack, this.voice, this.voiceColorHex, false,true); 503 | return w; 504 | }; 505 | 506 | renderLarge = async (w) => { 507 | w.addText('暂不支持') 508 | return w; 509 | }; 510 | 511 | renderWebView = async () => { 512 | const webView = new WebView(); 513 | const url = 'https://e.189.cn/index.do'; 514 | await webView.loadURL(url); 515 | await webView.present(false); 516 | 517 | const request = new Request(this.fetchUrl.detail); 518 | request.method = 'POST'; 519 | const response = await request.loadJSON(); 520 | console.log(response); 521 | if (response.result === -10001) { 522 | const index = await this.generateAlert('未获取到用户信息', [ 523 | '取消', 524 | '重试', 525 | ]); 526 | if (index === 0) return; 527 | await this.renderWebView(); 528 | } else { 529 | const cookies = request.response.cookies; 530 | let cookie = []; 531 | cookie = cookies.map((item) => `${item.name}=${item.value}`); 532 | cookie = cookie.join('; '); 533 | this.settings.cookie = cookie; 534 | this.saveSettings(); 535 | } 536 | }; 537 | 538 | Run() { 539 | if (config.runsInApp) { 540 | const widgetInitConfig = {cookie: 'china_telecom_cookie'}; 541 | this.registerAction('颜色配置', async () => { 542 | await this.setAlertInput( 543 | `${this.name}颜色配置`, 544 | '进度条颜色|底圈颜色\n底圈颜色留空将跟随进度条颜色并淡显', 545 | { 546 | gradient: '是否开启渐变进度条,缺省:false', 547 | step1: '流量进度条颜色', 548 | step2: '语音进度条颜色', 549 | inner1: '流量进度条底圈颜色', 550 | inner2: '语音进度条底圈颜色', 551 | }, 552 | ); 553 | }, 'https://gitee.com/anker1209/image/raw/master/jd/colorSet.png'); 554 | this.registerAction('尺寸设置', async () => { 555 | await this.setAlertInput( 556 | `${this.name}尺寸设置`, 557 | '进度条大小|文字大小', 558 | { 559 | logoScale: '小组件logo缩放,缺省:0.24', 560 | ringStackSize: '圆环大小,缺省:61', 561 | ringTextSize: '圆环中心文字大小,缺省:14', 562 | feeTextSize: '话费文字大小,缺省:21', 563 | textSize: '文字模式下文字大小,缺省:13', 564 | smallPadding: '小尺寸组件边距,缺省:16', 565 | padding: '中尺寸组件边距,缺省:10', 566 | }, 567 | ); 568 | }, 'https://gitee.com/anker1209/image/raw/master/jd/resize.png'); 569 | this.registerAction('流量设置', async () => { 570 | await this.setAlertInput( 571 | `${this.name}流量设置`, 572 | '是否显示已用流量\n不限量或伪不限量用户可将此值设为true', 573 | { 574 | usedFlow: '是否显示已用流量,缺省:false', 575 | maxFlow: '实际流量或超限流量(GB),缺省:40', 576 | }, 577 | ); 578 | }, 'https://gitee.com/anker1209/image/raw/master/jd/flow.png'); 579 | this.registerAction("账号设置", async () => { 580 | const index = await this.generateAlert("设置账号信息", [ 581 | "网站登录", 582 | "手动输入", 583 | ]); 584 | if (index === 0) { 585 | await this.renderWebView(); 586 | } else { 587 | await this.setAlertInput("账号设置", "中国电信 cookie", { 588 | cookie: 'cookie', 589 | }); 590 | } 591 | }, 'https://gitee.com/anker1209/image/raw/master/jd/account.png'); 592 | this.registerAction('代理缓存', async () => { 593 | await this.setCacheBoxJSData(widgetInitConfig); 594 | }, 'https://gitee.com/anker1209/image/raw/master/jd/boxjs.png'); 595 | this.registerAction('基础设置', this.setWidgetConfig, 'https://gitee.com/anker1209/image/raw/master/jd/preferences.png'); 596 | } 597 | 598 | try { 599 | const { 600 | cookie, 601 | step1, 602 | step2, 603 | inner1, 604 | inner2, 605 | logoScale, 606 | ringStackSize, 607 | ringTextSize, 608 | feeTextSize, 609 | textSize, 610 | smallPadding, 611 | padding, 612 | gradient, 613 | usedFlow, 614 | maxFlow, 615 | } = this.settings; 616 | this.cookie = cookie ? cookie : this.cookie; 617 | if (this.cookie) this.options.headers.cookie = this.cookie; 618 | this.gradient = gradient === 'true' ? true : this.gradient; 619 | this.usedFlow = usedFlow === 'true' ? true : this.usedFlow; 620 | this.flowColorHex = step1 ? step1 : this.flowColorHex; 621 | this.voiceColorHex = step2 ? step2 : this.voiceColorHex; 622 | this.flow.BGColor = inner1 ? new Color(inner1) : new Color(this.flowColorHex, 0.2); 623 | this.voice.BGColor = inner2 ? new Color(inner2) : new Color(this.voiceColorHex, 0.2); 624 | this.flow.FGColor = new Color(this.flowColorHex); 625 | this.voice.FGColor = new Color(this.voiceColorHex); 626 | 627 | this.flow.max = maxFlow ? parseFloat(maxFlow) : this.flow.max; 628 | this.logoScale = logoScale ? parseFloat(logoScale) : this.logoScale; 629 | this.ringStackSize = ringStackSize ? parseFloat(ringStackSize) : this.ringStackSize; 630 | this.ringTextSize = ringTextSize ? parseFloat(ringTextSize) : this.ringTextSize; 631 | this.feeTextSize = feeTextSize ? parseFloat(feeTextSize) : this.feeTextSize; 632 | this.textSize = textSize ? parseFloat(textSize) : this.textSize; 633 | this.smallPadding = smallPadding ? parseFloat(smallPadding) : this.smallPadding; 634 | this.padding = padding ? parseFloat(padding) : this.padding; 635 | 636 | if (this.gradient) { 637 | this.flow.colors = this.arrColor(); 638 | this.voice.colors = this.arrColor(); 639 | this.flow.BGColor = inner1 ? new Color(inner1) : new Color(this.flow.colors[1], 0.2); 640 | this.voice.BGColor = inner2 ? new Color(inner2) : new Color(this.voice.colors[1], 0.2); 641 | this.flow.FGColor = this.gradientColor(this.flow.colors, 360); 642 | this.voice.FGColor = this.gradientColor(this.voice.colors, 360); 643 | this.flowColorHex = this.flow.colors[1]; 644 | this.voiceColorHex = this.voice.colors[1]; 645 | } 646 | 647 | } catch (e) { 648 | console.log(e); 649 | } 650 | } 651 | 652 | async render() { 653 | await this.init(); 654 | const widget = new ListWidget(); 655 | await this.getWidgetBackgroundImage(widget); 656 | if (this.widgetFamily === 'medium') { 657 | return await this.renderMedium(widget); 658 | } else if (this.widgetFamily === 'large') { 659 | return await this.renderLarge(widget); 660 | } else { 661 | return await this.renderSmall(widget); 662 | } 663 | } 664 | } 665 | 666 | await Runing(Widget, args.widgetParameter, false); 667 | -------------------------------------------------------------------------------- /scripts/ChinaUnicom_2021.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: red; icon-glyph: mobile-alt; 4 | // Author: 脑瓜 5 | // 电报群:https://t.me/Scriptable_JS @anker1209 6 | // 该脚本小尺寸组件支持两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式 7 | // 渐变进度条为试验性功能,默认关闭 8 | // version:2.1.0 9 | // update:2021/04/02 10 | 11 | if (typeof require === 'undefined') require = importModule; 12 | const {DmYY, Runing} = require('./DmYY'); 13 | 14 | class Widget extends DmYY { 15 | constructor(arg) { 16 | super(arg); 17 | this.name = '中国联通'; 18 | this.en = 'ChinaUnicom_2021'; 19 | this.logo = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cbbfab2c-790c-4308-b11f-299ae4311c07.png'; 20 | this.verticalLogo = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/00010042-8cc7-4931-afe2-b65ee49f6374.png'; 21 | this.Run(); 22 | } 23 | 24 | widgetParam = args.widgetParameter; 25 | cookie = ''; // 推荐使用Boxjs代理缓存,若无请自行手动抓包后在此输入中国联通cookie数据。 26 | 27 | gradient = false; 28 | 29 | flowColorHex = '12A6E4'; 30 | voiceColorHex = 'F86527'; 31 | 32 | ringStackSize = 61; 33 | ringTextSize = 14; 34 | feeTextSize = 21; 35 | textSize = 13; 36 | smallPadding = 16; 37 | padding = 10; 38 | logoScale = 0.24; 39 | 40 | canvSize = 178; 41 | canvWidth = 18; 42 | canvRadius = 80; 43 | 44 | format = (str) => { 45 | return parseInt(str) >= 10 ? str : `0${str}`; 46 | }; 47 | 48 | date = new Date(); 49 | arrUpdateTime = [ 50 | this.format(this.date.getMonth() + 1), 51 | this.format(this.date.getDate()), 52 | this.format(this.date.getHours()), 53 | this.format(this.date.getMinutes()), 54 | ]; 55 | 56 | fee = { 57 | title: '话费剩余', 58 | number: 0, 59 | unit: '元', 60 | en: '¥', 61 | }; 62 | 63 | flow = { 64 | percent: 0, 65 | title: '已用流量', 66 | number: 0, 67 | unit: 'MB', 68 | en: 'MB', 69 | icon: 'antenna.radiowaves.left.and.right', 70 | iconColor: new Color('1ab6f8'), 71 | FGColor: new Color(this.flowColorHex), 72 | BGColor: new Color(this.flowColorHex, 0.2), 73 | colors: [], 74 | }; 75 | 76 | voice = { 77 | percent: 0, 78 | title: '语音剩余', 79 | number: 0, 80 | unit: '分钟', 81 | en: 'MIN', 82 | icon: 'phone.fill', 83 | iconColor: new Color('30d15b'), 84 | FGColor: new Color(this.voiceColorHex), 85 | BGColor: new Color(this.voiceColorHex, 0.2), 86 | colors: [], 87 | }; 88 | 89 | point = { 90 | title: '剩余积分', 91 | number: 0, 92 | unit: '', 93 | icon: 'tag.fill', 94 | iconColor: new Color('fc6d6d'), 95 | } 96 | 97 | init = async () => { 98 | try { 99 | await this.getData(); 100 | } catch (e) { 101 | console.log(e); 102 | } 103 | }; 104 | 105 | async login() { 106 | const url = 'https://m.client.10010.com/dailylottery/static/textdl/userLogin?version=iphone_c@8.0200&desmobile='; 107 | try { 108 | const sign = new Request(url); 109 | sign.headers = {'cookie': this.cookie}; 110 | const signInfo = await sign.loadString(); 111 | 112 | if (signInfo.indexOf('天天抽奖') >= 0 && signInfo.indexOf('请稍后重试') < 0) { 113 | console.log('用户登录成功'); 114 | } else { 115 | console.log('用户登录失败'); 116 | } 117 | } catch (e) { 118 | console.log('用户登录失败' + e); 119 | } 120 | } 121 | 122 | async getData() { 123 | await this.login(); 124 | const url= 'https://m.client.10010.com/mobileserviceimportant/home/queryUserInfoSeven?version=iphone_c@8.0200&desmobiel=&showType=0'; 125 | 126 | try { 127 | const req = new Request(url); 128 | req.headers = {'cookie': this.cookie}; 129 | const userInfo = await req.loadJSON(); 130 | 131 | if (userInfo.code === 'Y') { 132 | console.log('获取信息成功'); 133 | console.log(userInfo.data); 134 | userInfo.data.dataList.forEach((item) => { 135 | if (item.type === 'fee') { 136 | if (item.unit ==='万元') { 137 | this.fee.number = item.number * 10000; 138 | } else { 139 | this.fee.number = item.number; 140 | this.fee.unit = item.unit; 141 | } 142 | this.fee.title = item.remainTitle; 143 | } 144 | if (item.type === 'flow') { 145 | this.flow.number = item.number; 146 | this.flow.unit = item.unit; 147 | this.flow.en = item.unit; 148 | this.flow.percent = (100 - item.persent).toFixed(2); 149 | this.flow.title = item.remainTitle; 150 | } 151 | if (item.type === 'voice') { 152 | this.voice.number = item.number; 153 | this.voice.unit = item.unit; 154 | this.voice.percent = (100 - item.persent).toFixed(2); 155 | this.voice.title = item.remainTitle; 156 | } 157 | if (item.type === 'point') { 158 | this.point.number = item.number; 159 | this.point.title = item.remainTitle; 160 | } 161 | }); 162 | } else { 163 | throw 'cookie错误/服务器维护'; 164 | } 165 | } catch (e) { 166 | console.log('获取信息失败:' + e); 167 | } 168 | } 169 | 170 | async smallHeader(stack) { 171 | const headerStack = stack.addStack(); 172 | headerStack.addSpacer(); 173 | const logo = headerStack.addImage(await this.$request.get(this.logo, 'IMG')); 174 | logo.imageSize = new Size(415 * this.logoScale, 125 * this.logoScale); 175 | headerStack.addSpacer(); 176 | stack.addSpacer(); 177 | 178 | const feeStack = stack.addStack(); 179 | feeStack.centerAlignContent(); 180 | feeStack.addSpacer(); 181 | const feeValue = feeStack.addText(`${this.fee.number}`); 182 | feeValue.font = Font.mediumRoundedSystemFont(this.feeTextSize); 183 | feeValue.textColor = this.widgetColor; 184 | feeStack.addSpacer(); 185 | stack.addSpacer(); 186 | } 187 | 188 | textLayout(stack, data) { 189 | const rowStack = stack.addStack(); 190 | rowStack.centerAlignContent(); 191 | const icon = SFSymbol.named(data.icon); 192 | icon.applyHeavyWeight(); 193 | let iconElement = rowStack.addImage(icon.image); 194 | iconElement.imageSize = new Size(this.textSize, this.textSize); 195 | iconElement.tintColor = data.iconColor; 196 | rowStack.addSpacer(4); 197 | let title = rowStack.addText(data.title); 198 | rowStack.addSpacer(); 199 | let number = rowStack.addText(data.number + data.unit); 200 | ;[title, number].map(t => t.textColor = this.widgetColor); 201 | ;[title, number].map(t => t.font = Font.systemFont(this.textSize)); 202 | } 203 | 204 | async mediumCell(canvas, stack, data, color, fee = false, percent) { 205 | const bg = new LinearGradient(); 206 | bg.locations = [0, 1]; 207 | bg.colors = [ 208 | new Color(color, 0.03), 209 | new Color(color, 0.1) 210 | ]; 211 | const dataStack = stack.addStack(); 212 | dataStack.backgroundGradient = bg; 213 | dataStack.cornerRadius = 20; 214 | dataStack.layoutVertically(); 215 | dataStack.addSpacer(); 216 | 217 | const topStack = dataStack.addStack(); 218 | topStack.addSpacer(); 219 | await this.imageCell(canvas, topStack, data, fee, percent); 220 | topStack.addSpacer(); 221 | 222 | if (fee) { 223 | dataStack.addSpacer(5); 224 | const updateStack = dataStack.addStack(); 225 | updateStack.addSpacer(); 226 | updateStack.centerAlignContent(); 227 | const updataIcon = SFSymbol.named('arrow.2.circlepath'); 228 | updataIcon.applyHeavyWeight(); 229 | const updateImg = updateStack.addImage(updataIcon.image); 230 | updateImg.tintColor = new Color(color, 0.6); 231 | updateImg.imageSize = new Size(10, 10); 232 | updateStack.addSpacer(3); 233 | const updateText = updateStack.addText(`${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`) 234 | updateText.font = Font.mediumSystemFont(10); 235 | updateText.textColor = new Color(color, 0.6); 236 | updateStack.addSpacer(); 237 | /* 238 | let tempStack = dataStack.addStack(); 239 | tempStack.layoutVertically() 240 | let tempStack1 = tempStack.addStack() 241 | tempStack1.addSpacer() 242 | let tempText1 = tempStack1.addText(this.flow.colors.toString()) 243 | tempStack1.addSpacer() 244 | let tempStack2 = tempStack.addStack() 245 | tempStack2.addSpacer() 246 | let tempText2 = tempStack2.addText(this.voice.colors.toString()) 247 | tempStack2.addSpacer() 248 | ;[tempText1, tempText2].map(t => t.font = Font.systemFont(6)); 249 | ;[tempText1, tempText2].map(t => t.textColor = updateText.textColor); 250 | */ 251 | } 252 | 253 | dataStack.addSpacer(); 254 | 255 | const numberStack = dataStack.addStack(); 256 | numberStack.addSpacer(); 257 | const number = numberStack.addText(`${data.number} ${data.en}`); 258 | number.font = Font.semiboldSystemFont(15); 259 | numberStack.addSpacer(); 260 | 261 | dataStack.addSpacer(3); 262 | 263 | const titleStack = dataStack.addStack(); 264 | titleStack.addSpacer(); 265 | const title = titleStack.addText(data.title); 266 | title.font = Font.mediumSystemFont(11); 267 | title.textOpacity = 0.7; 268 | titleStack.addSpacer(); 269 | 270 | dataStack.addSpacer(15); 271 | ;[title, number].map(t => t.textColor = new Color(color)); 272 | } 273 | 274 | async imageCell(canvas, stack, data, fee, percent) { 275 | const canvaStack = stack.addStack(); 276 | canvaStack.layoutVertically(); 277 | if (!fee) { 278 | this.drawArc(canvas, data.percent * 3.6, data.FGColor, data.BGColor); 279 | canvaStack.size = new Size(this.ringStackSize, this.ringStackSize); 280 | canvaStack.backgroundImage = canvas.getImage(); 281 | this.ringContent(canvaStack, data, percent); 282 | } else { 283 | canvaStack.addSpacer(10); 284 | const smallLogo = await this.$request.get(this.verticalLogo, 'IMG'); 285 | const logoStack = canvaStack.addStack(); 286 | logoStack.size = new Size(40, 40); 287 | logoStack.backgroundImage = smallLogo; 288 | } 289 | } 290 | 291 | ringContent(stack, data, percent = false) { 292 | const rowIcon = stack.addStack(); 293 | rowIcon.addSpacer(); 294 | const icon = SFSymbol.named(data.icon); 295 | icon.applyHeavyWeight(); 296 | const iconElement = rowIcon.addImage(icon.image); 297 | iconElement.tintColor = this.gradient ? new Color(data.colors[1]) : data.FGColor; 298 | iconElement.imageSize = new Size(12, 12); 299 | iconElement.imageOpacity = 0.7; 300 | rowIcon.addSpacer(); 301 | 302 | stack.addSpacer(1); 303 | 304 | const rowNumber = stack.addStack(); 305 | rowNumber.addSpacer(); 306 | const number = rowNumber.addText(percent ? `${data.percent}` : `${data.number}`); 307 | number.font = percent ? Font.systemFont(this.ringTextSize - 2) : Font.mediumSystemFont(this.ringTextSize); 308 | rowNumber.addSpacer(); 309 | 310 | const rowUnit = stack.addStack(); 311 | rowUnit.addSpacer(); 312 | const unit = rowUnit.addText(percent ? '%' : data.unit); 313 | unit.font = Font.boldSystemFont(8); 314 | unit.textOpacity = 0.5; 315 | rowUnit.addSpacer(); 316 | 317 | if (percent) { 318 | if (this.gradient) { 319 | ;[unit, number].map(t => t.textColor = new Color(data.colors[1])); 320 | } else { 321 | ;[unit, number].map(t => t.textColor = data.FGColor); 322 | } 323 | } else { 324 | ;[unit, number].map(t => t.textColor = this.widgetColor); 325 | } 326 | } 327 | 328 | makeCanvas() { 329 | const canvas = new DrawContext(); 330 | canvas.opaque = false; 331 | canvas.respectScreenScale = true; 332 | canvas.size = new Size(this.canvSize, this.canvSize); 333 | return canvas; 334 | } 335 | 336 | sinDeg(deg) { 337 | return Math.sin((deg * Math.PI) / 180); 338 | } 339 | 340 | cosDeg(deg) { 341 | return Math.cos((deg * Math.PI) / 180); 342 | } 343 | 344 | drawArc(canvas, deg, fillColor, strokeColor) { 345 | let ctr = new Point(this.canvSize / 2, this.canvSize / 2); 346 | let bgx = ctr.x - this.canvRadius; 347 | let bgy = ctr.y - this.canvRadius; 348 | let bgd = 2 * this.canvRadius; 349 | let bgr = new Rect(bgx, bgy, bgd, bgd) 350 | 351 | canvas.setStrokeColor(strokeColor); 352 | canvas.setLineWidth(this.canvWidth); 353 | canvas.strokeEllipse(bgr); 354 | 355 | for (let t = 0; t < deg; t++) { 356 | let rect_x = ctr.x + this.canvRadius * this.sinDeg(t) - this.canvWidth / 2; 357 | let rect_y = ctr.y - this.canvRadius * this.cosDeg(t) - this.canvWidth / 2; 358 | let rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); 359 | canvas.setFillColor(this.gradient ? new Color(fillColor[t]) : fillColor); 360 | canvas.setStrokeColor(strokeColor) 361 | canvas.fillEllipse(rect_r); 362 | } 363 | } 364 | 365 | arrColor() { 366 | let colorArr = [['#FFF000', '#E62490'], ['#ABDCFF', '#0396FF'], ['#FEB692', '#EA5455'], ['#FEB692', '#EA5455'], ['#CE9FFC', '#7367F0'], ['#90F7EC', '#32CCBC'], ['#FFF6B7', '#F6416C'], ['#E2B0FF', '#9F44D3'], ['#F97794', '#F072B6'], ['#FCCF31', '#F55555'], ['#5EFCE8', '#736EFE'], ['#FAD7A1', '#E96D71'], ['#FFFF1C', '#00C3FF'], ['#FEC163', '#DE4313'], ['#F6CEEC', '#D939CD'], ['#FDD819', '#E80505'], ['#FFF3B0', '#CA26FF'], ['#EECDA3', '#EF629F'], ['#C2E59C', '#64B3F4'], ['#FFF886', '#F072B6'], ['#F5CBFF', '#C346C2'], ['#FFF720', '#3CD500'], ['#FFC371', '#FF5F6D'], ['#FFD3A5', '#FD6585'], ['#C2FFD8', '#465EFB'], ['#FFC600', '#FD6E6A'], ['#FFC600', '#FD6E6A'], ['#92FE9D', '#00C9FF'], ['#FFDDE1', '#EE9CA7'], ['#F0FF00', '#58CFFB'], ['#FFE985', '#FA742B'], ['#72EDF2', '#5151E5'], ['#F6D242', '#FF52E5'], ['#F9D423', '#FF4E50'], ['#00EAFF', '#3C8CE7'], ['#FCFF00', '#FFA8A8'], ['#FF96F9', '#C32BAC'], ['#FFDD94', '#FA897B'], ['#FFCC4B', '#FF7D58'], ['#D0E6A5', '#86E3CE'], ['#F0D5B6', '#F16238'], ['#C4E86B', '#00BCB4'], ['#FFC446', '#FA0874'], ['#E1EE32', '#FFB547'], ['#FFD804', '#2ACCC8'], ['#E9A6D2', '#E9037B'], ['#F8EC70', '#49E2F6'], ['#A2F8CD', '#A2F852'], ['#A2F8CD', '#00C3FF'], ['#FDEFE2', '#FE214F'], ['#F8EC70', '#A2F8CD'], ['#F8EC70', '#49E2F6'], ['#FFB7D1', '#E4B7FF'], ['#D0E6A5', '#86E3CE'], ['#E8E965', '#64C5C7']]; 367 | let colors = colorArr[Math.floor(Math.random() * colorArr.length)]; 368 | return colors; 369 | } 370 | 371 | gradientColor(colors, step) { 372 | var startRGB = this.colorToRgb(colors[0]), 373 | startR = startRGB[0], 374 | startG = startRGB[1], 375 | startB = startRGB[2]; 376 | 377 | var endRGB = this.colorToRgb(colors[1]), 378 | endR = endRGB[0], 379 | endG = endRGB[1], 380 | endB = endRGB[2]; 381 | 382 | var sR = (endR - startR) / step, 383 | sG = (endG - startG) / step, 384 | sB = (endB - startB) / step; 385 | 386 | var colorArr = []; 387 | for (var i = 0;i < step; i++) { 388 | var hex = this.colorToHex('rgb(' + parseInt((sR * i + startR)) + ',' + parseInt((sG * i + startG)) + ',' + parseInt((sB * i + startB)) + ')'); 389 | colorArr.push(hex); 390 | } 391 | return colorArr; 392 | } 393 | 394 | colorToRgb(sColor) { 395 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; 396 | var sColor = sColor.toLowerCase(); 397 | if (sColor && reg.test(sColor)) { 398 | if (sColor.length === 4) { 399 | var sColorNew = "#"; 400 | for (var i = 1; i < 4; i += 1) { 401 | sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); 402 | } 403 | sColor = sColorNew; 404 | } 405 | var sColorChange = []; 406 | for (var i = 1; i < 7; i += 2) { 407 | sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2))); 408 | } 409 | return sColorChange; 410 | } else { 411 | return sColor; 412 | } 413 | }; 414 | 415 | colorToHex(rgb) { 416 | var _this = rgb; 417 | var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; 418 | if (/^(rgb|RGB)/.test(_this)) { 419 | var aColor = _this.replace(/(?:\(|\)|rgb|RGB)*/g,"").split(","); 420 | var strHex = "#"; 421 | for (var i = 0; i < aColor.length; i++) { 422 | var hex = Number(aColor[i]).toString(16); 423 | hex = hex.length < 2 ? 0 + '' + hex : hex; 424 | if (hex === "0") { 425 | hex += hex; 426 | } 427 | strHex += hex; 428 | } 429 | if (strHex.length !== 7) { 430 | strHex = _this; 431 | } 432 | 433 | return strHex; 434 | } else if (reg.test(_this)) { 435 | var aNum = _this.replace(/#/,"").split(""); 436 | if (aNum.length === 6) { 437 | return _this; 438 | } else if (aNum.length === 3) { 439 | var numHex = "#"; 440 | for (var i = 0; i < aNum.length; i+=1) { 441 | numHex += (aNum[i] + aNum[i]); 442 | } 443 | return numHex; 444 | } 445 | } else { 446 | return _this; 447 | } 448 | } 449 | 450 | renderSmall = async (w) => { 451 | w.setPadding(this.smallPadding, this.smallPadding, this.smallPadding, this.smallPadding); 452 | await this.smallHeader(w); 453 | const bodyStack = w.addStack(); 454 | bodyStack.layoutVertically(); 455 | if (this.widgetParam == "1"){ 456 | this.textLayout(bodyStack, this.flow); 457 | bodyStack.addSpacer(7); 458 | this.textLayout(bodyStack, this.voice); 459 | bodyStack.addSpacer(7); 460 | this.textLayout(bodyStack, this.point); 461 | } else { 462 | const canvas = this.makeCanvas(); 463 | const ringStack = bodyStack.addStack(); 464 | this.imageCell(canvas, ringStack, this.flow); 465 | ringStack.addSpacer(); 466 | this.imageCell(canvas, ringStack, this.voice); 467 | } 468 | return w; 469 | }; 470 | 471 | renderMedium = async (w) => { 472 | w.setPadding(this.padding, this.padding, this.padding, this.padding); 473 | const canvas = this.makeCanvas(); 474 | const bodyStack = w.addStack(); 475 | await this.mediumCell(canvas, bodyStack, this.fee, 'd7000f', true); 476 | bodyStack.addSpacer(this.padding); 477 | await this.mediumCell(canvas, bodyStack, this.flow, this.flowColorHex, false, true); 478 | bodyStack.addSpacer(this.padding); 479 | await this.mediumCell(canvas, bodyStack, this.voice, this.voiceColorHex, false,true); 480 | return w; 481 | }; 482 | 483 | renderLarge = async (w) => { 484 | w.addText('暂不支持') 485 | return w; 486 | }; 487 | 488 | Run() { 489 | if (config.runsInApp) { 490 | const widgetInitConfig = { 491 | cookie: '@YaYa_10010.cookie', 492 | }; 493 | this.registerAction('颜色配置', async () => { 494 | await this.setAlertInput( 495 | `${this.name}颜色配置`, 496 | '进度条颜色|底圈颜色\n底圈颜色留空将跟随进度条颜色并淡显', 497 | { 498 | gradient: '是否开启渐变进度条,缺省:false', 499 | step1: '流量进度条颜色', 500 | step2: '语音进度条颜色', 501 | inner1: '流量进度条底圈颜色', 502 | inner2: '语音进度条底圈颜色', 503 | }, 504 | ); 505 | }, 'https://gitee.com/anker1209/image/raw/master/jd/colorSet.png'); 506 | this.registerAction('尺寸设置', async () => { 507 | await this.setAlertInput( 508 | `${this.name}尺寸设置`, 509 | '进度条大小|文字大小', 510 | { 511 | logoScale: '小组件logo缩放,缺省:0.24', 512 | ringStackSize: '圆环大小,缺省:61', 513 | ringTextSize: '圆环中心文字大小,缺省:14', 514 | feeTextSize: '话费文字大小,缺省:21', 515 | textSize: '文字模式下文字大小,缺省:13', 516 | smallPadding: '小尺寸组件边距,缺省:16', 517 | padding: '中尺寸组件边距,缺省:10', 518 | }, 519 | ); 520 | }, 'https://gitee.com/anker1209/image/raw/master/jd/resize.png'); 521 | this.registerAction('账号设置', async () => { 522 | await this.setAlertInput( 523 | `${this.name}账号`, 524 | '读取 BoxJS 缓存信息', 525 | {cookie: 'cookie'}, 526 | ); 527 | }, 'https://gitee.com/anker1209/image/raw/master/jd/account.png'); 528 | this.registerAction('代理缓存', async () => { 529 | await this.setCacheBoxJSData(widgetInitConfig); 530 | }, 'https://gitee.com/anker1209/image/raw/master/jd/boxjs.png'); 531 | this.registerAction('基础设置', this.setWidgetConfig, 'https://gitee.com/anker1209/image/raw/master/jd/preferences.png'); 532 | } 533 | 534 | try { 535 | const { 536 | cookie, 537 | step1, 538 | step2, 539 | inner1, 540 | inner2, 541 | logoScale, 542 | ringStackSize, 543 | ringTextSize, 544 | feeTextSize, 545 | textSize, 546 | smallPadding, 547 | padding, 548 | gradient, 549 | } = this.settings; 550 | this.cookie = cookie ? cookie : this.cookie; 551 | this.gradient = gradient === 'true' ? true : this.gradient; 552 | this.flowColorHex = step1 ? step1 : this.flowColorHex; 553 | this.voiceColorHex = step2 ? step2 : this.voiceColorHex; 554 | this.flow.BGColor = inner1 ? new Color(inner1) : new Color(this.flowColorHex, 0.2); 555 | this.voice.BGColor = inner2 ? new Color(inner2) : new Color(this.voiceColorHex, 0.2); 556 | this.flow.FGColor = new Color(this.flowColorHex); 557 | this.voice.FGColor = new Color(this.voiceColorHex); 558 | 559 | this.logoScale = logoScale ? parseFloat(logoScale) : this.logoScale; 560 | this.ringStackSize = ringStackSize ? parseFloat(ringStackSize) : this.ringStackSize; 561 | this.ringTextSize = ringTextSize ? parseFloat(ringTextSize) : this.ringTextSize; 562 | this.feeTextSize = feeTextSize ? parseFloat(feeTextSize) : this.feeTextSize; 563 | this.textSize = textSize ? parseFloat(textSize) : this.textSize; 564 | this.smallPadding = smallPadding ? parseFloat(smallPadding) : this.smallPadding; 565 | this.padding = padding ? parseFloat(padding) : this.padding; 566 | 567 | if (this.gradient) { 568 | this.flow.colors = this.arrColor(); 569 | this.voice.colors = this.arrColor(); 570 | this.flow.BGColor = inner1 ? new Color(inner1) : new Color(this.flow.colors[1], 0.2); 571 | this.voice.BGColor = inner2 ? new Color(inner2) : new Color(this.voice.colors[1], 0.2); 572 | this.flow.FGColor = this.gradientColor(this.flow.colors, 360); 573 | this.voice.FGColor = this.gradientColor(this.voice.colors, 360); 574 | this.flowColorHex = this.flow.colors[1]; 575 | this.voiceColorHex = this.voice.colors[1]; 576 | } 577 | 578 | } catch (e) { 579 | console.log(e); 580 | } 581 | } 582 | 583 | async render() { 584 | await this.init(); 585 | const widget = new ListWidget(); 586 | await this.getWidgetBackgroundImage(widget); 587 | if (this.widgetFamily === 'medium') { 588 | return await this.renderMedium(widget); 589 | } else if (this.widgetFamily === 'large') { 590 | return await this.renderLarge(widget); 591 | } else { 592 | return await this.renderSmall(widget); 593 | } 594 | } 595 | } 596 | 597 | await Runing(Widget, args.widgetParameter, false); 598 | -------------------------------------------------------------------------------- /scripts/Chinaunicom.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: red; icon-glyph: mobile-alt; 4 | // Author: 脑瓜 5 | // Github: https://github.com/anker1209/Scriptable 6 | // 该组件支持两种模式,默认为圆环进度条模式,主屏幕长按小组件-->编辑小组件-->Parameter,输入1,使用文字模式 7 | 8 | // #############设置############## 9 | const files = FileManager.local() 10 | const Tel = '' // 输入联通手机号码,,在两个引号之间输入 11 | const Cookie = '' // 输入联通cookie,在两个引号之间输入 12 | const ringStackSize = 61 // 圆环大小 13 | const ringTextSize = 14 // 圆环中心文字大小 14 | const creditTextSize = 21 // 话费文本大小 15 | const textSize = 13 // 文字模式下文字大小 16 | const databgColor = new Color("12A6E4", 0.2) // 流量环背景颜色 17 | const datafgColor = new Color("12A6E4") // 流量环前景颜色 18 | const voicebgColor = new Color("F86527", 0.2) // 语音环背景颜色 19 | const voicefgColor = new Color("F86527") // 语音环前景颜色 20 | const newBG = false //是否设置或者使用新的背景图片,若要设置背景图片,请勿将下一行值设为true 21 | const removeBG = false //是否需要清空背景图片,如果设置过背景图片,想再使用纯色背景,需将此设置为true清除背景图片缓存 22 | const setbgColor = false //是否设置固定纯色背景,如要设置,请在下行指定背景颜色 23 | const bgColor = "ffffff" // 背景颜色 24 | const widgetParam = args.widgetParameter 25 | let data = await getData() 26 | 27 | const title = "中国联通" 28 | const cuIconUrl = "https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/f77d3cdc-b757-4acd-9766-a64421bf0c6d.png" 29 | const dataSfs = SFSymbol.named("antenna.radiowaves.left.and.right") 30 | dataSfs.applyHeavyWeight() 31 | const dataIcon = dataSfs.image 32 | const voiceIcon = SFSymbol.named("phone.fill").image 33 | const scoreIcon = SFSymbol.named("tag.fill").image 34 | const iconColor = new Color("FE8900") 35 | const phoneData = data.data.dataList[0] 36 | const credit = data.data.dataList[1] 37 | const voice = data.data.dataList[2] 38 | const score = data.data.dataList[3] 39 | const canvSize = 178 40 | const canvTextSize = 45 41 | const canvas = new DrawContext() 42 | const canvWidth = 18 43 | const canvRadius = 80 44 | const widget = new ListWidget() 45 | widget.setPadding(16, 16, 16, 16) // widget边距(上,下,左,右) 46 | 47 | // ############背景设置############ 48 | const path = files.joinPath(files.documentsDirectory(), "Chinaunicom-20210129") 49 | if (newBG && config.runsInApp){ 50 | const img = await Photos.fromLibrary() 51 | widget.backgroundImage = img 52 | files.writeImage(path, img) 53 | } else { 54 | if (files.fileExists(path)) { 55 | try { 56 | widget.backgroundImage = files.readImage(path) 57 | log("读取图片成功") 58 | } catch (e){ 59 | log(e.message) 60 | } 61 | } 62 | } 63 | if (removeBG && files.fileExists(path)) { 64 | try { 65 | files.remove(path) 66 | log("背景图片清理成功") 67 | } catch (e) { 68 | log(e.message) 69 | } 70 | } 71 | if (setbgColor) { 72 | widget.backgroundColor = new Color(bgColor) 73 | } 74 | 75 | // ############LOGO############### 76 | let headerStack = widget.addStack() 77 | headerStack.layoutVertically 78 | headerStack.addSpacer() 79 | let logo = headerStack.addImage(await getImg(cuIconUrl)) 80 | logo.imageSize = new Size(393*0.25, 118*0.25) 81 | headerStack.addSpacer() 82 | widget.addSpacer() 83 | 84 | let creditStack = widget.addStack() 85 | creditStack.centerAlignContent() 86 | creditStack.addSpacer() 87 | let creditElement = creditStack.addText(credit.number) 88 | creditElement.font = Font.mediumRoundedSystemFont(creditTextSize) 89 | creditStack.addSpacer() 90 | widget.addSpacer() 91 | 92 | // ############################### 93 | let bodyStack = widget.addStack() 94 | bodyStack.layoutVertically() 95 | 96 | if (widgetParam == "1"){ 97 | await textLayout(dataIcon, phoneData.remainTitle, phoneData.number, phoneData.unit) 98 | bodyStack.addSpacer(8) 99 | await textLayout(voiceIcon, voice.remainTitle, voice.number, voice.unit) 100 | bodyStack.addSpacer(8) 101 | await textLayout(scoreIcon, score.remainTitle, score.number, score.unit) 102 | } else { 103 | canvas.size = new Size(canvSize, canvSize) 104 | canvas.opaque = false 105 | canvas.respectScreenScale = true 106 | 107 | const dataGap = (100-phoneData.persent)*3.6 108 | const voiceGap = (100-voice.persent)*3.6 109 | 110 | drawArc(dataGap, datafgColor, databgColor) 111 | let ringStack = bodyStack.addStack() 112 | let ringLeft = ringStack.addStack() 113 | ringLeft.layoutVertically() 114 | ringLeft.size = new Size(ringStackSize, ringStackSize) 115 | ringLeft.backgroundImage = canvas.getImage() 116 | await ringContent(ringLeft, dataIcon, datafgColor, phoneData.number, phoneData.unit) 117 | ringStack.addSpacer() 118 | 119 | drawArc(voiceGap, voicefgColor, voicebgColor) 120 | let ringRight = ringStack.addStack() 121 | ringRight.layoutVertically() 122 | ringRight.size = new Size(ringStackSize, ringStackSize) 123 | ringRight.backgroundImage = canvas.getImage() 124 | await ringContent(ringRight, voiceIcon, voicefgColor, voice.number, voice.unit) 125 | } 126 | // ############################### 127 | if (!config.runsInWidget) { 128 | await widget.presentSmall() 129 | } 130 | Script.setWidget(widget) 131 | Script.complete() 132 | 133 | async function getImg(url) { 134 | const req = new Request(url) 135 | const img = await req.loadImage() 136 | return img 137 | } 138 | 139 | function sinDeg(deg) { 140 | return Math.sin((deg * Math.PI) / 180) 141 | } 142 | 143 | function cosDeg(deg) { 144 | return Math.cos((deg * Math.PI) / 180) 145 | } 146 | 147 | function ringContent(widget, icon, iconColor, text, unit){ 148 | const rowIcon = widget.addStack() 149 | rowIcon.addSpacer() 150 | const iconElement = rowIcon.addImage(icon) 151 | iconElement.tintColor = iconColor 152 | iconElement.imageSize = new Size(12, 12) 153 | iconElement.imageOpacity = 0.7 154 | rowIcon.addSpacer() 155 | 156 | widget.addSpacer(1) 157 | 158 | const rowText = widget.addStack() 159 | rowText.addSpacer() 160 | const textElement = rowText.addText(text) 161 | textElement.font = Font.mediumSystemFont(ringTextSize) 162 | rowText.addSpacer() 163 | 164 | const rowUnit = widget.addStack() 165 | rowUnit.addSpacer() 166 | const unitElement = rowUnit.addText(unit) 167 | unitElement.font = Font.boldSystemFont(8) 168 | unitElement.textOpacity = 0.5 169 | rowUnit.addSpacer() 170 | } 171 | 172 | function textLayout(icon, title, number, unit){ 173 | const rowItem = bodyStack.addStack() 174 | rowItem.centerAlignContent() 175 | let iconElement = rowItem.addImage(icon) 176 | iconElement.imageSize = new Size(textSize, textSize) 177 | iconElement.tintColor = iconColor 178 | rowItem.addSpacer(4) 179 | let titleElement = rowItem.addText(title) 180 | titleElement.font = Font.systemFont(textSize) 181 | rowItem.addSpacer() 182 | let numberElement = rowItem.addText(number+unit) 183 | numberElement.font = Font.systemFont(textSize) 184 | } 185 | 186 | function drawArc(deg, fillColor, strokeColor, centerColor) { 187 | let ctr = new Point(canvSize / 2, canvSize / 2), 188 | bgx = ctr.x - canvRadius; 189 | bgy = ctr.y - canvRadius; 190 | bgd = 2 * canvRadius; 191 | bgr = new Rect(bgx, bgy, bgd, bgd) 192 | 193 | canvas.setFillColor(fillColor) 194 | canvas.setStrokeColor(strokeColor) 195 | canvas.setLineWidth(canvWidth) 196 | canvas.strokeEllipse(bgr) 197 | 198 | for (let t = 0; t < deg; t++) { 199 | rect_x = ctr.x + canvRadius * sinDeg(t) - canvWidth / 2 200 | rect_y = ctr.y - canvRadius * cosDeg(t) - canvWidth / 2 201 | rect_r = new Rect(rect_x, rect_y, canvWidth, canvWidth) 202 | canvas.fillEllipse(rect_r) 203 | } 204 | } 205 | 206 | async function getData() { 207 | const cachePath = files.joinPath(files.documentsDirectory(), "Chinaunicom-anker") 208 | var url= 'https://m.client.10010.com/mobileService/home/queryUserInfoSeven.htm?showType=3&version=iphone_c@7.0600&desmobiel='+Tel; 209 | var url1 = 'https://act.10010.com/SigninApp/signin/daySign' 210 | try { 211 | var req = new Request(url) 212 | req.headers = {'cookie': Cookie } 213 | console.log(req) 214 | var data = await req.loadJSON() 215 | console.log(data) 216 | var req1 = new Request(url1); 217 | req1.headers = {'cookie': Cookie } 218 | var data1 = await req1.loadJSON(); 219 | if (data.signinState === '0'){ 220 | files.writeString(cachePath, JSON.stringify(data)) 221 | log("==>数据请求成功") 222 | } 223 | } 224 | catch (e) { 225 | data = JSON.parse(files.readString(cachePath)) 226 | log("==>数据请求失败,使用缓存数据/"+ e) 227 | } 228 | return data 229 | } 230 | -------------------------------------------------------------------------------- /scripts/JD-in-one-v2.js: -------------------------------------------------------------------------------- 1 | // Variables used by Scriptable. 2 | // These must be at the very top of the file. Do not edit. 3 | // icon-color: pink; icon-glyph: shopping-cart; 4 | // Author: 脑瓜 5 | // 电报群: https://t.me/Scriptable_JS @anker1209 6 | // 采用了2Ya美女的京豆收支脚本及DmYY依赖 https://github.com/dompling/Scriptable/tree/master/Scripts 7 | // version:2.2.7 8 | // update:2022/01/04 9 | 10 | if (typeof require === 'undefined') require = importModule; 11 | const {DmYY, Runing} = require('./DmYY'); 12 | 13 | class Widget extends DmYY { 14 | constructor(arg) { 15 | super(arg); 16 | this.name = '京东多合一'; 17 | this.en = 'jd_in_one'; 18 | this.run(module.filename, args); 19 | } 20 | fm = FileManager.local(); 21 | CACHE_FOLDER = 'JD_in_one'; 22 | cachePath = null; 23 | 24 | logo = 'https://pic.imgdb.cn/item/6187994b2ab3f51d919028cc.png'; 25 | JDImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/43300bf7-61a2-4bd1-94a1-bf2faa2ed9e8.png'; 26 | beanImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/7ea91cf8-6dea-477c-ae72-cb4d3f646c34.png'; 27 | plusFG = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cd0d2b80-0857-4202-8d12-af4eb7d241d6.png'; 28 | plusBG = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/24fc5a14-edea-4b1b-8e30-bdcc1a27a037.png'; 29 | baitiaoImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/30c40f5b-7428-46c3-a2c0-d81b2b95ec41.png'; 30 | plusIcon = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/06f78540-a5a4-462e-b8c5-98cb8059efc1.png'; 31 | walletImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cd89ceec-7895-41ee-a1a3-3d3e7223035f.png'; 32 | jingtieImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/16a7038e-6082-4ad8-b17f-fdd08266fb22.png'; 33 | gangbengImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/9704e332-9e7f-47e8-b09a-1f1991d4aa84.png'; 34 | userImage = 'https://img11.360buyimg.com/jdphoto/s120x120_jfs/t21160/90/706848746/2813/d1060df5/5b163ef9N4a3d7aa6.png'; 35 | nameImg = 'https://pic.imgdb.cn/item/6188bfb62ab3f51d91bca276.png'; 36 | tagImg = 'https://pic.imgdb.cn/item/6188bfc72ab3f51d91bcbcb0.png'; 37 | 38 | // 请勿在此修改参数值 39 | 40 | version = '2.2.6'; 41 | basicSetting = { 42 | scale: 1.00, 43 | logo: 30, 44 | userImage: 69, 45 | userStack: 103, 46 | division: 25, 47 | interval: 10, 48 | directory: 'Local', 49 | customizeName: '', 50 | customizeAvatar: '', 51 | smallShowType: '京豆、钱包数据', 52 | walletShowType: '红包', 53 | }; 54 | chartSetting = { 55 | height: 130, 56 | daySize: 9, 57 | dayText: '', 58 | textSize: 18, 59 | textDayColor: '999999', 60 | textNightColor: '999999', 61 | lineColor: '#FA6859', 62 | linePadding: 15, 63 | barPadding: 5, 64 | smallShowType: '双日视图', 65 | showType: '双日视图', 66 | countBean: '收入-支出', 67 | colorful: '关闭', 68 | }; 69 | funcSetting = { 70 | showBaitiao: '打开', 71 | showPackage: '关闭', 72 | showFruit: '打开', 73 | logable: '关闭', 74 | alwaysRefreshChart: '打开', 75 | }; 76 | package = { 77 | number: 0, 78 | title: '', 79 | desc: '', 80 | time: '', 81 | status: '', 82 | }; 83 | baitiao = { 84 | title: '', 85 | number: 0, 86 | desc: '', 87 | }; 88 | redPackage = { 89 | title: '红包', 90 | number: 0, 91 | desc: '今日无过期', 92 | }; 93 | extra = { 94 | jingtie: 0, 95 | gangbeng: 0, 96 | }; 97 | bean = { 98 | todayIncome: 0, 99 | todayExpense: 0, 100 | ydayIncome: 0, 101 | ydayExpense: 0, 102 | }; 103 | 104 | nickName = '未知用户'; 105 | jValue = '0'; 106 | isPlus = false; 107 | 108 | cookie = ''; 109 | userName = ''; 110 | CookiesData = []; 111 | cacheChart = false; 112 | beanCount = 0; 113 | maxDays = 6; 114 | rangeTimer = {}; 115 | timerKeys = []; 116 | fruitState = "😢"; 117 | 118 | doubleDate = this.getDay(1); 119 | doubleDay = Object.keys(this.doubleDate); 120 | yestoday = this.doubleDay[0]; 121 | today = this.doubleDay[1]; 122 | CACHES = []; 123 | 124 | lineChart(labels = [], datas = [], chartTextSize, topPadding) { 125 | let chartTextColor = Color.dynamic(new Color(this.chartSetting.textDayColor),new Color(this.chartSetting.textNightColor),); 126 | let lineColor = this.chartSetting.lineColor.split(','); 127 | const chartStr = ` 128 | { 129 | type: 'bar', 130 | data: { 131 | labels: ${JSON.stringify(labels)}, 132 | datasets: [ 133 | { 134 | type: 'line', 135 | backgroundColor: '#FFFFFF', 136 | borderColor: getGradientFillHelper('horizontal', ${JSON.stringify(lineColor)}), 137 | borderWidth: ${this.isSmall(true) ? 4 : 3}, 138 | pointRadius: ${this.isSmall(true) ? 8 : 6}, 139 | fill: false, 140 | showLine: true, 141 | data: ${JSON.stringify(datas)}, 142 | }, 143 | ], 144 | }, 145 | options: { 146 | plugins: { 147 | datalabels: { 148 | display: true, 149 | align: 'top', 150 | color: '#${chartTextColor.hex}', 151 | font: { 152 | family: 'ArialMT', 153 | size: ${chartTextSize} 154 | } 155 | }, 156 | }, 157 | layout: { 158 | padding: { 159 | left: -20, 160 | right: 0, 161 | top: ${topPadding}, 162 | bottom: 0 163 | } 164 | }, 165 | responsive: true, 166 | maintainAspectRatio: true, 167 | legend: { 168 | display: false, 169 | }, 170 | scales: { 171 | xAxes: [ 172 | { 173 | gridLines: { 174 | display: false, 175 | }, 176 | ticks: { 177 | display: false, 178 | }, 179 | }, 180 | ], 181 | yAxes: [ 182 | { 183 | ticks: { 184 | display: false, 185 | beginAtZero: false, 186 | }, 187 | gridLines: { 188 | display: false, 189 | }, 190 | }, 191 | ], 192 | }, 193 | }, 194 | }`; 195 | return chartStr; 196 | } 197 | 198 | barChart(labels = [], datas = [], chartTextSize, topPadding, showType) { 199 | let chartTextColor = Color.dynamic(new Color(this.chartSetting.textDayColor),new Color(this.chartSetting.textNightColor),); 200 | let backgroundColor = []; 201 | if (this.chartSetting.colorful === '打开') backgroundColor = JSON.stringify(this.colorfulBar()) 202 | else backgroundColor = `getGradientFillHelper('vertical', ${JSON.stringify(this.chartColors())})` 203 | const chartStr = ` 204 | { 205 | type: 'bar', 206 | data: { 207 | labels: ${JSON.stringify(labels)}, 208 | datasets: [ 209 | { 210 | type: '${showType}', 211 | borderWidth: 0, 212 | pointRadius: 0, 213 | barPercentage: 0.5, 214 | backgroundColor: ${backgroundColor}, 215 | borderColor: false, 216 | data: ${JSON.stringify(datas)}, 217 | }, 218 | ], 219 | }, 220 | options: { 221 | plugins: { 222 | datalabels: { 223 | display: true, 224 | align: 'top', 225 | offset: -4, 226 | anchor:'end', 227 | color: '#${chartTextColor.hex}', 228 | font: { 229 | family: 'ArialMT', 230 | size: ${chartTextSize} 231 | } 232 | }, 233 | }, 234 | layout: { 235 | padding: { 236 | left: -20, 237 | right: 0, 238 | top: ${topPadding}, 239 | bottom: 0 240 | } 241 | }, 242 | responsive: true, 243 | maintainAspectRatio: true, 244 | legend: { 245 | display: false, 246 | }, 247 | title: { 248 | display: false, 249 | }, 250 | scales: { 251 | xAxes: [ 252 | { 253 | gridLines: { 254 | offsetGridLines: true, 255 | display: false, 256 | }, 257 | ticks: { 258 | display: false, 259 | }, 260 | }, 261 | ], 262 | yAxes: [ 263 | { 264 | ticks: { 265 | display: false, 266 | beginAtZero: true, 267 | }, 268 | gridLines: { 269 | offsetGridLines: true, 270 | display: false, 271 | }, 272 | }, 273 | ], 274 | }, 275 | }, 276 | }`; 277 | return chartStr; 278 | } 279 | 280 | chartColors () { 281 | let colorArr = [['#FFF000', '#E62490'], ['#FDEB71', '#F8D800'], ['#ABDCFF', '#0396FF'], ['#FEB692', '#EA5455'], ['#FEB692', '#EA5455'], ['#CE9FFC', '#7367F0'], ['#90F7EC', '#32CCBC'], ['#FFF6B7', '#F6416C'], ['#E2B0FF', '#9F44D3'], ['#F97794', '#F072B6'], ['#FCCF31', '#F55555'], ['#5EFCE8', '#736EFE'], ['#FAD7A1', '#E96D71'], ['#FFFF1C', '#00C3FF'], ['#FEC163', '#DE4313'], ['#F6CEEC', '#D939CD'], ['#FDD819', '#E80505'], ['#FFF3B0', '#CA26FF'], ['#2AFADF', '#4C83FF'], ['#EECDA3', '#EF629F'], ['#C2E59C', '#64B3F4'], ['#FFF886', '#F072B6'], ['#F5CBFF', '#C346C2'], ['#FFF720', '#3CD500'], ['#EE9AE5', '#5961F9'], ['#FFC371', '#FF5F6D'], ['#FFD3A5', '#FD6585'], ['#C2FFD8', '#465EFB'], ['#FFC600', '#FD6E6A'], ['#FFC600', '#FD6E6A'], ['#92FE9D', '#00C9FF'], ['#FFDDE1', '#EE9CA7'], ['#F0FF00', '#58CFFB'], ['#FFE985', '#FA742B'], ['#72EDF2', '#5151E5'], ['#F6D242', '#FF52E5'], ['#F9D423', '#FF4E50'], ['#3C8CE7', '#00EAFF'], ['#FCFF00', '#FFA8A8'], ['#FF96F9', '#C32BAC'], ['#D0E6A5', '#FFDD94'], ['#FFDD94', '#FA897B'], ['#FFCC4B', '#FF7D58'], ['#D0E6A5', '#86E3CE'], ['#F0D5B6', '#F16238'], ['#F8EC70', '#F9C708'], ['#C4E86B', '#00BCB4'], ['#F5CEC7', '#E79796'], ['#FFC446', '#FA0874'], ['#E1EE32', '#FFB547'], ['#FFD804', '#2ACCC8'], ['#E9A6D2', '#E9037B'], ['#F8EC70', '#49E2F6'], ['#A2F8CD', '#A2F852'], ['#49E2F6', '#A2F8CD'], ['#FDEFE2', '#FE214F'], ['#F8EC70', '#A2F8CD'], ['#F8EC70', '#49E2F6'], ['#D1FFB7', '#FFB7D1'], ['#B7FFE4', '#E4B7FF'], ['#FFB7D1', '#E4B7FF'], ['#D0E6A5', '#86E3CE'], ['#E8E965', '#64C5C7']]; 282 | let chartColors = colorArr[Math.floor(Math.random() * colorArr.length)]; 283 | //chartColors = ['#DB36A4', '#F7FF00']; // 固定京豆图表填充颜色 284 | return chartColors; 285 | } 286 | 287 | colorfulBar () { 288 | let colorArr = [['#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02'], ['#F46277', '#FC8D59', '#FEE08B', '#E6F598', '#99D594', '#3288BD'], ['#A6CEE3', '#1F78B4', '#B2DF8A', '#33A02C', '#FB9A99', '#E31A1C'], ['#E41A1C', '#377EB8', '#4DAF4A', '#984EA3', '#FF7F00', '#9ED80E'], ['#F81B02', '#FC7715', '#AFBF41', '#50C49F', '#3B95C4', '#B560D4'], ['#FFC000', '#A5D028', '#08CC78', '#F24099', '#5AA6C0', '#F56617'], ['#F09415', '#C1B56B', '#4BAF73', '#5AA6C0', '#D17DF9', '#FA7E5C'], ['#0F6FC6', '#009DD9', '#0BD0D9', '#10CF9B', '#7CCA62', '#A5C249'], ['#9ACD4C', '#FAA93A', '#D35940', '#B258D3', '#63A0CC', '#8AC4A7'], ['#A7EA52', '#EFAB16', '#78AC35', '#35ACA2', '#4083CF', '#FF8021'], ['#9EC544', '#50BEA3', '#4A9CCC', '#9A66CA', '#C54F71', '#DE9C3C'], ['#41AEBD', '#97E9D5', '#A2CF49', '#608F3D', '#F4DE3A', '#FCB11C'], ['#2FA3EE', '#4BCAAD', '#86C157', '#D99C3F', '#CE6633', '#A35DD1'], ['#3399FF', '#69FFFF', '#CCFF33', '#3333FF', '#9933FF', '#FF33FF'], ['#FBC01E', '#EFE1A2', '#FA8716', '#F2575F', '#A5D848', '#A155F9'], ['#90C226', '#54A021', '#E6B91E', '#E76618', '#C42F1A', '#FA8716'], ['#0F6FC6', '#009DD9', '#0BD0D9', '#10CF9B', '#7CCA62', '#A5C249'], ['#FFB91D', '#F97817', '#6DE304', '#F98080', '#8F58F9', '#F789EA'], ['#C70F0C', '#DD6B0D', '#FAA700', '#93E50D', '#17C7BA', '#0A96E4'], ['#40BAD2', '#FAB900', '#90BB23', '#EE7008', '#1AB39F', '#D5393D'], ['#80B606', '#E29F1D', '#2397E2', '#35ACA2', '#5430BB', '#8D34E0'], ['#549E39', '#8AB833', '#C0CF3A', '#029676', '#4AB5C4', '#0989B1'], ['#99CB38', '#63A537', '#37A76F', '#44C1A3', '#4EB3CF', '#51C3F9'], ['#8C73D0', '#C2E8C4', '#C5A6E8', '#B45EC7', '#9FDAFB', '#95C5B0'], ['#1CADE4', '#2683C6', '#27CED7', '#42BA97', '#3E8853', '#62A39F'], ['#B31166', '#E33D6F', '#E45F3C', '#E9943A', '#9B6BF2', '#D53DD0'], ['#76C5EF', '#FEA022', '#FF6700', '#70A525', '#A5D848', '#20768C'], ['#A1D68B', '#5EC795', '#4DADCF', '#CDB756', '#E29C36', '#8EC0C1'], ['#418AB3', '#A6B727', '#F69200', '#80C34F', '#FEC306', '#DF5327'], ['#7FD13B', '#EA157A', '#FEB80A', '#00ADDC', '#738AC8', '#1AB39F'], ['#F0AD00', '#60B5CC', '#E66C7D', '#6BB76D', '#E88651', '#C64847'], ['#5B9BD5', '#ED7D31', '#A5D848', '#FFC000', '#4472C4', '#70AD47'], ['#4F81BD', '#C0504D', '#9BBB59', '#8064A2', '#4BACC6', '#F79646'], ['#F95F9A', '#AC66BB', '#DE6C36', '#F9B639', '#CF6DA4', '#FA8D3D'], ['#F2D908', '#9DE61E', '#0D8BE6', '#C61B1B', '#E26F08', '#8D35D1'], ['#A5B592', '#F3A447', '#E7BC29', '#D092A7', '#9C85C0', '#809EC2'], ['#30ACEC', '#80C34F', '#E29D3E', '#D64A3B', '#D64787', '#A666E1'], ['#A2C816', '#E07602', '#E4C402', '#7DC1EF', '#21449B', '#A2B170'], ['#FF7F01', '#F1B015', '#FBEC85', '#D2C2F1', '#DA5AF4', '#9D09D1'], ['#FDA023', '#A7EA52', '#5ECCF3', '#64A73B', '#EB5605', '#B9CA1A'], ['#00C6BB', '#6FEBA0', '#B6DF5E', '#EFB251', '#EF755F', '#ED515C'], ['#E32D91', '#C830CC', '#4EA6DC', '#4775E7', '#8971E1', '#D54773'], ['#1CADE4', '#2683C6', '#27CED7', '#42BA97', '#3E8853', '#62A39F'], ['#073779', '#8FD9FB', '#FFCC00', '#EB6615', '#C76402', '#B523B4'], ['#4E67C8', '#5ECCF3', '#A7EA52', '#5DCEAF', '#FF8021', '#F14124'], ['#3891A7', '#FEB80A', '#FC8389', '#84AA33', '#F9934E', '#4379EF'], ['#990000', '#FF6600', '#FFBA00', '#99CC00', '#528A02', '#9C007F'], ['#F7901E', '#FEC60B', '#9FE62F', '#4EA5D1', '#4282EA', '#854FED'], ['#E8BC4A', '#83C1C6', '#E78D35', '#909CE1', '#839C41', '#F9826E'], ['#86CE24', '#00A2E6', '#FAC810', '#AA69F7', '#D06B20', '#FF8021'], ['#DF2E28', '#FE801A', '#E9BF35', '#81BB42', '#32C7A9', '#4A9BDC'], ['#31B6FD', '#4584D3', '#5BD078', '#A5D028', '#F5C040', '#05E0DB'], ['#FFCA08', '#F8931D', '#CE8D3E', '#EC7016', '#E64823', '#9C6A6A'], ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948'], ['#4E79A7', '#A0CBE8', '#F28E2B', '#FFBE7D', '#59A14F', '#8CD17D'], ['#E03531', '#F0BD27', '#51B364', '#FF684C', '#FFDA66', '#8ACE7E'], ['#4E9F50', '#87D180', '#EF8A0C', '#FCC66D', '#3CA8BC', '#98D9E4'], ['#1F77B4', '#FF7F0E', '#2CA02C', '#D62728', '#9467BD', '#E377C2'], ['#32A251', '#ACD98D', '#FF7F0F', '#FFB977', '#3CB7CC', '#98D9E4'], ]; 289 | let chartColors = colorArr[Math.floor(Math.random() * colorArr.length)]; 290 | //chartColors = ['#C1B14A','#6FBC75','#39B6B3','#86A1CD','#D083AB','#DF786B']; // 固定京豆图表填充颜色 291 | return chartColors; 292 | } 293 | 294 | isSmall (a = false) { 295 | if (a) return config.widgetFamily == 'small' ? true : false; 296 | else return config.widgetFamily == 'small' ? '_small' : ''; 297 | } 298 | 299 | // #####################小组件################### 300 | renderSmall = async (w) => { 301 | const bodyStack = w.addStack(); 302 | bodyStack.layoutVertically(); 303 | if (this.basicSetting.smallShowType === '个人信息') { 304 | await this.setUserShow(bodyStack); 305 | } else { 306 | await this.setHeaderShow(bodyStack); 307 | bodyStack.addSpacer(); 308 | switch (this.chartSetting.smallShowType) { 309 | case '折线图表' : 310 | await this.setChartShow(bodyStack, 1); 311 | break; 312 | case '柱状图表' : 313 | await this.setChartShow(bodyStack, 2); 314 | break; 315 | case '曲线面积图': 316 | await this.setChartShow(bodyStack, 3); 317 | break; 318 | default: 319 | await this.setBeanShow(bodyStack, 22 * this.basicSetting.scale, 40 * this.basicSetting.scale); 320 | } 321 | bodyStack.addSpacer(5 * this.basicSetting.scale); 322 | if (this.funcSetting.showBaitiao === '打开' && this.baitiao.number > 0) { 323 | await this.setBaitiaoShow(bodyStack, true); 324 | } else if (this.basicSetting.walletShowType === '红包') { 325 | await this.setRedPackageShow(bodyStack, true); 326 | } else { 327 | await this.setCoinShow(bodyStack, true); 328 | } 329 | } 330 | return w; 331 | } 332 | 333 | // #####################中组件################### 334 | renderMedium = async (w) => { 335 | const bodyStack = w.addStack(); 336 | await this.setUserShow(bodyStack); 337 | bodyStack.addSpacer(this.basicSetting.division * this.basicSetting.scale); 338 | const mainStack = bodyStack.addStack(); 339 | mainStack.layoutVertically(); 340 | await this.setHeaderShow(mainStack, this.JDImg); 341 | mainStack.addSpacer(); 342 | if (this.funcSetting.showPackage === '打开' && this.package.number > 0) { 343 | await this.setPackageShow(mainStack); 344 | mainStack.addSpacer(); 345 | } else { 346 | switch (this.chartSetting.showType) { 347 | case '折线图表': 348 | await this.setChartShow(mainStack, 1); 349 | mainStack.addSpacer(5 * this.basicSetting.scale); 350 | break; 351 | case '柱状图表': 352 | await this.setChartShow(mainStack, 2); 353 | mainStack.addSpacer(5 * this.basicSetting.scale); 354 | break; 355 | case '曲线面积图': 356 | await this.setChartShow(mainStack, 3); 357 | mainStack.addSpacer(5 * this.basicSetting.scale); 358 | break; 359 | default: 360 | await this.setBeanShow(mainStack, 30 * this.basicSetting.scale, 50 * this.basicSetting.scale); 361 | mainStack.addSpacer(); 362 | } 363 | } 364 | if (this.funcSetting.showBaitiao === '打开' && this.baitiao.number > 0) { 365 | await this.setBaitiaoShow(mainStack); 366 | } else if (this.basicSetting.walletShowType === '红包') { 367 | await this.setRedPackageShow(mainStack); 368 | } else { 369 | await this.setCoinShow(mainStack); 370 | } 371 | return w; 372 | } 373 | 374 | // #####################大组件################### 375 | renderLarge = async (w) => { 376 | const bodyStack = w.addStack(); 377 | bodyStack.size = new Size(0, 150); 378 | bodyStack.addSpacer(); 379 | await this.setUserShow(bodyStack); 380 | bodyStack.addSpacer(); 381 | w.addSpacer(20); 382 | const text = w.addText('\u6211\u600e\u4e48\u8fd9\u4e48\u597d\u770b'); 383 | w.addSpacer(20); 384 | text.font = Font.thinSystemFont(30); 385 | text.centerAlignText(); 386 | const emoji = w.addText('🤣🥰🤪'); 387 | emoji.centerAlignText(); 388 | w.addSpacer(); 389 | return w; 390 | } 391 | 392 | // #####################用户信息################### 393 | async setUserShow(stack) { 394 | const userStack = stack.addStack(); 395 | userStack.size = new Size(this.basicSetting.userStack * this.basicSetting.scale, 0); 396 | userStack.layoutVertically(); 397 | // 头像 398 | const userImgStack = userStack.addStack(); 399 | userImgStack.addSpacer(); 400 | const imgStack = userImgStack.addStack(); 401 | if (this.isPlus) { 402 | imgStack.size = new Size(this.basicSetting.userImage * this.basicSetting.scale, this.basicSetting.userImage * this.basicSetting.scale * 1.039); 403 | imgStack.backgroundImage = await this.getImageByUrl(this.plusBG, 'plusBGImage.png'); 404 | } 405 | const subStack = imgStack.addStack(); 406 | subStack.url = 'openapp.jdmobile://'; 407 | subStack.size = new Size(this.basicSetting.userImage * this.basicSetting.scale, this.basicSetting.userImage * this.basicSetting.scale); 408 | subStack.cornerRadius = this.basicSetting.userImage / 2 * this.basicSetting.scale; 409 | subStack.backgroundImage = await this.getImageByUrl(this.basicSetting.customizeAvatar || this.userImage, `userImage_${this.userName}.png`); 410 | if (this.isPlus) { 411 | const userImg = subStack.addImage(await this.getImageByUrl(this.plusFG, 'plusFGImage.png')); 412 | } 413 | userImgStack.addSpacer(); 414 | userStack.addSpacer(); 415 | // 物流提示 416 | const tipStack = userStack.addStack(); 417 | tipStack.addSpacer(); 418 | const signStack = tipStack.addStack(); 419 | signStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) 420 | signStack.backgroundColor = new Color('0dD6A0'); 421 | signStack.cornerRadius = 14 * this.basicSetting.scale / 2; 422 | signStack.centerAlignContent(); 423 | let signIcon = SFSymbol.named('checkmark'); 424 | const signItem = signStack.addImage(signIcon.image); 425 | signItem.imageSize = new Size(8 * this.basicSetting.scale, 8 * this.basicSetting.scale); 426 | signItem.tintColor = new Color('FFFFFF'); 427 | if (this.package.number > 0) { 428 | tipStack.addSpacer(3 * this.basicSetting.scale); 429 | const packageStack = tipStack.addStack(); 430 | packageStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) 431 | packageStack.backgroundColor = new Color('FC8600'); 432 | packageStack.cornerRadius = 14 * this.basicSetting.scale / 2; 433 | packageStack.centerAlignContent(); 434 | packageStack.setPadding(1 * this.basicSetting.scale, 2 * this.basicSetting.scale, 1 * this.basicSetting.scale, 2 * this.basicSetting.scale); 435 | let packageNum = packageStack.addText(this.package.number.toString()); 436 | packageNum.font = Font.mediumSystemFont(15 * this.basicSetting.scale); 437 | packageNum.textColor = new Color('FFFFFF'); 438 | packageNum.minimumScaleFactor = 0.1; 439 | } 440 | if (this.funcSetting.showFruit === '打开') { 441 | tipStack.addSpacer(3 * this.basicSetting.scale); 442 | const fruitStack = tipStack.addStack(); 443 | fruitStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) 444 | fruitStack.backgroundColor = new Color('118AB2'); 445 | fruitStack.cornerRadius = 14 * this.basicSetting.scale / 2; 446 | fruitStack.centerAlignContent(); 447 | fruitStack.setPadding(1 * this.basicSetting.scale, 2 * this.basicSetting.scale, 1 * this.basicSetting.scale, 2 * this.basicSetting.scale); 448 | let fruitText = fruitStack.addText(this.fruitState); 449 | fruitText.font = Font.mediumSystemFont(15 * this.basicSetting.scale); 450 | fruitText.textColor = new Color('FFFFFF'); 451 | fruitText.minimumScaleFactor = 0.1; 452 | } 453 | tipStack.addSpacer(); 454 | userStack.addSpacer(); 455 | // 用户名 456 | const nameStack = userStack.addStack(); 457 | nameStack.centerAlignContent(); 458 | if (this.isPlus) { 459 | const nameImg = nameStack.addImage(await this.getImageByUrl(this.plusIcon, 'plusIcon.png')); 460 | nameImg.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); 461 | } else { 462 | const nameIcon = nameStack.addImage(await this.getImageByUrl(this.nameImg, 'nameImg.png')); 463 | nameIcon.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); 464 | } 465 | nameStack.addSpacer(5 * this.basicSetting.scale); 466 | const name = nameStack.addText(this.basicSetting.customizeName || this.nickName); 467 | name.lineLimit = 1; 468 | name.font = Font.regularSystemFont(14 * this.basicSetting.scale); 469 | userStack.addSpacer(5 * this.basicSetting.scale); 470 | // 京享值 471 | const valueStack = userStack.addStack(); 472 | valueStack.centerAlignContent(); 473 | const lableIcon = valueStack.addImage(await this.getImageByUrl(this.tagImg, 'tagImg.png')); 474 | lableIcon.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); 475 | valueStack.addSpacer(5 * this.basicSetting.scale); 476 | const value = valueStack.addText(this.jValue.toString()); 477 | value.font = Font.mediumSystemFont(14 * this.basicSetting.scale); 478 | 479 | valueStack.addSpacer(5 * this.basicSetting.scale); 480 | const jStack = valueStack.addStack(); 481 | jStack.backgroundColor = new Color('fa2d19'); // “京享”二字背景颜色 482 | jStack.cornerRadius = 5; 483 | jStack.setPadding(1 * this.basicSetting.scale, 4 * this.basicSetting.scale, 1 * this.basicSetting.scale, 4 * this.basicSetting.scale); 484 | const jLable = jStack.addText('京享'); 485 | jLable.font = Font.systemFont(8 * this.basicSetting.scale); 486 | jLable.textColor = new Color('FFFFFF') // “京享”二字字体颜色 487 | ;[name, value].map(t => t.textColor = this.widgetColor); 488 | } 489 | 490 | // #####################顶部内容################### 491 | async setHeaderShow(stack, image) { 492 | const topStack = stack.addStack(); 493 | topStack.centerAlignContent(); 494 | if (image) { 495 | const JDLogo = topStack.addImage(await this.getImageByUrl(this.logo, 'logoImage.png')); 496 | JDLogo.imageSize = new Size(this.basicSetting.logo * this.basicSetting.scale, this.basicSetting.logo * this.basicSetting.scale); 497 | topStack.addSpacer(10 * this.basicSetting.scale); 498 | const JD = topStack.addImage(await this.getImageByUrl(image, 'jingdongImage.png')); 499 | JD.imageSize = new Size(194 * 0.2 * this.basicSetting.scale, 78 * 0.2 * this.basicSetting.scale); 500 | } else { 501 | const imgStack = topStack.addStack(); 502 | if (this.isPlus) { 503 | imgStack.size = new Size(30 * this.basicSetting.scale, 30 * this.basicSetting.scale * 1.039); 504 | imgStack.backgroundImage = await this.getImageByUrl(this.plusBG, 'plusBGImage.png'); 505 | } 506 | const subStack = imgStack.addStack(); 507 | subStack.url = 'openapp.jdmobile://'; 508 | subStack.size = new Size(30 * this.basicSetting.scale, 30 * this.basicSetting.scale); 509 | subStack.cornerRadius = 30 / 2 * this.basicSetting.scale; 510 | subStack.backgroundImage = await this.getImageByUrl(this.basicSetting.customizeAvatar || this.userImage, `userImage_${this.userName}.png`); 511 | if (this.isPlus) { 512 | const userImg = subStack.addImage(await this.getImageByUrl(this.plusFG, 'plusFGImage.png')); 513 | } 514 | } 515 | topStack.addSpacer(); 516 | const jdBean = topStack.addText(this.beanCount.toString()); 517 | jdBean.font = Font.mediumSystemFont(20 * this.basicSetting.scale); 518 | jdBean.textColor = new Color('fa2d19'); // 右上角京豆数颜色 519 | jdBean.url = 'openapp.jdmobile://virtual?params=%7B%22category%22%3A%22jump%22%2C%22des%22%3A%22m%22%2C%22url%22%3A%22https%3A%2F%2Fbean.m.jd.com%2FbeanDetail%2Findex.action%3FresourceValue%3Dbean%22%7D'; 520 | const desStack = topStack.addStack(); 521 | desStack.layoutVertically(); 522 | desStack.addSpacer(5.5 * this.basicSetting.scale); 523 | const desText = desStack.addText(' 京豆'); 524 | desText.font = Font.mediumSystemFont(10 * this.basicSetting.scale); 525 | desText.textColor = new Color('fa2d19', 0.7); 526 | } 527 | 528 | // #####################京豆收支################### 529 | async setBeanShow(stack, textSize, imageSize) { 530 | const beanStack = stack.addStack(); 531 | // 今日收支 532 | const yestodayStack = beanStack.addStack(); 533 | yestodayStack.layoutVertically(); 534 | try { 535 | this.bean.ydayIncome = this.rangeTimer[this.yestoday][0] - this.rangeTimer[this.yestoday][1]; 536 | this.bean.ydayExpense = this.rangeTimer[this.yestoday][1]; 537 | this.bean.todayIncome = this.rangeTimer[this.today][0] - this.rangeTimer[this.today][1];; 538 | this.bean.todayExpense = this.rangeTimer[this.today][1]; 539 | } catch (e) { 540 | this.notify(this.name, '\u597d\u50cf\u4f60\u6628\u5929\u6ca1\u6709\u4f7f\u7528\u8be5\u5c0f\u7ec4\u4ef6\uff0c\u8bf7\u91cd\u7f6e\u4eac\u8c46\u6570\u636e'); 541 | } 542 | this.rowBeanCell( 543 | yestodayStack, 544 | this.bean.ydayExpense.toString(), 545 | this.bean.ydayIncome.toString(), 546 | textSize, 547 | '昨日', 548 | ); 549 | beanStack.addSpacer(); 550 | // 京豆图片 551 | const ddStack = beanStack.addStack(); 552 | ddStack.layoutVertically(); 553 | const ddImg = ddStack.addImage(await this.getImageByUrl(this.beanImg, 'beanImage.png')); 554 | ddImg.imageSize = new Size(imageSize, imageSize); 555 | beanStack.addSpacer(); 556 | // 昨日收支 557 | const todayStack = beanStack.addStack(); 558 | todayStack.layoutVertically(); 559 | this.rowBeanCell( 560 | todayStack, 561 | this.bean.todayExpense.toString(), 562 | this.bean.todayIncome.toString(), 563 | textSize, 564 | '今日', 565 | ); 566 | } 567 | 568 | // #####################京豆图表################### 569 | async setChartShow(stack, type) { 570 | let labels = [], data = []; 571 | Object.keys(this.rangeTimer).forEach((day) => { 572 | const value = this.rangeTimer[day]; 573 | const arrDay = day.split('-'); 574 | labels.push(arrDay[2]); 575 | if (this.chartSetting.countBean === '收入-支出') 576 | data.push(value[0]); 577 | else data.push(value[0] - value[1]); 578 | }); 579 | let cacheKey = `chart${type}Image${this.isSmall()}_${this.userName}.png`; 580 | let textSize = this.chartSetting.textSize; 581 | let linePadding = this.chartSetting.linePadding; 582 | let barPadding = this.chartSetting.barPadding; 583 | if (config.widgetFamily === 'small') { 584 | data.splice(0, 2); 585 | labels.splice(0, 2); 586 | textSize = this.chartSetting.textSize + 7; 587 | linePadding = this.chartSetting.linePadding + 10; 588 | barPadding = this.chartSetting.barPadding + 5; 589 | } 590 | let chartStr; 591 | switch (type) { 592 | case 2: 593 | chartStr = this.barChart(labels, data, textSize, barPadding, 'bar'); 594 | break; 595 | case 3: 596 | chartStr = this.barChart(labels, data, textSize, barPadding, 'line'); 597 | break; 598 | default: 599 | chartStr = this.lineChart(labels, data, textSize, linePadding); 600 | } 601 | const url = `https://quickchart.io/chart?w=${400}&h=${this.chartSetting.height}&f=png&c=${encodeURIComponent(chartStr)}`; 602 | const chart = await this.getImageByUrl(url, cacheKey, this.cacheChart); 603 | 604 | const chartStack = stack.addStack(); 605 | const chartImage = chartStack.addImage(chart); 606 | const beanDateStack = stack.addStack(); 607 | let showDays = data.length; 608 | for (let i = 0; i < showDays; i++) { 609 | beanDateStack.addSpacer(); 610 | let subStack = beanDateStack.addStack(); 611 | let beanDay = beanDateStack.addText(`${labels[i]}${this.chartSetting.dayText}`); 612 | beanDay.textColor = this.widgetColor; 613 | beanDay.font = new Font('ArialMT', this.chartSetting.daySize * this.basicSetting.scale); 614 | beanDay.textOpacity = 0.8; 615 | beanDateStack.addSpacer(); 616 | } 617 | } 618 | 619 | // #####################物流信息################### 620 | setPackageShow(stack) { 621 | const packageStack = stack.addStack(); 622 | const detailStack = packageStack.addStack(); 623 | detailStack.layoutVertically(); 624 | const titleStack = detailStack.addStack(); 625 | titleStack.centerAlignContent(); 626 | const title = titleStack.addText(this.package.title); 627 | title.lineLimit = 1; 628 | title.font = Font.mediumSystemFont(12 * this.basicSetting.scale); 629 | detailStack.addSpacer(2 * this.basicSetting.scale); 630 | const desc = detailStack.addText(this.package.desc); 631 | desc.lineLimit = 3; 632 | desc.font = Font.regularSystemFont(12 * this.basicSetting.scale); 633 | detailStack.addSpacer(2 * this.basicSetting.scale); 634 | const statusStack = detailStack.addStack(); 635 | const time = statusStack.addText(this.package.time); 636 | statusStack.addSpacer(); 637 | const status = statusStack.addText(this.package.status); 638 | ;[title, desc, time, status].map(t => t.textColor = this.widgetColor); 639 | ;[time, status].map(t => t.font = Font.regularSystemFont(9 * this.basicSetting.scale)); 640 | ;[time, status].map(t => t.textOpacity = 0.7); 641 | } 642 | 643 | // #####################金贴&钢镚################## 644 | async setCoinShow(stack, vertical = false) { 645 | await this.getExtraData(); 646 | const extraDataStack = stack.addStack(); 647 | const jtImage = await this.getImageByUrl(this.jingtieImg, 'jtImage.png'); 648 | const gbImage = await this.getImageByUrl(this.gangbengImg, 'gbImage.png'); 649 | const dataStack = extraDataStack.addStack(); 650 | if (vertical) dataStack.layoutVertically(); 651 | this.rowCell(dataStack, jtImage, this.extra.jingtie.toString(), '金贴'); 652 | if (vertical) extraDataStack.addSpacer(5 * this.basicSetting.scale); 653 | if (!vertical) dataStack.addSpacer(20 * this.basicSetting.scale); 654 | this.rowCell(dataStack, gbImage, this.extra.gangbeng.toString(), '钢镚'); 655 | } 656 | 657 | // #####################京东红包################## 658 | async setRedPackageShow(stack, small = false) { 659 | await this.getRedPackageData(); 660 | const walletImage = await this.getImageByUrl(this.walletImg, 'walletImage.png'); 661 | small ? this.rowSmallWalletCell(stack, walletImage, this.redPackage) : this.rowWalletCell(stack, walletImage, this.redPackage); 662 | } 663 | 664 | // #####################京东白条################## 665 | async setBaitiaoShow(stack, small = false) { 666 | const baitiaoImage = await this.getImageByUrl(this.baitiaoImg, 'baitiaoImage.png'); 667 | small ? this.rowSmallWalletCell(stack, baitiaoImage, this.baitiao) : this.rowWalletCell(stack, baitiaoImage, this.baitiao); 668 | } 669 | 670 | rowCell(stack, image, value, title) { 671 | const rowStack = stack.addStack(); 672 | rowStack.centerAlignContent(); 673 | const rowImage = rowStack.addImage(image); 674 | rowImage.imageSize = new Size(13 * this.basicSetting.scale, 13 * this.basicSetting.scale); 675 | rowStack.addSpacer(); 676 | const rowValue = rowStack.addText(value); 677 | rowValue.font = Font.mediumSystemFont(15 * this.basicSetting.scale); 678 | rowStack.addSpacer(); 679 | const rowTitle = rowStack.addText(title); 680 | rowTitle.font = Font.regularSystemFont(13 * this.basicSetting.scale); 681 | ;[rowValue, rowTitle].map(t => t.textColor = this.widgetColor); 682 | } 683 | 684 | rowBeanCell(stack, min, add, textSize, label) { 685 | const rowOne = stack.addStack(); 686 | const labelText = rowOne.addText(label); 687 | labelText.font = Font.regularSystemFont(10 * this.basicSetting.scale); 688 | labelText.textOpacity = 0.7; 689 | const rowTwo = stack.addStack(); 690 | const rowNumber = rowTwo.addText(add); 691 | rowNumber.font = Font.lightSystemFont(textSize); 692 | if (min < 0) { 693 | const rowThree = stack.addStack(); 694 | const minText = rowThree.addText(min); 695 | minText.font = Font.mediumSystemFont(10 * this.basicSetting.scale); 696 | minText.textColor = new Color('fa2d19'); // 支出京豆颜色 697 | } 698 | ;[labelText, rowNumber].map(t => t.textColor = this.widgetColor); 699 | } 700 | 701 | rowWalletCell(stack, image, data) { 702 | const stackOne = stack.addStack(); 703 | stackOne.centerAlignContent(); 704 | const stackImage = stackOne.addImage(image); 705 | stackImage.imageSize = new Size(127 * 0.17 * this.basicSetting.scale, 75 * 0.17 * this.basicSetting.scale); 706 | stackOne.addSpacer(5 * this.basicSetting.scale); 707 | const title = stackOne.addText(data.title); 708 | title.font = Font.regularSystemFont(13 * this.basicSetting.scale); 709 | stackOne.addSpacer(); 710 | const number = stackOne.addText(`${data.number}`); 711 | number.font = Font.mediumSystemFont(15 * this.basicSetting.scale); 712 | stackOne.addSpacer(); 713 | const desc = stackOne.addText(data.desc); 714 | desc.font = Font.regularSystemFont(10 * this.basicSetting.scale); 715 | desc.textOpacity = 0.7; 716 | ;[title, number, desc].map(t => t.textColor = this.widgetColor); 717 | } 718 | 719 | rowSmallWalletCell(stack, image, data) { 720 | const stackOne = stack.addStack(); 721 | stackOne.centerAlignContent(); 722 | const stackImage = stackOne.addImage(image); 723 | stackImage.imageSize = new Size(127 * 0.17 * this.basicSetting.scale, 75 * 0.17 * this.basicSetting.scale); 724 | stackOne.addSpacer(); 725 | const number = stackOne.addText(`${data.number}`); 726 | number.font = Font.mediumSystemFont(15 * this.basicSetting.scale); 727 | stack.addSpacer(5 * this.basicSetting.scale); 728 | const stackTwo = stack.addStack(); 729 | stackTwo.centerAlignContent(); 730 | const title = stackTwo.addText(data.title); 731 | title.font = Font.regularSystemFont(13 * this.basicSetting.scale); 732 | stackTwo.addSpacer(); 733 | const desc = stackTwo.addText(data.desc); 734 | desc.font = Font.regularSystemFont(10 * this.basicSetting.scale); 735 | desc.textOpacity = 0.7; 736 | ;[number, title, desc].map(t => t.textColor = this.widgetColor); 737 | } 738 | 739 | init = async () => { 740 | try { 741 | let beanCacheKey = `beanData${this.isSmall()}_${this.userName}`; 742 | let beanCacheData = !this.loadStringCache(beanCacheKey) ? {} : JSON.parse(this.loadStringCache(beanCacheKey)); 743 | let beanCache = beanCacheData.data ? beanCacheData.data.assetInfo.beanNum : 0; 744 | await this.TotalBean(); 745 | await this.wxData(); 746 | console.log(`京豆数据:${beanCache}`); 747 | console.log(`京豆数据:${this.beanCount}`); 748 | 749 | if (!this.cookie) return; 750 | if (Keychain.contains(this.CACHE_KEY)) { 751 | this.rangeTimer = JSON.parse(Keychain.get(this.CACHE_KEY)); 752 | if (this.rangeTimer.hasOwnProperty(this.today) && beanCache !== 0 && beanCache == this.beanCount) { 753 | this.cacheChart = this.funcSetting.alwaysRefreshChart ==='打开' ? false : true; 754 | console.log('京豆数据:无变化,使用缓存数据'); 755 | return; 756 | } 757 | 758 | this.rangeTimer[this.today] = [0, 0]; 759 | const timerKeys = Object.keys(this.rangeTimer); 760 | if (timerKeys.length > this.maxDays) { 761 | for (let i = 0; i < timerKeys.length - this.maxDays; i++) { 762 | delete this.rangeTimer[timerKeys[i]]; 763 | } 764 | Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); 765 | } 766 | 767 | this.timerKeys = [this.today]; 768 | } else { 769 | this.rangeTimer = this.getDay(5); 770 | this.timerKeys = Object.keys(this.rangeTimer); 771 | } 772 | await this.getAmountData(); 773 | } catch (e) { 774 | console.log(e); 775 | } 776 | }; 777 | 778 | getAmountData = async () => { 779 | let i = 0, 780 | page = 1; 781 | do { 782 | const response = await this.getJingBeanBalanceDetail(page); 783 | const result = response.code === '0'; 784 | console.log(`第${page}页:${result ? '请求成功' : '请求失败'}`); 785 | if (response.code === '3') { 786 | i = 1; 787 | // this.notify(this.name, response.message) 788 | console.log(response); 789 | } 790 | if (response && result) { 791 | page++; 792 | let detailList = response.jingDetailList; 793 | if (detailList && detailList.length > 0) { 794 | for (let item of detailList) { 795 | const dates = item.date.split(' '); 796 | if (this.timerKeys.indexOf(dates[0]) > -1) { 797 | const amount = Number(item.amount); 798 | this.rangeTimer[dates[0]][0] += amount; 799 | if (amount < 0) 800 | this.rangeTimer[dates[0]][1] += amount; 801 | } else { 802 | i = 1; 803 | Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); 804 | break; 805 | } 806 | } 807 | } 808 | } 809 | } while (i === 0); 810 | } 811 | 812 | getDay(dayNumber) { 813 | let data = {}; 814 | let i = dayNumber; 815 | do { 816 | const today = new Date(); 817 | const year = today.getFullYear(); 818 | const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; 819 | today.setTime(targetday_milliseconds); 820 | let month = today.getMonth() + 1; 821 | month = month >= 10 ? month : `0${month}`; 822 | let day = today.getDate(); 823 | day = day >= 10 ? day : `0${day}`; 824 | data[`${year}-${month}-${day}`] = [0, 0]; 825 | i--; 826 | } while (i >= 0); 827 | return data; 828 | } 829 | 830 | TotalBean = async () => { 831 | const dataName = '京豆数据'; 832 | let userCache = `beanData${this.isSmall()}`; 833 | const url = 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion?isLogin=1'; 834 | const options = { 835 | headers: { 836 | cookie: this.cookie, 837 | }, 838 | }; 839 | const response = await this.httpRequest(dataName, url, true, options, userCache); 840 | try { 841 | if (response.retcode === '0' && response['data']) { 842 | this.beanCount = response.data.assetInfo.beanNum; 843 | this.userImage = response.data.userInfo.baseInfo.headImageUrl.replace(/big/, 'mid') || 'https://img11.360buyimg.com/jdphoto/s120x120_jfs/t21160/90/706848746/2813/d1060df5/5b163ef9N4a3d7aa6.png'; 844 | this.nickName = response.data.userInfo.baseInfo.nickname; 845 | this.isPlus = response.data.userInfo.isPlusVip === '1' ? true : false; 846 | } else { 847 | this.notify(this.name, response.msg); 848 | console.log('京豆数据:获取失败,' + response.msg) 849 | } 850 | } catch (e) { 851 | console.log(e); 852 | console.log('京豆数据:获取失败,') 853 | } 854 | } 855 | 856 | wxData = async () => { 857 | const dataName = '微信数据'; 858 | let userCache = `wxData${this.isSmall()}`; 859 | const url = 'https://wxapp.m.jd.com/kwxhome/myJd/home.json?&useGuideModule=0&bizId=&brandId=&fromType=wxapp&isLogin=1'; 860 | const options = { 861 | headers: { 862 | cookie: this.cookie, 863 | }, 864 | }; 865 | const response = await this.httpRequest(dataName, url, true, options, userCache); 866 | try { 867 | if (response['user']) { 868 | this.jValue = response.user.uclass.replace(/[^0-9]/ig, ''); 869 | } else { 870 | // this.notify(this.name, response.msg); 871 | console.log('微信数据:获取失败,' + response.msg) 872 | } 873 | } catch (e) { 874 | console.log(e); 875 | } 876 | } 877 | 878 | getJingBeanBalanceDetail = async (page) => { 879 | try { 880 | const options = { 881 | url: `https://bean.m.jd.com/beanDetail/detail.json`, 882 | body: `page=${page}`, 883 | headers: { 884 | 'X-Requested-With': `XMLHttpRequest`, 885 | Connection: `keep-alive`, 886 | 'Accept-Encoding': `gzip, deflate, br`, 887 | 'Content-Type': `application/x-www-form-urlencoded; charset=UTF-8`, 888 | Origin: `https://bean.m.jd.com`, 889 | 'User-Agent': `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15`, 890 | Cookie: this.cookie, 891 | Host: `bean.m.jd.com`, 892 | Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, 893 | 'Accept-Language': `zh-cn`, 894 | Accept: `application/json, text/javascript, */*; q=0.01`, 895 | }, 896 | }; 897 | let params = {...options, method: 'POST'}; 898 | let request = new Request(params.url); 899 | Object.keys(params).forEach((key) => { 900 | request[key] = params[key]; 901 | }); 902 | return await request.loadJSON(); 903 | } catch (e) { 904 | console.log(e); 905 | } 906 | } 907 | 908 | getExtraData = async () => { 909 | const JTDataName = '金贴数据'; 910 | const JTUrl = 'https://ms.jr.jd.com/gw/generic/uc/h5/m/mySubsidyBalance'; 911 | const GBDataName = '钢镚数据'; 912 | const GBUrl = 'https://coin.jd.com/m/gb/getBaseInfo.html'; 913 | const options = { 914 | headers: { 915 | cookie: this.cookie, 916 | Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', 917 | }, 918 | }; 919 | try { 920 | const JTData = await this.httpRequest(JTDataName, JTUrl, true, options, 'jintieData'); 921 | const GBData = await this.httpRequest(GBDataName, GBUrl, true, options, 'gangbengData'); 922 | if (JTData.resultCode === 0) { 923 | this.extra.jingtie = JTData.resultData.data['balance']; 924 | } else { 925 | // this.notify(this.name, JTdata.resultMsg); 926 | console.log('金贴数据:获取失败,' + JTdata.resultMsg); 927 | }; 928 | if (GBData.gbBalance) this.extra.gangbeng = GBData.gbBalance; 929 | } catch(e) { 930 | console.log(e); 931 | } 932 | } 933 | 934 | getPackageData = async () => { 935 | const dataName = '包裹数据'; 936 | const url ='https://wq.jd.com/bases/wuliudetail/notify?sceneval=2&sceneval=2&g_login_type=1&callback'; 937 | const options = { 938 | headers: { 939 | cookie: this.cookie, 940 | Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', 941 | }, 942 | }; 943 | try { 944 | const data = await this.httpRequest(dataName, url, true, options, 'packageData'); 945 | if (data.errCode == 0 && data['dealLogList']) { 946 | console.log('包裹数据:获取成功'); 947 | console.log(`包裹数据:您有${data['dealLogList'].length}个包裹`); 948 | if (data['dealLogList'].length > 0) { 949 | let item = data.dealLogList[Math.floor(Math.random() * data['dealLogList'].length)] 950 | this.package.number = data.dealLogList.length; 951 | this.package.title = item['name']; 952 | this.package.desc = item['wlStateDesc']; 953 | this.package.time = item['createTime']; 954 | this.package.status = item['stateName']; 955 | } 956 | } else { 957 | console.log('包裹数据:获取失败'); 958 | } 959 | } catch (e) { 960 | console.log(e); 961 | } 962 | } 963 | 964 | getRedPackageData = async () => { 965 | const dataName = '红包数据'; 966 | const url = 967 | 'https://wq.jd.com/user/info/QueryUserRedEnvelopesV2?type=1&orgFlag=JD_PinGou_New&page=1&cashRedType=1&redBalanceFlag=1&channel=3&sceneval=2&g_login_type=1'; 968 | const options = { 969 | headers: { 970 | cookie: this.cookie, 971 | Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', 972 | }, 973 | }; 974 | try { 975 | const data = await this.httpRequest(dataName, url, true, options, 'redPackageData'); 976 | if (data.errcode === 0) { 977 | this.redPackage.number = data.data.balance ? data.data.balance : 0; 978 | if (data.data.expiredBalance && data.data.expiredBalance !== '') this.redPackage.desc = `即将过期${data.data.expiredBalance}`; 979 | } else { 980 | // this.notify(this.name, data.msg); 981 | console.log('红包数据:获取失败,' + data.msg); 982 | } 983 | } catch (e) { 984 | console.log(e); 985 | } 986 | } 987 | 988 | getJValue = async () => { 989 | const dataName = '京享数据'; 990 | const url = 'https://vip.m.jd.com/scoreDetail/current'; 991 | const options = { 992 | headers: { 993 | cookie: this.cookie, 994 | }, 995 | }; 996 | try { 997 | const data = await this.httpRequest(dataName, url, true, options, 'JValue'); 998 | if (data.code === 0) { 999 | this.jValue = data.model.scoreDescription.userScore.score; 1000 | } else { 1001 | console.log('京享数据:获取失败'); 1002 | }; 1003 | } catch (e) { 1004 | console.log(e); 1005 | } 1006 | } 1007 | 1008 | getBaitiaoData = async () => { 1009 | const dataName = '白条数据'; 1010 | const url = 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew'; 1011 | const options = { 1012 | body: 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}', 1013 | headers: { 1014 | cookie: this.cookie, 1015 | }, 1016 | }; 1017 | try { 1018 | const data = await this.httpRequest(dataName, url, true, options, 'baitiaoData', 'POST', false); 1019 | if (data.resultCode !== 0) { 1020 | // this.notify(this.name, data['resultMsg']); 1021 | return 1022 | } 1023 | this.baitiao.title = data['resultData']['data']['bill']['title']; 1024 | this.baitiao.number = data['resultData']['data']['bill']['amount'].replace(/,/g, ''); 1025 | this.baitiao.desc = data['resultData']['data']['bill']['buttonName'].replace(/最近还款日/, ''); 1026 | } catch (e) { 1027 | console.log(e); 1028 | } 1029 | } 1030 | 1031 | getFruitData = async () => { 1032 | const dataName = '东东农场'; 1033 | const url = 'https://api.m.jd.com/client.action?functionId=initForFarm'; 1034 | const options = { 1035 | body: 'body=version:4&appid=wh5&clientVersion=9.1.0', 1036 | headers: { 1037 | 'User-Agent': 'jdapp;iPhone;9.2.2;14.2;%E4%BA%AC%E4%B8%9C/9.2.2 CFNetwork/1206 Darwin/20.1.0', 1038 | 'Content-Type': 'application/x-www-form-urlencoded', 1039 | 'cookie': this.cookie, 1040 | }, 1041 | }; 1042 | try { 1043 | const data = await this.httpRequest(dataName, url, true, options, 'FruitData', 'POST', false); 1044 | if (data.msg && data.msg == 'not login') { 1045 | this.fruitState = "X"; 1046 | } 1047 | else if (data.farmUserPro.treeState == 2 || data.farmUserPro.treeState == 3) { 1048 | this.fruitState = "100"; 1049 | } 1050 | else if (data.farmUserPro.treeState == 0) { 1051 | this.fruitState = "X"; 1052 | } 1053 | else { 1054 | this.fruitState = Math.floor((data.farmUserPro.treeEnergy / data.farmUserPro.treeTotalEnergy) * 100).toString(); 1055 | } 1056 | } catch (e) { 1057 | console.log(e); 1058 | } 1059 | } 1060 | 1061 | getImageByUrl = async(url, cacheKey, useCache = true, logable = true) => { 1062 | if (this.CACHES.indexOf(cacheKey) < 0) { 1063 | this.CACHES.push(cacheKey); 1064 | this.settings.CACHES = this.CACHES; 1065 | this.saveSettings(false); 1066 | } 1067 | if (useCache) { 1068 | const cacheImg = this.loadImgCache(cacheKey); 1069 | if (cacheImg != undefined && cacheImg != null) { 1070 | if (logable) console.log(`使用缓存:${cacheKey}`); 1071 | return this.loadImgCache(cacheKey); 1072 | } 1073 | } 1074 | 1075 | try { 1076 | if (logable) console.log(`在线请求:${cacheKey}`); 1077 | const req = new Request(url); 1078 | const imgData = await req.load(); 1079 | const img = Image.fromData(imgData); 1080 | this.saveImgCache(cacheKey, img); 1081 | return img; 1082 | } catch (e) { 1083 | console.error(`图片加载失败:${e}`); 1084 | let cacheImg = this.loadImgCache(cacheKey); 1085 | if (cacheImg != undefined) { 1086 | console.log(`使用缓存图片:${cacheKey}`); 1087 | return cacheImg; 1088 | } 1089 | console.log(`使用预设图片`); 1090 | let ctx = new DrawContext(); 1091 | ctx.size = new Size(80, 80); 1092 | ctx.setFillColor(Color.darkGray()); 1093 | ctx.fillRect(new Rect(0, 0, 80, 80)); 1094 | return await ctx.getImage(); 1095 | } 1096 | } 1097 | 1098 | saveImgCache(cacheKey, img) { 1099 | if (!this.fm.fileExists(this.cachePath)) { 1100 | this.fm.createDirectory(this.cachePath, true); 1101 | }; 1102 | const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); 1103 | this.fm.writeImage(cacheFile, img); 1104 | } 1105 | 1106 | loadImgCache(cacheKey) { 1107 | const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); 1108 | const fileExists = this.fm.fileExists(cacheFile); 1109 | let img = undefined; 1110 | if (fileExists) { 1111 | img = Image.fromFile(cacheFile); 1112 | } 1113 | return img; 1114 | } 1115 | 1116 | httpRequest = async(dataName, url, json = true, options, key, method = 'GET', logable = this.funcSetting.logable === '打开') => { 1117 | let cacheKey = `${key}_${this.userName}`; 1118 | if (this.CACHES.indexOf(cacheKey) < 0) { 1119 | this.CACHES.push(cacheKey); 1120 | this.settings.CACHES = this.CACHES; 1121 | this.saveSettings(false); 1122 | } 1123 | let localCache = this.loadStringCache(cacheKey); 1124 | const lastCacheTime = this.getCacheModificationDate(cacheKey); 1125 | const timeInterval = Math.floor((this.getCurrentTimeStamp() - lastCacheTime) / 60); 1126 | 1127 | console.log(`${dataName}:缓存${timeInterval}分钟前,有效期${this.basicSetting.interval}分钟,${localCache.length}`); 1128 | 1129 | if (timeInterval < this.basicSetting.interval && localCache != null && localCache.length > 0) { 1130 | console.log(`${dataName}:读取缓存`); 1131 | return json ? JSON.parse(localCache) : localCache; 1132 | } 1133 | 1134 | let data = null; 1135 | try { 1136 | console.log(`${dataName}:在线请求`); 1137 | let req = new Request(url); 1138 | req.method = method; 1139 | Object.keys(options).forEach((key) => { 1140 | req[key] = options[key]; 1141 | }); 1142 | data = await (json ? req.loadJSON() : req.loadString()); 1143 | if ( 1144 | data.errCode === '0' || 1145 | data.msg === 'success' || 1146 | data.resultCode === 0 1147 | ) { 1148 | this.saveStringCache(cacheKey, json ? JSON.stringify(data) : data) 1149 | } 1150 | } catch (e) { 1151 | console.error(`${dataName}:请求失败:${e}`); 1152 | } 1153 | 1154 | localCache = this.loadStringCache(cacheKey); 1155 | 1156 | if (!data && localCache != null && localCache.length > 0) { 1157 | console.log(`${dataName}:获取失败,读取缓存`); 1158 | return json ? JSON.parse(localCache) : localCache; 1159 | } 1160 | 1161 | if (logable) { 1162 | console.log(`${dataName}:在线请求响应数据:${JSON.stringify(data)}`); 1163 | } 1164 | return data; 1165 | } 1166 | 1167 | loadStringCache(cacheKey) { 1168 | const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); 1169 | const fileExists = this.fm.fileExists(cacheFile); 1170 | let cacheString = ''; 1171 | if (fileExists) { 1172 | cacheString = this.fm.readString(cacheFile); 1173 | } 1174 | return cacheString; 1175 | } 1176 | 1177 | saveStringCache(cacheKey, content) { 1178 | if (!this.fm.fileExists(this.cachePath)) { 1179 | this.fm.createDirectory(this.cachePath, true); 1180 | }; 1181 | const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); 1182 | this.fm.writeString(cacheFile, content); 1183 | } 1184 | 1185 | getCacheModificationDate(cacheKey) { 1186 | const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); 1187 | const fileExists = this.fm.fileExists(cacheFile); 1188 | if (fileExists) { 1189 | return this.fm.modificationDate(cacheFile).getTime() / 1000; 1190 | } else { 1191 | return 0; 1192 | } 1193 | } 1194 | 1195 | getCurrentTimeStamp() { 1196 | return new Date().getTime() / 1000; 1197 | } 1198 | 1199 | timeFormat(time) { 1200 | let date; 1201 | if (time) { 1202 | date = new Date(time); 1203 | } else { 1204 | date = new Date(); 1205 | } 1206 | return ((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : '0' + (date.getMonth() + 1)) + '月' + (date.getDate() >= 10 ? date.getDate() : '0' + date.getDate()) + '日'; 1207 | } 1208 | 1209 | async updateCheck(version){ 1210 | let data; 1211 | try { 1212 | let updateCheck = new Request('https://raw.githubusercontent.com/anker1209/Scriptable/main/upcoming.json'); 1213 | data = await updateCheck.loadJSON(); 1214 | if (data.version != version) { 1215 | let updata = new Alert(); 1216 | updata.title = `有新版 ${data.version} 可用`; 1217 | updata.addAction('去Github更新'); 1218 | updata.addAction('网页版商店更新'); 1219 | updata.addCancelAction('稍后'); 1220 | updata.message = '\n更新说明:\n' + data.notes + '\n\n点击相应按钮更新脚本'; 1221 | let id = await updata.present(); 1222 | if (id == 0) { 1223 | Safari.openInApp('https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/JD-in-one-v2.js'); 1224 | } else if (id == 1) { 1225 | Safari.openInApp('http://scriptablejs.gitee.io/store/#/menu/myInfo'); 1226 | } else { 1227 | return; 1228 | } 1229 | } else { 1230 | let updata = new Alert(); 1231 | updata.title = '暂无更新'; 1232 | updata.addCancelAction('好的'); 1233 | updata.message = `\n当前版本 ${version} 为最新版本`; 1234 | await updata.present(); 1235 | } 1236 | } catch(e) { 1237 | console.log(e); 1238 | } 1239 | } 1240 | 1241 | async faqTable() { 1242 | const table = new UITable(); 1243 | table.showSeparators = false; 1244 | let data; 1245 | try { 1246 | let faq = new Request('https://raw.githubusercontent.com/anker1209/Scriptable/main/faq.json'); 1247 | data = await faq.loadJSON(); 1248 | let info = new UITableRow(); 1249 | info.height = parseFloat(data.height); 1250 | let desc = info.addText(data.update, data.desc); 1251 | desc.subtitleColor = Color.blue(); 1252 | desc.titleFont = Font.mediumSystemFont(14); 1253 | desc.subtitleFont = Font.systemFont(14); 1254 | table.addRow(info); 1255 | for (let i = 0; i < data.data.length; i++) { 1256 | let header = new UITableRow(); 1257 | header.backgroundColor = Color.dynamic(new Color('F5F5F5'), new Color('000000'));; 1258 | let heading = header.addText(data.data[i].name) 1259 | heading.titleFont = Font.mediumSystemFont(17); 1260 | heading.centerAligned(); 1261 | table.addRow(header); 1262 | data.data[i].item.forEach((faq) => { 1263 | let row = new UITableRow(); 1264 | row.height = parseFloat(faq['height']); 1265 | let rowtext = row.addText(faq['question'], faq['answer']); 1266 | rowtext.titleFont = Font.mediumSystemFont(16); 1267 | rowtext.titleColor = Color.blue(); 1268 | rowtext.subtitleFont = Font.systemFont(14); 1269 | rowtext.subtitleColor = Color.dynamic(new Color('000000', 0.7), new Color('ffffff', 0.7)); 1270 | table.addRow(row); 1271 | }); 1272 | } 1273 | } catch (e) { 1274 | console.log(e); 1275 | } 1276 | await table.present(); 1277 | } 1278 | 1279 | async settingCategory(table, item, outfit, category) { 1280 | let header = new UITableRow(); 1281 | let heading = header.addText(outfit) 1282 | heading.titleFont = Font.mediumSystemFont(17); 1283 | heading.centerAligned(); 1284 | table.addRow(header); 1285 | item.forEach((data) => { 1286 | Object.keys(data.option).forEach((key) => { 1287 | let row = new UITableRow(); 1288 | let rowIcon = row.addImageAtURL(data['icon']); 1289 | rowIcon.widthWeight = 100; 1290 | let rowtext = row.addText(data['title']); 1291 | rowtext.widthWeight = 400; 1292 | let rowNumber = row.addText(`${this.settings[category][key]} >`); 1293 | rowNumber.widthWeight = 500; 1294 | rowNumber.rightAligned(); 1295 | rowNumber.titleColor = Color.gray(); 1296 | rowNumber.titleFont = Font.systemFont(16); 1297 | rowtext.titleFont = Font.systemFont(16); 1298 | row.dismissOnSelect = false; 1299 | row.onSelect = async () => { 1300 | if (data.type == 'text') { 1301 | await this.alertInput(data['title'], data['desc'], category, data['option']); 1302 | } else if (data.type == 'menu') { 1303 | await this.showAlert(data['title'], data['desc'], data['menu'], category, key,); 1304 | } 1305 | await this.tableContent(table); 1306 | } 1307 | table.addRow(row); 1308 | }); 1309 | }); 1310 | table.reload(); 1311 | } 1312 | 1313 | async tableContent(table) { 1314 | const basic = [ 1315 | {type: 'text', title: '全局缩放比例', desc: '排版溢出、显示不全的请优先调低此数,建议递减0.05调整,如0.95、0.90……\n\n缺省值:1.00', option: {scale: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/scale.png'}, 1316 | {type: 'text', title: '京东标志大小', desc: '京东logo(形象狗)大小\n\n缺省值:30', option: {logo: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/logo.png'}, 1317 | {type: 'text', title: '用户头像大小', desc: '⚠️注意:若要修改头像,请在京东app上传后将缓存清除再运行脚本。\n\n缺省值:69', option: {userImage: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/userImage.png'}, 1318 | {type: 'text', title: '左侧栏宽度', desc: '左侧用户信息栏整体宽度\n\n缺省值:103', option: {userStack: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/userStack.png'}, 1319 | {type: 'text', title: '左右栏间距', desc: '左侧用户信息栏与右侧京豆数据间距\n\n缺省值:25', option: {division: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/division.png'}, 1320 | {type: 'text', title: '缓存时间', desc: '数据请求间隔时间\n请设置合适时间,避免频繁访问接口数据以及加载缓慢。单位:分钟\n\n缺省值:10', option: {interval: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/interval.png'}, 1321 | {type: 'menu', title: '缓存位置', desc: '将缓存保存在Local或者iCloud。\n\n缺省值:Local', option: {directory: ''}, menu: ['Local', 'iCloud'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/directory.png'}, 1322 | {type: 'text', title: '自定义昵称', desc: '自定义用户信息栏的昵称名称,\n留空将显示京东账号昵称。\n\n注意:单脚本多账户若使用自定义昵称,所有账户将同时显示此昵称,如需单独自定义昵称,请复制脚本单独设置。', option: {customizeName: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/customizeName.png'}, 1323 | {type: 'text', title: '自定义头像', desc: '自定义用户信息栏的头像,\n留空将显示京东APP头像。\n\n注意:单脚本多账户若使用自定义头像,所有账户将同时显示此头像,如需单独自定义头像,请复制脚本单独设置。', option: {customizeAvatar: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/customizeAvatar.png'}, 1324 | {type: 'menu', title: '小组件显示内容', desc: '\n缺省值:京豆、钱包数据', option: {smallShowType: ''}, menu: ['京豆、钱包数据', '个人信息'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/smallShowType.png'}, 1325 | {type: 'menu', title: '钱包显示类型', desc: '若要显示钱包内容,白条需关闭或者白条打开的情况下无待还白条。\n\n缺省值:红包', option: {walletShowType: ''}, menu: ['红包', '钢镚和金贴'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/walletShowType.png'}, 1326 | ]; 1327 | const chart = [ 1328 | {type: 'text', title: '图表高度', desc: '京豆数据未与日期对齐的,\n请调低此数值\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:130', option: {height: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/height.png'}, 1329 | {type: 'text', title: '日期文字大小', desc: '京豆图表底部日期文字大小\n\n缺省值:9', option: {daySize: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/daySize.png'}, 1330 | {type: 'text', title: '日期文字后缀', desc: '京豆图表底部日期文字后缀', option: {dayText: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/dayText.png'}, 1331 | {type: 'text', title: '京豆数文字大小', desc: '京豆图表数据文字大小\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:18', option: {textSize: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textSize.png'}, 1332 | {type: 'text', title: '京豆数白天颜色', desc: '⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:999999', option: {textDayColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textDayColor.png'}, 1333 | {type: 'text', title: '京豆数晚上颜色', desc: '⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:999999', option: {textNightColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textNightColor.png'}, 1334 | {type: 'text', title: '折线图线条颜色', desc: '支持渐变色,每个颜色之间以英文逗号分隔,颜色值必须带“#”。\n\n缺省值:#FA6859', option: {lineColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/lineColor.png'}, 1335 | {type: 'text', title: '折线图表顶边距', desc: '京豆折线图顶边距\n京豆数据在顶部被剪切显示不全的,\n请调高此数值。\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:15', option: {linePadding: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/linePadding.png'}, 1336 | {type: 'text', title: '柱状图表顶边距', desc: '京豆柱状图和曲线面积图顶边距\n京豆数据在顶部被剪切显示不全的,\n请调高此数值。\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:5', option: {barPadding: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/barPadding.png'}, 1337 | {type: 'menu', title: '小组件图表类型', desc: '\n缺省值:双日视图', option: {smallShowType: ''}, menu: ['双日视图', '折线图表', '柱状图表', '曲线面积图'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/smallShowType2.png'}, 1338 | {type: 'menu', title: '中组件图表类型', desc: '\n缺省值:双日视图', option: {showType: ''}, menu: ['双日视图', '折线图表', '柱状图表', '曲线面积图'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showType.png'}, 1339 | {type: 'menu', title: '每日京豆数计算', desc: '\n缺省值:收入-支出', option: {countBean: ''}, menu: ['收入-支出', '收入'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/countBean.png'}, 1340 | {type: 'menu', title: '多彩柱状图', desc: '设置为打开时仅对柱状图表生效\n\n缺省值:关闭', option: {colorful: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/colorful.png'}, 1341 | ]; 1342 | const func = [ 1343 | {type: 'menu', title: '白条信息', desc: '关闭或者打开后无待还白条的情况下,\n会显示基础设置里选择的钱包内容。\n\n缺省值:打开', option: {showBaitiao: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showBaitiao.png'}, 1344 | {type: 'menu', title: '包裹信息', desc: '只有中组件显示一条物流信息,\n若无物流信息会显示图表设置里选择的图表类型。\n\n缺省值:关闭', option: {showPackage: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showPackage.png'}, 1345 | {type: 'menu', title: '农场进度', desc: '显示东东农场种植进度。\n\n缺省值:打开', option: {showFruit: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showFruit.png'}, 1346 | {type: 'menu', title: '运行日志', desc: '出现数据异常请将此值设为true,\n查看运行日志。\n\n⚠️注意:\n查看运行日志需将缓存时间更改为0。\n\n缺省值:关闭', option: {logable: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/logable.png'}, 1347 | {type: 'menu', title: '刷新图表', desc: '打开,每次刷新组件会随机刷新图表颜色(仅柱状图表和曲线面积图);关闭,则只有在京豆数据有变化的情况下刷新图表颜色及数据。建议在排版调整没有问题后,设置为关闭。设置为打开会加长数据载入时间。\n\n⚠️注意:图表设置选项里修改图表高度、颜色、文字大小、顶边距需打开此选项以查看即时反馈。\n\n缺省值:打开', option: {alwaysRefreshChart: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/alwaysRefreshChart.png'}, 1348 | ]; 1349 | table.removeAllRows(); 1350 | let topRow = new UITableRow(); 1351 | let leftText = topRow.addButton('教程'); 1352 | leftText.widthWeight = 0.25; 1353 | leftText.onTap = async () => { 1354 | await Safari.open('https://github.com/anker1209/Scriptable#jd_in_one'); 1355 | } 1356 | let faqText = topRow.addButton('常见问题'); 1357 | faqText.widthWeight = 0.25; 1358 | faqText.leftAligned(); 1359 | faqText.onTap = async () => { 1360 | await this.faqTable(); 1361 | } 1362 | let versionText = topRow.addButton('版本检测'); 1363 | versionText.widthWeight = 0.25; 1364 | versionText.rightAligned(); 1365 | versionText.onTap = async () => { 1366 | await this.updateCheck(this.version); 1367 | } 1368 | let rightText = topRow.addButton('电报群'); 1369 | rightText.widthWeight = 0.25; 1370 | rightText.rightAligned(); 1371 | rightText.onTap = async () => { 1372 | await Safari.open('https://t.me/Scriptable_JS'); 1373 | } 1374 | table.addRow(topRow); 1375 | 1376 | let header = new UITableRow(); 1377 | let heading = header.addText('重置设置') 1378 | heading.titleFont = Font.mediumSystemFont(17); 1379 | heading.centerAligned(); 1380 | table.addRow(header); 1381 | let row1 = new UITableRow(); 1382 | let rowtext1 = row1.addText('重置缓存','若需要修改头像或数据显示错误,尝试此操作'); 1383 | rowtext1.titleFont = Font.systemFont(16); 1384 | rowtext1.subtitleFont = Font.systemFont(12); 1385 | rowtext1.subtitleColor = new Color('999999'); 1386 | row1.dismissOnSelect = false; 1387 | row1.onSelect = async () => { 1388 | const options = ['取消', '重置']; 1389 | const message = '所有在线请求的数据缓存将会被清空'; 1390 | const index = await this.generateAlert(message, options); 1391 | if (index === 0) return; 1392 | this.fm.remove(this.cachePath); 1393 | delete this.settings['CACHES']; 1394 | this.saveSettings(); 1395 | } 1396 | table.addRow(row1); 1397 | let row2 = new UITableRow(); 1398 | let rowtext2 = row2.addText('重置京豆数据','若京豆数据缺失或显示有误,尝试此操作'); 1399 | rowtext2.titleFont = Font.systemFont(16); 1400 | rowtext2.subtitleFont = Font.systemFont(12); 1401 | rowtext2.subtitleColor = new Color('999999'); 1402 | row2.dismissOnSelect = false; 1403 | row2.onSelect = async () => { 1404 | const options = ['取消', '重置']; 1405 | const message = '若缺少京豆数据或显示为0(双日视图或图表的京豆数)采用此操作。京豆数据重置后,将会重新抓取近6天的京豆明细。请勿频繁使用,会产生大量数据'; 1406 | const index = await this.generateAlert(message, options); 1407 | if (index === 0) return; 1408 | Keychain.remove(this.settings.CACHE_KEY); 1409 | delete this.settings.CACHE_KEY; 1410 | this.saveSettings(); 1411 | } 1412 | table.addRow(row2); 1413 | let row3 = new UITableRow(); 1414 | let rowtext3 = row3.addText('重置设置参数','设置参数绑定脚本文件名,请勿随意更改脚本文件名'); 1415 | rowtext3.titleFont = Font.systemFont(16); 1416 | rowtext3.subtitleFont = Font.systemFont(12); 1417 | rowtext3.subtitleColor = new Color('999999'); 1418 | row3.dismissOnSelect = false; 1419 | row3.onSelect = async () => { 1420 | const options = ['取消', '重置']; 1421 | const message = '本菜单里的所有设置参数将会重置为默认值,重置后请重新打开设置菜单'; 1422 | const index = await this.generateAlert(message, options); 1423 | if (index === 0) return; 1424 | delete this.settings['basicSetting']; 1425 | delete this.settings['chartSetting']; 1426 | delete this.settings['funcSetting']; 1427 | this.saveSettings(); 1428 | } 1429 | table.addRow(row3); 1430 | await this.settingCategory(table, basic, '基础设置', 'basicSetting'); 1431 | await this.settingCategory(table, chart, '图表设置', 'chartSetting'); 1432 | await this.settingCategory(table, func, '功能设置', 'funcSetting'); 1433 | } 1434 | 1435 | async editSettings() { 1436 | const table = new UITable(); 1437 | table.showSeparators = true; 1438 | await this.tableContent(table); 1439 | await table.present(true); 1440 | } 1441 | 1442 | alertInput = async (title, desc, category, opt = {}) => { 1443 | const a = new Alert(); 1444 | a.title = title; 1445 | a.message = !desc ? '' : desc; 1446 | let key = Object.keys(opt)[0]; 1447 | a.addTextField(key, `${this.settings[category][key]}`); 1448 | a.addAction('确定'); 1449 | a.addCancelAction('取消'); 1450 | const id = await a.presentAlert(); 1451 | if (id === -1) return; 1452 | this.settings[category][key] = a.textFieldValue(0); 1453 | this.saveSettings(); 1454 | }; 1455 | 1456 | async showAlert(title, message, options, category, key) { 1457 | let alert = new Alert(); 1458 | alert.title = title; 1459 | alert.message = message; 1460 | alert.addCancelAction('取消') 1461 | for (const option of options) { 1462 | alert.addAction(option); 1463 | }; 1464 | let id = await alert.presentAlert(); 1465 | if (id === -1) return; 1466 | this.settings[category][key] = options[id]; 1467 | this.saveSettings(); 1468 | } 1469 | 1470 | run = (filename, args) => { 1471 | if(!this.settings.basicSetting) this.settings.basicSetting = this.basicSetting; 1472 | Object.keys(this.basicSetting).forEach((key) => { 1473 | if(!this.settings.basicSetting.hasOwnProperty(key)) 1474 | this.settings['basicSetting'][key] = this.basicSetting[key]; 1475 | }); 1476 | if(!this.settings.chartSetting) this.settings.chartSetting = this.chartSetting; 1477 | Object.keys(this.chartSetting).forEach((key) => { 1478 | if(!this.settings.chartSetting.hasOwnProperty(key)) 1479 | this.settings['chartSetting'][key] = this.chartSetting[key]; 1480 | }); 1481 | if(!this.settings.funcSetting) this.settings.funcSetting = this.funcSetting; 1482 | Object.keys(this.funcSetting).forEach((key) => { 1483 | if(!this.settings.funcSetting.hasOwnProperty(key)) 1484 | this.settings['funcSetting'][key] = this.funcSetting[key]; 1485 | }); 1486 | if(!this.settings.CACHES) this.settings.CACHES = []; 1487 | this.CACHES = this.settings.CACHES; 1488 | if (this.settings['basicSetting']['directory'] === 'iCloud') this.fm = FileManager.iCloud(); 1489 | this.cachePath = this.fm.joinPath(this.fm.documentsDirectory(), this.CACHE_FOLDER); 1490 | 1491 | if (config.runsInApp) { 1492 | this.registerAction('参数配置', this.editSettings, 'https://gitee.com/anker1209/image/raw/master/jd/setting.png'); 1493 | this.registerAction('账号设置', async () => { 1494 | const index = await this.generateAlert('设置账号信息', [ 1495 | '网站登录', 1496 | '手动输入', 1497 | ]); 1498 | if (index === 0) { 1499 | await this.jdWebView(); 1500 | } else { 1501 | await this.setAlertInput('账号设置', '京东账号cookie\n\n⚠️\n用户名和cookie必须输入!\n多账号注意用户名不要重复!', { 1502 | username: '用户名,必须输入!多账号勿重复!', 1503 | cookie: 'Cookie', 1504 | }); 1505 | } 1506 | }, 'https://gitee.com/anker1209/image/raw/master/jd/account.png'); 1507 | this.registerAction('代理缓存', this.actionSettings, 'https://gitee.com/anker1209/image/raw/master/jd/boxjs.png'); 1508 | this.registerAction('基础设置', this.setWidgetConfig, 'https://gitee.com/anker1209/image/raw/master/jd/preferences.png'); 1509 | } 1510 | Object.keys(this.settings['basicSetting']).forEach((key) => { 1511 | if (key == 'customizeName' || key == 'customizeAvatar' || key == 'smallShowType' || key == 'walletShowType' || key == 'directory') { 1512 | this.basicSetting[key] = this.settings['basicSetting'][key]; 1513 | } else if (!isNaN(this.settings['basicSetting'][key])) { 1514 | this.basicSetting[key] = parseFloat(this.settings['basicSetting'][key]); 1515 | } 1516 | }); 1517 | Object.keys(this.settings['chartSetting']).forEach((key) => { 1518 | if (key == 'textDayColor' || key == 'textNightColor' || key =='showType' || key == 'smallShowType' || key == 'countBean' || key == 'colorful' || key == 'lineColor' || key == 'dayText') { 1519 | this.chartSetting[key] = this.settings['chartSetting'][key]; 1520 | } else if (!isNaN(this.settings['chartSetting'][key])) { 1521 | this.chartSetting[key] = parseFloat(this.settings['chartSetting'][key]); 1522 | } 1523 | }); 1524 | Object.keys(this.settings['funcSetting']).forEach((key) => { 1525 | this.funcSetting[key] = this.settings['funcSetting'][key]; 1526 | }); 1527 | 1528 | 1529 | }; 1530 | 1531 | jdWebView = async () => { 1532 | const webView = new WebView(); 1533 | const url = 1534 | 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; 1535 | await webView.loadURL(url); 1536 | await webView.present(true); 1537 | const req = new Request( 1538 | 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', 1539 | ); 1540 | req.method = 'POST'; 1541 | req.body = 1542 | 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; 1543 | await req.loadJSON(); 1544 | const cookies = req.response.cookies; 1545 | const account = {username: '', cookie: ''}; 1546 | const cookie = []; 1547 | cookies.forEach((item) => { 1548 | const value = `${item.name}=${item.value}`; 1549 | if (item.name === 'pt_key') cookie.push(value); 1550 | if (item.name === 'pt_pin') { 1551 | account.username = item.value; 1552 | cookie.push(value); 1553 | } 1554 | }); 1555 | account.cookie = cookie.join('; '); 1556 | console.log(account); 1557 | 1558 | if (account.cookie) { 1559 | this.settings = {...this.settings, ...account}; 1560 | this.saveSettings(false); 1561 | console.log(`${this.name}: cookie获取成功,请关闭窗口!`); 1562 | this.notify(this.name, 'cookie获取成功,请关闭窗口!'); 1563 | } 1564 | }; 1565 | 1566 | _loadJDCk = async () => { 1567 | try { 1568 | this.CookiesData = await this.getCache('CookiesJD', false) 1569 | if (this.CookiesData) { 1570 | this.CookiesData = this.transforJSON(this.CookiesData) 1571 | } else { 1572 | throw "未获取到数据" 1573 | } 1574 | return true; 1575 | } catch (e) { 1576 | console.log(e); 1577 | this.CookiesData = []; 1578 | return false; 1579 | } 1580 | }; 1581 | 1582 | async actionSettings() { 1583 | try { 1584 | const table = new UITable(); 1585 | if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; 1586 | // 如果是节点,则先远程获取 1587 | this.settings.cookieData = this.CookiesData; 1588 | this.saveSettings(false); 1589 | this.CookiesData.map((t, index) => { 1590 | const r = new UITableRow(); 1591 | r.addText(`parameter:${index} ${t.userName}`); 1592 | r.onSelect = (n) => { 1593 | this.settings.username = t.userName; 1594 | this.settings.cookie = t.cookie; 1595 | this.saveSettings(); 1596 | }; 1597 | table.addRow(r); 1598 | }); 1599 | let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; 1600 | if (this.settings.cookie) { 1601 | body += ',或者使用当前选中Ck:' + this.settings.username; 1602 | } 1603 | this.notify(this.name, body); 1604 | table.present(false); 1605 | } catch (e) { 1606 | console.log(e); 1607 | await this.notify( 1608 | `${this.name} - BoxJS 数据读取失败`, 1609 | '请检查 BoxJS 域名是否为代理复写的域名,如(boxjs.net 或 boxjs.com)。\n若没有配置 BoxJS 相关模块,请点击通知查看教程', 1610 | 'https://chavyleung.gitbook.io/boxjs/awesome/videos' 1611 | ) 1612 | } 1613 | } 1614 | 1615 | async getCookie() { 1616 | this.JDindex = typeof args.widgetParameter === 'string' 1617 | ? parseInt(args.widgetParameter) 1618 | : false; 1619 | let _md5 = this.md5(module.filename + this.en); 1620 | if (this.funcSetting.logable === '打开') console.log('当前配置内容:' + JSON.stringify(this.settings)); 1621 | try { 1622 | if (this.JDindex !== false && this.JDindex + 1 > 0) { 1623 | 1624 | if (!(await this._loadJDCk())) this.CookiesData = this.settings.cookieData 1625 | 1626 | this.cookie = this.CookiesData[this.JDindex]['cookie']; 1627 | this.userName =this.CookiesData[this.JDindex]["userName"]; 1628 | } else { 1629 | this.userName = this.settings.username; 1630 | this.cookie = this.settings.cookie; 1631 | } 1632 | if (!this.cookie) throw "京东 CK 获取失败"; 1633 | this.userName = decodeURI(this.userName); 1634 | this.CACHE_KEY = `cache_${_md5}_` + this.userName; 1635 | this.settings.CACHE_KEY = this.CACHE_KEY; 1636 | this.saveSettings(false); 1637 | return true; 1638 | } catch (e) { 1639 | this.notify("错误提示", e); 1640 | return false; 1641 | } 1642 | } 1643 | 1644 | async render() { 1645 | await this.getCookie(); 1646 | if (!this.cookie || !this.userName) { 1647 | this.notify(this.name, 'cookie或用户名未设置'); 1648 | return; 1649 | }; 1650 | await this.init(); 1651 | await this.getPackageData(); 1652 | if (this.funcSetting.showBaitiao === '打开') await this.getBaitiaoData(); 1653 | if (this.funcSetting.showFruit === '打开') await this.getFruitData(); 1654 | if (this.funcSetting.logable === '打开') console.log(this.rangeTimer); 1655 | const widget = new ListWidget(); 1656 | const padding = 14 * this.basicSetting.scale; 1657 | widget.setPadding(padding, padding, padding, padding); 1658 | await this.getWidgetBackgroundImage(widget); 1659 | if (this.widgetFamily === 'medium') { 1660 | return await this.renderMedium(widget); 1661 | } else if (this.widgetFamily === 'large') { 1662 | return await this.renderLarge(widget); 1663 | } else { 1664 | return await this.renderSmall(widget); 1665 | } 1666 | } 1667 | } 1668 | 1669 | await Runing(Widget, '', false); 1670 | -------------------------------------------------------------------------------- /scripts/none.cpp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /upcoming.json: -------------------------------------------------------------------------------- 1 | { 2 | "version":"2.2.7", 3 | "date": "2022.01.04", 4 | "notes": "\n- 脚本调整为直接读取BoxJS内的CK,多账号用户无需点击代理缓存读取CK" 5 | } 6 | --------------------------------------------------------------------------------