diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 6569aa5..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.gitignore b/.gitignore index d50d748..815c85a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ /.idea /.history test.js -Env.js \ No newline at end of file +Env.js +.DS_Store +.DS_Store +/.output diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c8b9c24 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5" +} \ No newline at end of file diff --git a/2YaInstall.js b/2YaInstall.js index d01b069..3da117c 100644 --- a/2YaInstall.js +++ b/2YaInstall.js @@ -2,123 +2,69 @@ // These must be at the very top of the file. Do not edit. // icon-color: brown; icon-glyph: download; -const scripts = [ - { - moduleName: "2YaInstall", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/2YaInstall.js", - }, - { - moduleName: "Env", - url: - "https://raw.githubusercontent.com/GideonSenku/Scriptable/master/Env.js", - // 感谢G大的 EnvJS (https://github.com/GideonSenku) - }, - { - moduleName: "Install", - url: - "https://raw.githubusercontent.com/GideonSenku/Scriptable/master/Install%20Scripts.js", - // 感谢G大的 脚本库安装包 (https://github.com/GideonSenku) - }, - { - moduleName: "Calendar", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Calendar.js", - }, - { - moduleName: "Birthday", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Birthday.js", - }, - { - moduleName: "HistoryDay", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/HistoryToday.js", - }, - { - moduleName: "DmYY", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js", - }, - { - moduleName: "JDDou", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/JDDou.js", - }, - { - moduleName: "JDDouK", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/JDDouK.js", - }, - { - moduleName: "JDWuLiu", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/JDWuLiu.js", - }, - { - moduleName: "BiliBiliWatch", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/BiliBiliWatch.js", - }, - { - moduleName: "ZXTrains", - url: - "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/ZXTrains.js", - }, -]; +//订阅地址 +const subscriptionURL = 'https://raw.githubusercontent.com/dompling/Scriptable/master/install.json'; class YaYaInstall { - constructor() { - this.request = new Request(""); - this.files = FileManager.iCloud(); - this.rootPath = this.files.documentsDirectory(); - this.defaultHeaders = { - Accept: "*/*", - "Content-Type": "application/json", - }; - } + constructor() { + this.request = new Request(''); + this.files = FileManager.iCloud(); + this.rootPath = this.files.documentsDirectory(); + this.defaultHeaders = { + Accept: '*/*', + 'Content-Type': 'application/json', + }; + } - saveFileName = (fileName) => { - const hasSuffix = fileName.lastIndexOf(".") + 1; - return !hasSuffix ? `${fileName}.js` : fileName; - }; + saveFileName = (fileName) => { + const hasSuffix = fileName.lastIndexOf('.') + 1; + return !hasSuffix ? `${fileName}.js` : fileName; + }; - write = (fileName, content) => { - let file = this.saveFileName(fileName); - const filePath = `${this.rootPath}/${file}`; - FileManager.iCloud().writeString(filePath, content); - return true; - }; + write = (fileName, content) => { + let file = this.saveFileName(fileName); + const filePath = `${this.rootPath}/${file}`; + FileManager.iCloud().writeString(filePath, content); + return true; + }; - fetchUrlString = async ({ url, headers = {} }, callback = () => {}) => { - this.request.url = url; - this.request.method = "GET"; - this.request.headers = { - ...headers, - ...this.defaultHeaders, - }; - const data = await this.request.loadString(); - callback(this.request.response, data); - return data; - }; + fetchUrlString = async ({url, headers = {}}, callback = () => {}) => { + this.request.url = url; + this.request.method = 'GET'; + this.request.headers = { + ...headers, + ...this.defaultHeaders, + }; + const data = await this.request.loadString(); + callback(this.request.response, data); + return data; + }; - saveFile = async ({ moduleName, url }) => { - const header = `// Variables used by Scriptable. + saveFile = async ({moduleName, url}) => { + const header = `// Variables used by Scriptable. // These must be at the very top of the file. Do not edit. // icon-color: deep-gray; icon-glyph: file-code;\n`; - const content = await this.fetchUrlString({ url }); - const fileHeader = content.includes("icon-color") ? `` : header; - this.write(`${moduleName}`, `${fileHeader}${content}`); - }; + const content = await this.fetchUrlString({url}); + const fileHeader = content.includes('icon-color') ? `` : header; + this.write(`${moduleName}`, `${fileHeader}${content}`); + }; - install = async () => { - console.log("🤖更新开始!"); - for (const script of scripts) { - await this.saveFile(script); - console.log(script.moduleName + ":更新成功"); - } - console.log("🤖更新结束!"); - }; + install = async () => { + console.log('🤖更新开始!'); + const req = new Request(subscriptionURL); + const subscription = await req.loadJSON(); + const apps = subscription.apps; + for (const script of apps) { + await this.saveFile({moduleName: script.name, url: script.scriptURL}); + if (script.depend) { + for (const item of script.depend) { + await this.saveFile({moduleName: item.name, url: item.scriptURL}); + } + } + // console.log(script.moduleName + ':更新成功'); + } + console.log('🤖更新结束!'); + }; } await new YaYaInstall().install(); diff --git a/2YaInstall.scriptable b/2YaInstall.scriptable deleted file mode 100644 index 1b0d113..0000000 --- a/2YaInstall.scriptable +++ /dev/null @@ -1,12 +0,0 @@ -{ - "always_run_in_app" : false, - "icon" : { - "color" : "cyan", - "glyph" : "cloud-download-alt" - }, - "name" : "2YaInstall", - "script" : "\/\/ Variables used by Scriptable.\n\/\/ These must be at the very top of the file. Do not edit.\n\/\/ icon-color: brown; icon-glyph: download;\n\nconst scripts = [\n {\n moduleName: \"Env\",\n url:\n \"https:\/\/raw.githubusercontent.com\/GideonSenku\/Scriptable\/master\/Env.js\",\n },\n {\n moduleName: \"2YaInstall\",\n url:\n \"https:\/\/raw.githubusercontent.com\/dompling\/Scriptable\/master\/2YaInstall.js\",\n },\n {\n moduleName: \"Calendar\",\n url:\n \"https:\/\/raw.githubusercontent.com\/dompling\/Scriptable\/master\/birthdayCountDown\/Components\/Calendar.js\",\n },\n {\n moduleName: \"Birthday\",\n url:\n \"https:\/\/raw.githubusercontent.com\/dompling\/Scriptable\/master\/birthdayCountDown\/index.js\",\n },\n];\n\nclass YaYaInstall {\n constructor() {\n this.request = new Request(\"\");\n this.files = FileManager.iCloud();\n this.rootPath = this.files.documentsDirectory();\n this.defaultHeaders = {\n Accept: \"*\/*\",\n \"Content-Type\": \"application\/json\",\n };\n }\n\n saveFileName = (fileName) => {\n const hasSuffix = fileName.lastIndexOf(\".\") + 1;\n return !hasSuffix ? `${fileName}.js` : fileName;\n };\n\n write = (fileName, content) => {\n let file = this.saveFileName(fileName);\n const filePath = `${this.rootPath}\/${file}`;\n FileManager.iCloud().writeString(filePath, content);\n return true;\n };\n\n fetchUrlString = async ({ url, headers = {} }, callback = () => {}) => {\n this.request.url = url;\n this.request.method = \"GET\";\n this.request.headers = {\n ...headers,\n ...this.defaultHeaders,\n };\n const data = await this.request.loadString();\n callback(this.request.response, data);\n return data;\n };\n\n saveFile = async ({ moduleName, url }) => {\n const header = `\/\/ Variables used by Scriptable.\n \/\/ These must be at the very top of the file. Do not edit.\n \/\/ icon-color: deep-gray; icon-glyph: file-code;\\n`;\n const content = await this.fetchUrlString({ url });\n const fileHeader = content.includes(\"icon-color\") ? `` : header;\n this.write(`${moduleName}`, `${fileHeader}${content}`);\n };\n\n install = () => {\n console.log(\"🤖更新开始!\");\n scripts.forEach(async (script) => {\n await this.saveFile(script);\n });\n console.log(\"🤖更新结束!\");\n };\n}\nnew YaYaInstall().install();\n", - "share_sheet_inputs" : [ - - ] -} \ No newline at end of file diff --git a/JDDou/jddnew.png b/JDDou/jddnew.png new file mode 100644 index 0000000..4251a3a Binary files /dev/null and b/JDDou/jddnew.png differ diff --git a/README.md b/README.md index 72d7b4c..c3fe449 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,16 @@ -# scriptable +# scriptable [Tg电报群](https://t.me/Scriptable_JS) -> 将文件 [2YaInstall.scriptable](https://raw.githubusercontent.com/dompling/Scriptable/master/2YaInstall.js) 导入到 scriptable 软件中,点击运行即可 +>首先安装 TF版的 [scriptable](https://testflight.apple.com/join/uN1vTqxk) -> 如果需要单个安装 [widget.Install](https://raw.githubusercontent.com/dompling/Scriptable/master/widget.Install.js) 安装导入到 scriptable,添加订阅,然后选择即可获取安装列表 +> 网页安装:(推荐:⭐️⭐️⭐️⭐️⭐️)[WebStore](https://dompling.github.io/store/#/menu) +> 安装教程:[网页安装步骤](https://t.me/Scriptable_JS/101536) + +- 作者:@2Ya - 订阅安装包 + +- 作者: @2214962083 - 订阅安装包 + +- 作者: @anker1209 - 订阅安装包 + ##### BoxJs 简单说明可看作者[BoxJs 仓库地址](https://github.com/chavyleung/scripts/) @@ -14,20 +22,40 @@ BoxJS 数据,是根据 圈 X,Loon , Surge 做的代理功能,教程如下 # 组件列表 -| 名称 | 说明 | 示例参数 | +> 特别说明:关于京东的小组件都支持多账号,但是必须用代理脚本去抓取 ck,通过选择设置下标的方式来切换多个账号。[京东 Cookie](https://raw.githubusercontent.com/dompling/Script/master/jd/JD_extra_cookie.js) + +| 名称 | 说明 | 备注 | | ----------------------------- | ---------- | --------------------------------------------------------------------------------------------------- | | [破壳日](Scripts/Birthday.js) | BoxJs 数据 | | -| [历史上的今天](historyToDay/) | | | +| [历史上的今天](Scripts/HistoryToday.js) | | | | [京东豆收支明细](Scripts/JDDou.js) | BoxJs 数据 | parameter: 0 京东 ck 下标 ,也可根据 APP 提示自行选择 | | [京东豆收支 K 线图](Scripts/JDDouK.js) | BoxJs 数据 | parameter: 0 京东 ck 下标 ,也可根据 APP 提示自行选择 | | [京东商品物流](Scripts/JDWuLiu.js) | BoxJs 数据 | parameter: 0 京东 ck 下标 ,也可根据 APP 提示自行选择 | | [哔哩哔哩今日番剧](Scripts/BiliBili.js) | | | | [哔哩哔哩关注消息](Scripts/BiliBiliWatch.js) | BoxJs 数据 | [哔哩哔哩 CK](https://raw.githubusercontent.com/dompling/Script/master/BiliBili/bilibili.cookie.js) | -| [智联火车票提醒](Scripts/ZXTrains.js) | BoxJs 数据,请根据教程来使用 | [购买火车票重写教程](https://raw.githubusercontent.com/dompling/Script/master/ZXTrians/ZXTrains.js) | +| [哔哩哔哩 UP 主](Scripts/BiliBiliUp.js) | | parameter:50952087 ,(mid:50952087)获取方式,打开 B 站Up主个人空间,复制地址的数字。如[https://space.bilibili.com/50952087/](https://space.bilibili.com/50952087/)的 mid 为 '50952087' | +| [智联火车票提醒](Scripts/ZXTrains.js) | BoxJs 数据,请根据教程来使用 | [购买火车票重写教程](https://raw.githubusercontent.com/dompling/Script/master/ZXTrians/ZXTrains.js) | +| [健康步数](Scripts/Health.js) | 需要配合[健康数据](https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf)捷径进行使用 | 捷径运行之后自动生成数据 | +| [桌面联系人](Scripts/Contact.js) | 显示桌面联系人 | parameter:YaYa (输入相应的手机联系人的名字,会自动关联搜索通讯录,可以是姓,可以是名字,可以是姓+名字 ) | +| [Telegram](Scripts/Telegram.js) | 显示桌面订阅 | parameter: Durov (输入相应的电报名) | +| [YouTube](Scripts/YouTube.js) | 显示桌面订阅 | parameter: Durov (输入相应的油管作者名,若不显示输入作者订阅地址再试) | +| [中国电信](Scripts/ChinaTelecom.js) | BoxJs 数据 | 本组件读取 boxjs 缓存,其他用户请自行抓取 authToken,Cookie | +| [中国移动](Scripts/ChinaMobile.js) | BoxJs 数据 | 本组件读取 boxjs 缓存,其他用户请自行抓取 | +| [机场+签到](Scripts/VpnBoard.js) | 手动新增账号 | parameter:下标 | +| [机场+签到 第二版](Scripts/VPNBoardPress.js) | 手动新增账号 | parameter:下标 | +| [机场订阅流量](Scripts/VPNSubscription.js) | 手动新增账号 | parameter:下标 | +| [v2board机场模板](Scripts/VPNV2Bord.js) | 手动新增账号 | parameter:下标 | +| [毒汤日历](Scripts/PoisonCalendar.js) | BoxJS数据 |[cookie 获取方式](https://raw.githubusercontent.com/dompling/Script/master/DJT/djt.cookie.js) | +| [疫情日报](https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/COVID-19.js) | 地区拼音,cd|xian 般城市直接首字母,可能不显示的时候试试全部拼音 | +| [今日油价](https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/TodayOilPrice.js) | BoxJS数据 | 自行申请腾讯地图的 token,以显示附近加油站 | + +# 赞赏码 + ## 特别感谢 - - [@GideonSenku](https://github.com/GideonSenku) - [@NobyDa](https://github.com/NobyDa) - [@chavyleung](https://github.com/chavyleung) - [@lxk0301](https://github.com/lxk0301) +- [@『Hell Cell』](https://t.me/HellCellZC123) +- [@xinian](https://github.com/58xinian) 提供 UI 帮助 diff --git a/Scripts/12123.js b/Scripts/12123.js new file mode 100644 index 0000000..3714c6c --- /dev/null +++ b/Scripts/12123.js @@ -0,0 +1,261 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: car; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +const API_PARAMS = { + api4: 'biz.vio.detail.query', + infoURL: 'https://miniappcsfw.122.gov.cn:8443/openapi/invokeApi/business/biz', + api1: 'biz.vio.unhandledVioCount.query', + productId: 'p10000000000000000001', + alipay: 'alipays://platformapi/startapp?appId=2019050964403523', + api2: 'biz.vio.peccancyChannelList.query', + status: + 'alipays://platformapi/startapp?appId=2019050964403523&page=pages%2Flicense%2Flicense', + update: 'https://gitcode.net/4qiao/scriptable/raw/master/api/violation.js', + api3: 'biz.vio.peccancyUnhandleInfoList.query', + Ver: 'Version 1.2\n\nverifyToken过期需打开Quantumult-X', +}; + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg, { + lightBgColor: '#2581f2', + darkBgColor: '#2581f2', + darkColor: '#fff', + lightColor: '#fff', + }); + this.en = '12123'; + this.name = '交管 12123'; + config.runsInApp && + this.registerAction({ + icon: { name: 'paperplane', color: '#722ed1' }, + type: 'input', + title: 'Token', + desc: '微信小程序交管12123获取', + val: 'Token', + onClick: async () => { + const token = this.settings.token; + this.settings.token = + (await this.getCache('wx_12123', false)) || token; + if (this.settings.token) this.saveSettings(false); + return this.setAlertInput('Token', '设置 token', { + token: '微信小程序交管12123获取', + }); + }, + }); + + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + } + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + date = new Date(); + arrUpdateTime = [ + this.format(this.date.getMonth() + 1), + this.format(this.date.getDate()), + this.format(this.date.getHours()), + this.format(this.date.getMinutes()), + ]; + + dataSource = { + left: { + title: '川 G88888', + icon: 'car.fill', + listItem: [ + { label: '未处违法', value: `0`, unit: '条' }, + { label: '车辆状态', value: '正常' }, + { label: '上次更新', value: '00:00' }, + ], + }, + right: { + title: '驾驶证', + icon: 'creditcard.fill', + listItem: [ + { label: '证件状态', value: '正常' }, + { label: '累计扣分', value: `0`, unit: '分' }, + { label: '重置日期', value: '—' }, + ], + }, + }; + + init = async () => { + this.settings.token = + (await this.getCache('wx_12123', false)) || this.settings.token; + if (this.settings.dataSource) { + this.dataSource = this.settings.dataSource; + } else { + await this.cacheData(); + } + this.cacheData(); + }; + + cacheData = async () => { + try { + const token = this.settings.token.replace('params=', ''); + const body = JSON.parse(decodeURIComponent(token)); + const params = { + sign: body.sign, + // businessId: body.businessId, + verifyToken: body.verifyToken, + // businessPrincipalId: body.businessPrincipalId, + }; + + const response = await this.$request.post(API_PARAMS.infoURL, { + body: `params=${JSON.stringify({ + api: API_PARAMS.api1, + productId: API_PARAMS.productId, + ...params, + })}`, + }); + console.log(response); + if (response.success) { + const illegal = response.data.list[0] || {}; + this.dataSource.left.listItem[0].value = illegal.count || 0; + + const details = await this.$request.post(API_PARAMS.infoURL, { + body: `params=${encodeURIComponent( + JSON.stringify({ + api: 'biz.user.integration.query', + productId: API_PARAMS.productId, + ...params, + }) + )}`, + }); + + console.log(details); + + if (details.success) { + const { drivingLicense, vehicles } = details.data; + const reaccDate = drivingLicense.reaccDate.split('-'); + this.dataSource.right.title = `驾驶证 ${drivingLicense.allowToDrive}`; + this.dataSource.right.listItem[1].value = + drivingLicense.cumulativePoint; + this.dataSource.right.listItem[2].value = `${reaccDate[1]}-${reaccDate[2]}`; + + if (vehicles.length) { + this.dataSource.left.title = vehicles[0].plateNumber; + } + } + + this.dataSource.left.listItem[2].value = `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`; + + this.settings.dataSource = this.dataSource; + this.saveSettings(false); + } else { + this.notify( + `verifyToken已过期 ⚠️`, + '点击通知框自动跳转到支付宝小程序交管12123页面获取最新的Token ( 请确保已打开辅助工具 )', + API_PARAMS.alipay + ); + } + } catch (e) { + console.log(e); + } + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + renderSmall = async (w) => { + this.notSupport(w); + return w; + }; + + renderLarge = async (w) => { + return this.notSupport(w); + }; + + renderCard = (w, data) => { + w.borderColor = this.widgetColor; + w.borderWidth = 2; + w.cornerRadius = 8; + + w.layoutVertically(); + w.setPadding(10, 10, 10, 10); + const topStack = w.addStack(); + topStack.layoutHorizontally(); + topStack.centerAlignContent(); + const iconImage = SFSymbol.named(data.icon).image; + const iconImageStack = topStack.addImage(iconImage); + iconImageStack.tintColor = this.widgetColor; + iconImageStack.imageSize = new Size(30, 30); + + topStack.addSpacer(10); + + const licensePlateText = topStack.addText(data.title); + licensePlateText.textColor = this.widgetColor; + licensePlateText.font = this.provideFont('bold', 14); + + w.addSpacer(); + + data.listItem.forEach((item, index) => { + const listItemStack = w.addStack(); + listItemStack.centerAlignContent(); + const labelText = listItemStack.addText(item.label); + labelText.textColor = this.widgetColor; + labelText.font = this.provideFont('medium', 14); + + listItemStack.addSpacer(); + if (index !== data.listItem.length - 1) w.addSpacer(); + + const valueText = listItemStack.addText(`${item.value}`); + valueText.textColor = this.widgetColor; + valueText.font = this.provideFont('medium', 14); + + if (item.unit) { + const unitText = listItemStack.addText(item.unit); + unitText.textColor = this.widgetColor; + unitText.font = this.provideFont('medium', 14); + } + }); + }; + + renderMedium = async (w) => { + const containerStack = w.addStack(); + containerStack.layoutHorizontally(); + containerStack.centerAlignContent(); + + const leftStack = containerStack.addStack(); + this.renderCard(leftStack, this.dataSource.left); + + containerStack.addSpacer(10); + + const rightStack = containerStack.addStack(); + this.renderCard(rightStack, this.dataSource.right); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/BiliBili.js b/Scripts/BiliBili.js index 49276f2..90216bc 100644 --- a/Scripts/BiliBili.js +++ b/Scripts/BiliBili.js @@ -1,168 +1,273 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: tv; + // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "bilibili今日番剧"; - this.en = "BiliBiliMonitor"; - this.logo = - "https://raw.githubusercontent.com/Orz-3/task/master/bilibili.png"; - config.runsInApp && - this.registerAction("设置背景图", this.setWidgetBackground); - this.cacheName = this.md5(`dataSouce_${this.en}`); - } - - today = ""; - dataSource = []; - - init = async () => { - try { - const today = new Date(); - const month = today.getMonth() + 1; - const day = today.getDate(); - this.today = `${month}-${day}`; - if (Keychain.contains(this.cacheName)) { - const dataSource = JSON.parse(Keychain.get(this.cacheName)); - if (dataSource[this.today]) { - this.dataSource = dataSource[this.today].seasons; - } else { - this.dataSource = await this.getDramaList(); - } - } else { - this.dataSource = await this.getDramaList(); - } - } catch (e) { - console.log(e); - } - }; - - getDramaList = async () => { - const url = `https://bangumi.bilibili.com/web_api/timeline_global`; - const response = await this.$request.get(url); - try { - if (response.code === 0 && response.result.length > 0) { - const dataSource = response.result; - const result = dataSource.find((item) => item.date === this.today); - if (result) { - Keychain.set( - this.cacheName, - JSON.stringify({ [this.today]: result }), - ); - return result.seasons; - } - } - return false; - } catch (e) { - return false; - } - }; - - setListCell = async (cell, data) => { - let { cover, url, title, pub_time, pub_index, delay, delay_reason, delay_index } = data; - let body = cell.addStack(); - body.url = url; - if (this.widgetFamily !== "small") { - const imageView = body.addStack(); - imageView.size = new Size(43, 43); - imageView.cornerRadius = 5; - const image = await this.$request.get(cover, "IMG"); - imageView.backgroundImage = image; - body.addSpacer(10); - } - - const textView = body.addStack(); - textView.layoutVertically(); - - const descText = textView.addText(title); - descText.font = Font.boldSystemFont(14); - descText.textColor = this.widgetColor; - descText.lineLimit = 1; - - textView.addSpacer(3); - if (delay) pub_index = `${delay_index}「${delay_reason}」`; - const subContent = textView.addText(pub_index); - subContent.font = Font.boldSystemFont(10); - subContent.textColor = this.widgetColor; - subContent.lineLimit = 1; - const timerText = textView.addText(`更新时间:${pub_time}`); - timerText.font = Font.lightSystemFont(10); - timerText.textColor = this.widgetColor; - timerText.lineLimit = 1; - - return cell; - }; - - setWidget = async (body, size) => { - const container = body.addStack(); - container.layoutVertically(); - const dataSource = this.getRandomArrayElements(this.dataSource, size); - for (let index = 0; index < dataSource.length; index++) { - const data = dataSource[index]; - let listItem = container.addStack(); - await this.setListCell(listItem, data); - container.addSpacer(10); - } - body.addSpacer(); - return body; - }; - - renderSmall = async (w) => { - return await this.setWidget(w, 2); - }; - - renderLarge = async (w) => { - return await this.setWidget(w, 5); - }; - - renderMedium = async (w) => { - return await this.setWidget(w, 2); - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderJDHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(10); - if (this.widgetFamily === "medium") { - return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - return await this.renderLarge(widget); - } else { - return await this.renderSmall(widget); - } - } - - renderJDHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - headerMore.url = ""; - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(this.today); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; + constructor(arg) { + super(arg); + this.name = '哔哩哔哩今日番剧'; + this.en = 'BiliBiliMonitor'; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png'; + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + this.cacheName = this.md5(`dataSouce_${this.en}`); + } + + useBoxJS = false; + today = ''; + dataSource = []; + + init = async () => { + try { + const today = new Date(); + const month = today.getMonth() + 1; + const day = today.getDate(); + this.today = `${month}-${day}`; + if (Keychain.contains(this.cacheName)) { + const dataSource = JSON.parse(Keychain.get(this.cacheName)); + if (dataSource[this.today]) { + this.dataSource = dataSource[this.today].seasons; + } else { + this.dataSource = await this.getDramaList(); + } + } else { + this.dataSource = await this.getDramaList(); + } + } catch (e) { + console.log(e); + } + }; + + getDramaList = async () => { + const url = `https://bangumi.bilibili.com/web_api/timeline_global`; + const response = await this.$request.get(url); + try { + if (response.code === 0 && response.result.length > 0) { + const dataSource = response.result; + const result = dataSource.find((item) => item.date === this.today); + if (result) { + Keychain.set( + this.cacheName, + JSON.stringify({ [this.today]: result }), + ); + return result.seasons; + } + } + return false; + } catch (e) { + return false; + } + }; + + setListCell = async (body, data) => { + let { + cover, + url, + title, + pub_time, + pub_index, + delay, + delay_reason, + delay_index, + } = data; + body.url = url; + const imageView = body.addStack(); + imageView.size = new Size(89, 105); + imageView.cornerRadius = 5; + const image = await this.$request.get(cover, 'IMG'); + imageView.backgroundImage = image; + imageView.borderWidth = 1; + imageView.borderColor = new Color(this.widgetColor.hex, 0.7); + const stackDesc = imageView.addStack(); + + stackDesc.layoutVertically(); + const stackDescTop = stackDesc.addStack(); + stackDescTop.setPadding(5, 0, 0, 0); + const textColor = new Color('#fff'); + if (delay) pub_index = `${delay_index}「${delay_reason}」`; + stackDescTop.addSpacer(); + const stackTopText = stackDescTop.addStack(); + stackTopText.setPadding(0, 2, 0, 2); + stackTopText.backgroundColor = new Color('#000', 0.3); + stackTopText.cornerRadius = 4; + const subContent = stackTopText.addText(pub_index); + subContent.font = Font.boldSystemFont(10); + subContent.textColor = textColor; + subContent.lineLimit = 1; + + stackDesc.addSpacer(); + const stackDescBottom = stackDesc.addStack(); + stackDescBottom.addSpacer(); + stackDescBottom.backgroundColor = new Color('#000', 0.3); + + const textView = stackDescBottom.addStack(); + textView.setPadding(0, 10, 0, 10); + textView.size = new Size(89, 30); + textView.layoutVertically(); + const descText = textView.addText(title); + descText.font = Font.boldSystemFont(10); + descText.textColor = textColor; + descText.lineLimit = 1; + + const timerText = textView.addText(`更新:${pub_time}`); + timerText.font = Font.lightSystemFont(10); + timerText.textColor = textColor; + timerText.lineLimit = 1; + stackDescBottom.addSpacer(); + + return body; + }; + + fillRect(drawing, rect, color) { + const path = new Path(); + path.addRoundedRect(rect, 0, 0); + drawing.addPath(path); + drawing.setFillColor(new Color(color, 1)); + drawing.fillPath(); + } + + drawLine(drawing, rect, color, scale) { + const x1 = Math.round(rect.x + scale * 1.5); + const y1 = rect.y - scale; + const x2 = Math.round(rect.width + scale * 1.5); + const point1 = new Point(x1, y1); + const point2 = new Point(x2, y1); + const path = new Path(); + path.move(point1); + path.addLine(point2); + drawing.addPath(path); + drawing.setStrokeColor(new Color(color, 1)); + drawing.setLineWidth(60 / (40 + 15 * scale)); + drawing.strokePath(); + } + + setLine = (stack, color) => { + try { + const topDrawing = new DrawContext(); + topDrawing.size = new Size(642, 100); + topDrawing.opaque = false; + topDrawing.respectScreenScale = true; + + const rectLine = new Rect(0, 70, 610, 26); + this.fillRect(topDrawing, rectLine, color); + for (let i = 0; i < 40; i++) { + this.drawLine(topDrawing, rectLine, color, i); + } + + const stackLine = stack.addStack(); + stack.backgroundImage = topDrawing.getImage(); + stackLine.addSpacer(); + const line = stackLine.addStack(); + line.size = new Size(1, 10); + stackLine.addSpacer(); + } catch (e) { + console.log(e); + } + }; + + setWidget = async (body, data) => { + const d = body.addStack(); + d.addSpacer(); + const container = d.addStack(); + container.layoutVertically(); + const dataSource = data.length > 3 ? [data.splice(0, 3), data] : [data]; + let itemIndex = 0; + for (const item of dataSource) { + let listItem = container.addStack(); + let index = 0; + for (const video of item) { + const stackItem = listItem.addStack(); + await this.setListCell(stackItem, video); + index++; + if (item.length !== index) listItem.addSpacer(13); + } + itemIndex++; + if (dataSource.length !== itemIndex) container.addSpacer(13); + } + if (this.widgetFamily === 'large') { + container.addSpacer(); + this.setLine(container, '#e8e8e8'); + const timerColor = new Color(this.widgetColor.hex, 0.7); + const fontSize = 10; + container.addSpacer(); + const stackFooter = container.addStack(); + stackFooter.addSpacer(); + const now = new Date(); + const stackDate = stackFooter.addDate( + new Date(`${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`), + ); + stackDate.textColor = timerColor; + stackDate.fontSize = fontSize; + stackDate.rightAlignText(); + stackDate.applyTimerStyle(); + } + d.addSpacer(); + return body; + }; + + renderSmall = async (w) => { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + }; + + renderLarge = async (w) => { + const dataSource = this.getRandomArrayElements(this.dataSource, 6); + return await this.setWidget(w, dataSource); + }; + + renderMedium = async (w) => { + const dataSource = this.getRandomArrayElements(this.dataSource, 3); + return await this.setWidget(w, dataSource); + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + const header = widget.addStack(); + if (this.widgetFamily !== 'small') { + await this.renderJDHeader(header); + } else { + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + } + widget.addSpacer(10); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } + + renderJDHeader = async (header) => { + header.centerAlignContent(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + header.addSpacer(); + const headerMore = header.addStack(); + headerMore.url = ''; + headerMore.setPadding(1, 10, 1, 10); + headerMore.cornerRadius = 10; + headerMore.backgroundColor = new Color('#fff', 0.5); + const textItem = headerMore.addText(this.today); + textItem.font = Font.boldSystemFont(12); + textItem.textColor = this.widgetColor; + textItem.lineLimit = 1; + textItem.rightAlignText(); + return header; + }; } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/BiliBiliUp.js b/Scripts/BiliBiliUp.js new file mode 100644 index 0000000..56dd5f7 --- /dev/null +++ b/Scripts/BiliBiliUp.js @@ -0,0 +1,215 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: gray; icon-glyph: chalkboard; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = 'B站 UP 主'; + this.en = 'BiliBiliUp'; + this.inputValue = arg || '50952087'; + this.Run(); + } + + useBoxJS = false; + ytInitialData = {}; + videos = []; + baseUrl = 'https://api.bilibili.com/x'; + url; + + numberFormat(value) { + try { + const param = {}; + let k = 10000; + const size = ['', '万', '亿', '万亿']; + let i; + if (value < k) { + param.value = value; + param.unit = ''; + } else { + i = Math.floor(Math.log(value) / Math.log(k)); + param.value = ((value / Math.pow(k, i))).toFixed(2); + param.unit = size[i]; + } + return `${param.value}${param.unit}`; + } catch (e) { + console.log(e); + } + } + + init = async () => { + try { + await this.getData(); + await this.getRelationStat(); + await this.getVideoList(); + } catch (e) { + console.log('❌错误信息:' + e); + } + }; + + getData = async () => { + const url = `${this.baseUrl}/space/acc/info?mid=${this.inputValue}&jsonp=jsonp`; + const response = await this.$request.get(url); + if (response.code === 0) { + this.ytInitialData = response.data; + } + }; + + getRelationStat = async () => { + const url = `${this.baseUrl}/relation/stat?vmid=${this.inputValue}&jsonp=jsonp`; + const response = await this.$request.get(url); + if (response.code === 0) { + this.ytInitialData = {...this.ytInitialData, relation: response.data}; + } + }; + + getVideoList = async () => { + const url = `${this.baseUrl}/space/arc/search?mid=${this.inputValue}&pn=1&ps=25&index=1&jsonp=jsonp`; + const response = await this.$request.get(url); + if (response.code === 0) { + this.ytInitialData = { + ...this.ytInitialData, + videos: response.data.list.vlist.map(item => ({ + thumb: item.pic, + title: item.title, + view: `${this.numberFormat(item.play)}次`, + url: item.bvid, + })), + }; + } + }; + + setAvatar = async (stack) => { + stack.size = new Size(50, 50); + stack.cornerRadius = 5; + const {face} = this.ytInitialData; + const imgLogo = await this.$request.get(face, 'IMG'); + const imgLogoItem = stack.addImage(imgLogo); + imgLogoItem.imageSize = new Size(50, 50); + return stack; + }; + + setTitleStack = (stack) => { + const textFormatNumber = this.textFormat.title; + textFormatNumber.color = this.backGroundColor; + const {name} = this.ytInitialData; + const title = name; + textFormatNumber.size = + title.length > 20 || this.widgetFamily === 'small' ? 16 : 20; + const titleItem = this.provideText(title, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setPathStack = (stack) => { + const textFormatNumber = this.textFormat.defaultText; + textFormatNumber.color = this.backGroundColor; + textFormatNumber.size = 12; + const {relation} = this.ytInitialData; + let simpleText = `关注数:${this.numberFormat(relation.follower)}`; + const titleItem = this.provideText( + simpleText, + stack, + textFormatNumber, + ); + titleItem.lineLimit = 1; + titleItem.textOpacity = 0.9; + }; + + setFooterCell = async (stack) => { + const datas = this.getRandomArrayElements(this.ytInitialData.videos, 3); + for (let i = 0; i < datas.length; i++) { + if (i === 1) stack.addSpacer(); + const video = datas[i]; + const stackVideo = stack.addStack(); + stackVideo.setPadding(10, 10, 10, 10); + stackVideo.url = 'https://www.bilibili.com/video/' + video.url; + stackVideo.backgroundColor = this.widgetColor; + stackVideo.centerAlignContent(); + stackVideo.layoutVertically(); + const img = await this.$request.get(video.thumb, 'IMG'); + stackVideo.backgroundImage = await this.shadowImage(img, '#000', 0.3); + const title = {...this.textFormat.defaultText}; + title.size = 8; + title.color = new Color('#fff'); + this.provideText(video.title, stackVideo, title); + stackVideo.addSpacer(); + title.color = new Color('#fff', 0.7); + this.provideText(video.view, stackVideo, title); + stackVideo.size = new Size(87, 56); + stackVideo.cornerRadius = 4; + if (i === 1) stack.addSpacer(); + } + }; + + renderSmall = async (w) => { + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.url = `https://space.bilibili.com/${this.inputValue}/`; + stackBody.layoutVertically(); + const stackHeader = stackBody.addStack(); + stackHeader.setPadding(5, 10, 5, 10); + stackHeader.cornerRadius = 10; + stackHeader.backgroundColor = this.widgetColor; + + stackHeader.centerAlignContent(); + const stackLeft = stackHeader.addStack(); + await this.setAvatar(stackLeft); + stackHeader.addSpacer(20); + + const stackRight = stackHeader.addStack(); + stackRight.layoutVertically(); + this.setTitleStack(stackRight); + stackRight.addSpacer(5); + this.setPathStack(stackRight); + stackHeader.addSpacer(); + stackBody.addSpacer(); + + const stackFooter = stackBody.addStack(); + stackFooter.setPadding(10, 0, 10, 0); + stackFooter.cornerRadius = 10; + stackFooter.backgroundColor = this.widgetColor; + stackFooter.addSpacer(); + await this.setFooterCell(stackFooter); + stackFooter.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return w; + }; + + Run() { + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", true); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/BiliBiliWatch.js b/Scripts/BiliBiliWatch.js index 5df35a8..6e8d751 100644 --- a/Scripts/BiliBiliWatch.js +++ b/Scripts/BiliBiliWatch.js @@ -3,261 +3,256 @@ // icon-color: gray; icon-glyph: chalkboard; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "哔哩哔哩关注"; - this.en = "BiliBiliWatch"; - this.logo = - "https://raw.githubusercontent.com/Orz-3/task/master/bilibili.png"; - this.Run(module.filename); - this.cacheName = this.md5(`dataSouce_${this.en}`); - } + constructor(arg) { + super(arg); + this.name = '哔哩哔哩关注'; + this.en = 'BiliBiliWatch'; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png'; + this.Run(module.filename); + } - cookie = ""; - dataSource = []; + cookie = ''; + dataSource = []; - init = async () => { - try { - await this.getDramaList(); - } catch (e) { - console.log(e); - } - }; + init = async () => { + try { + await this.getDramaList(); + } catch (e) { + console.log(e); + } + }; - getDramaList = async () => { - const url = `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?type_list=268435455`; - const method = `GET`; - const headers = { - Cookie: this.cookie, - "User-Agent": `bili-universal/10320 CFNetwork/1206 Darwin/20.1.0 os/ios model/iPhone XR mobi_app/iphone build/10320 osVer/14.2 network/2 channel/AppStore`, - }; - const response = await this.$request.get(url, { - method, - headers, - }); - try { - const { code, data } = response; - if (code === 0 && data.cards.length > 0) { - let dataSource = data.cards; - dataSource.forEach((item) => { - const card = JSON.parse(item.card); - let temp = false; - if (card.apiSeasonInfo) { - temp = {}; - temp.title = card.apiSeasonInfo.title; - temp.url = card.url; - temp.reply = card.reply_count; - temp.play = card.play_count; - temp.img = card.cover; - temp.desc = card.new_desc; - temp.timestamp = item.desc.timestamp; - } else if (card.videos === 1) { - temp = {}; - temp.title = card.title; - temp.url = card.jump_url; - temp.reply = card.stat.reply; - temp.play = card.stat.view; - temp.desc = card.desc; - temp.img = card.pic; - temp.timestamp = item.desc.timestamp; - } - if (temp) this.dataSource.push(temp); - }); - return this.dataSource; - } - return false; - } catch (e) { - console.log(e); - return false; - } - }; + getDramaList = async () => { + const url = `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new?type_list=268435455`; + const method = `GET`; + const headers = { + Cookie: this.cookie, + 'User-Agent': `bili-universal/10320 CFNetwork/1206 Darwin/20.1.0 os/ios model/iPhone XR mobi_app/iphone build/10320 osVer/14.2 network/2 channel/AppStore`, + }; + const response = await this.$request.get(url, { + method, + headers, + }); + try { + const { code, data } = response; + if (code === 0 && data.cards.length > 0) { + let dataSource = data.cards; + dataSource.forEach((item) => { + const card = JSON.parse(item.card); + let temp = false; + if (card.apiSeasonInfo) { + temp = {}; + temp.title = card.apiSeasonInfo.title; + temp.url = card.url; + temp.reply = card.reply_count; + temp.play = card.play_count; + temp.img = card.cover; + temp.desc = card.new_desc; + temp.timestamp = item.desc.timestamp; + } else if (card.videos === 1) { + temp = {}; + temp.title = card.title; + temp.url = card.jump_url; + temp.reply = card.stat.reply; + temp.play = card.stat.view; + temp.desc = card.desc; + temp.img = card.pic; + temp.timestamp = item.desc.timestamp; + } + if (temp) this.dataSource.push(temp); + }); + return this.dataSource; + } else { + throw 'cookie 失效,请重新获取'; + } + } catch (e) { + console.log('❌错误信息:' + e); + return false; + } + }; - setListCell = async (cell, data) => { - const { title, url, reply, play, desc, img, timestamp } = data; - let body = cell.addStack(); - body.url = url; - if (this.widgetFamily !== "small") { - const imageView = body.addStack(); - imageView.size = new Size(43, 43); - imageView.cornerRadius = 5; - const image = await this.$request.get(img, "IMG"); - imageView.backgroundImage = image; - body.addSpacer(10); - } + setListCell = async (cell, data) => { + const { title, url, reply, play, desc, img, timestamp } = data; + let body = cell.addStack(); + body.url = url; + if (this.widgetFamily !== 'small') { + const imageView = body.addStack(); + imageView.size = new Size(43, 43); + imageView.cornerRadius = 5; + const image = await this.$request.get(img, 'IMG'); + imageView.backgroundImage = image; + body.addSpacer(10); + } - const textView = body.addStack(); - textView.layoutVertically(); + const textView = body.addStack(); + textView.layoutVertically(); - const descText = textView.addText(`${title} ${desc}`); - descText.font = Font.boldSystemFont(14); - descText.textColor = this.widgetColor; - descText.lineLimit = 1; + const descText = textView.addText(`${title} ${desc}`); + descText.font = Font.boldSystemFont(14); + descText.textColor = this.widgetColor; + descText.lineLimit = 1; - textView.addSpacer(3); + textView.addSpacer(3); - const date = new Date(); - date.setTime(timestamp * 1000); //注意,这行是关键代码 - let month = date.getMonth() + 1; - let day = date.getDate(); + const date = new Date(); + date.setTime(timestamp * 1000); //注意,这行是关键代码 + let month = date.getMonth() + 1; + let day = date.getDate(); - const timerText1 = textView.addText(`${month}-${day} 更新了`); - timerText1.font = Font.lightSystemFont(10); - timerText1.textColor = this.widgetColor; - timerText1.lineLimit = 1; + const timerText1 = textView.addText(`${month}-${day} 更新了`); + timerText1.font = Font.lightSystemFont(10); + timerText1.textColor = this.widgetColor; + timerText1.lineLimit = 1; - const descView = textView.addStack(); + const descView = textView.addStack(); - const icon1 = descView.addText("浏览:"); - icon1.font = Font.lightSystemFont(10); - icon1.textColor = this.widgetColor; - descView.addSpacer(3); - const timerText = descView.addText(`${play}`); - timerText.font = Font.lightSystemFont(10); - timerText.textColor = this.widgetColor; - descView.addSpacer(5); + const icon1 = descView.addText('浏览:'); + icon1.font = Font.lightSystemFont(10); + icon1.textColor = this.widgetColor; + descView.addSpacer(3); + const timerText = descView.addText(`${play}`); + timerText.font = Font.lightSystemFont(10); + timerText.textColor = this.widgetColor; + descView.addSpacer(5); - const icon2 = descView.addText("评论:"); - icon2.font = Font.lightSystemFont(10); - icon2.textColor = this.widgetColor; + const icon2 = descView.addText('评论:'); + icon2.font = Font.lightSystemFont(10); + icon2.textColor = this.widgetColor; - descView.addSpacer(3); - const timerText2 = descView.addText(`${reply}`); - timerText2.font = Font.lightSystemFont(10); - timerText2.textColor = this.widgetColor; - return cell; - }; + descView.addSpacer(3); + const timerText2 = descView.addText(`${reply}`); + timerText2.font = Font.lightSystemFont(10); + timerText2.textColor = this.widgetColor; + return cell; + }; - setWidget = async (body, size) => { - const container = body.addStack(); - container.layoutVertically(); - const dataSource = this.getRandomArrayElements(this.dataSource, size); - for (let index = 0; index < dataSource.length; index++) { - const data = dataSource[index]; - let listItem = container.addStack(); - await this.setListCell(listItem, data); - container.addSpacer(10); - } - body.addSpacer(); - return body; - }; + setWidget = async (body, size) => { + const container = body.addStack(); + container.layoutVertically(); + const dataSource = this.getRandomArrayElements(this.dataSource, size); + for (let index = 0; index < dataSource.length; index++) { + const data = dataSource[index]; + let listItem = container.addStack(); + await this.setListCell(listItem, data); + container.addSpacer(10); + } + body.addSpacer(); + return body; + }; - renderSmall = async (w) => { - return await this.setWidget(w, 2); - }; + renderSmall = async (w) => { + return await this.setWidget(w, 2); + }; - renderLarge = async (w) => { - return await this.setWidget(w, 5); - }; + renderLarge = async (w) => { + return await this.setWidget(w, 6); + }; - renderMedium = async (w) => { - return await this.setWidget(w, 2); - }; + renderMedium = async (w) => { + return await this.setWidget(w, 2); + }; - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderJDHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(10); - if (this.widgetFamily === "medium") { - return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - return await this.renderLarge(widget); - } else { - return await this.renderSmall(widget); - } - } + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + const header = widget.addStack(); + if (this.widgetFamily !== 'small') { + await this.renderJDHeader(header); + } else { + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + } + widget.addSpacer(10); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } - renderJDHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - headerMore.url = ""; - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText("个人中心"); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; + renderJDHeader = async (header) => { + header.centerAlignContent(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + header.addSpacer(); + const headerMore = header.addStack(); + headerMore.url = ''; + headerMore.setPadding(1, 10, 1, 10); + headerMore.cornerRadius = 10; + headerMore.backgroundColor = new Color('#fff', 0.5); + const textItem = headerMore.addText('个人中心'); + textItem.font = Font.boldSystemFont(12); + textItem.textColor = this.widgetColor; + textItem.lineLimit = 1; + textItem.rightAlignText(); + return header; + }; - Run = (filename) => { - if (config.runsInApp) { - this.registerAction("设置背景图", this.setWidgetBackground); - this.registerAction("设置 Cookie", this.inputCk); - this.registerAction("设置 BoxJS Cookie", this._loadCk); - } - let _md5 = this.md5(filename + this.en); - this.CACHE_KEY = `cache_${_md5}`; - try { - this.cookie = this.settings[this.en]; - if (!this.cookie) { - throw "CK 获取失败"; - } - return true; - } catch (e) { - this.notify("错误提示", e); - return false; - } - }; + Run = (filename) => { + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', this.inputCk); + this.registerAction('代理缓存', this._loadCk); + } + let _md5 = this.md5(filename + this.en); + this.CACHE_KEY = `cache_${_md5}`; + try { + this.cookie = this.settings[this.en]; + if (!this.cookie) { + throw 'CK 获取失败'; + } + return true; + } catch (e) { + this.notify('错误提示', e); + return false; + } + }; - _loadCk = async () => { - try { - const cookie = await this.getCache("@bilibili.cookie"); - if (cookie) { - this.cookie = cookie; - this.settings[this.en] = this.cookie; - this.saveSettings(); - } else { - throw "ck 获取失败"; - } - return true; - } catch (e) { - console.log(e); - this.cookie = ""; - this.notify( - this.name, - "BoxJS Cookie 设置失败,点击根据配置获取", - "https://raw.githubusercontent.com/dompling/Script/master/BiliBili/bilibili.cookie.js", - ); - return false; - } - }; + _loadCk = async () => { + try { + const cookie = await this.getCache('@bilibili.cookie'); + if (cookie) { + this.cookie = cookie; + this.settings[this.en] = this.cookie; + this.saveSettings(); + } else { + throw 'ck 获取失败'; + } + return true; + } catch (e) { + console.log(e); + this.cookie = ''; + return false; + } + }; - async inputCk() { - const a = new Alert(); - a.title = "哔哩哔哩 CK"; - a.message = "手动输入 Ck"; - a.addTextField("Cookie", this.cookie); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.cookie = a.textFieldValue(0); - // 保存到本地 - this.settings[this.en] = this.cookie; - this.saveSettings(); - } + async inputCk() { + const a = new Alert(); + a.title = '账号设置'; + a.message = '手动输入 Ck'; + a.addTextField('Cookie', this.cookie); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return; + this.cookie = a.textFieldValue(0); + // 保存到本地 + this.settings[this.en] = this.cookie; + this.saveSettings(); + } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/Birthday.js b/Scripts/Birthday.js deleted file mode 100644 index 8ae4a54..0000000 --- a/Scripts/Birthday.js +++ /dev/null @@ -1,399 +0,0 @@ -// Variables used by Scriptable. -// These must be at the very top of the file. Do not edit. -// icon-color: pink; icon-glyph: birthday-cake; - -// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY } = require("./DmYY"); -const { Calendar } = require("./Calendar"); - -const $ = new Calendar(); - -class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "破壳日"; - this.en = "birthday"; - this.logo = - "https://raw.githubusercontent.com/Orz-3/task/master/birthday.png"; - this.LEFT_IMG_KEY = this.FILE_MGR_LOCAL.joinPath( - this.FILE_MGR_LOCAL.documentsDirectory(), - `left_image_${this.SETTING_KEY}.jpg` - ); - this.defaultData = { ...this.defaultData, ...this.settings[this.en] }; - console.log(this.defaultData); - this.registerAction("组件内部配置", this.setWidgetConfig); - this.registerAction("读取BoxJS数据", this.setWidgetBoxJSConfig); - this.registerAction("设置左侧图", this.setLeftWidgetImage); - this.registerAction("设置背景图", this.setWidgetBackground); - } - - defaultData = { - username: "", // 姓名 - time: "", // 生日日期 - nongli: "", // 农历生日 - eday: "", //相识 - isLeapMonth: false, //如果是农历闰月第四个参数赋值true即可 - }; - - contentText = {}; - - init = async () => { - try { - this.getCalendarData(); - } catch (e) { - console.log(e); - } - }; - - getEdayNumber = (date) => { - var initDay = date.split("-"); - var obj = { - cYear: parseInt(initDay[0]), - cMonth: parseInt(initDay[1]), - cDay: parseInt(initDay[2]), - }; - return Math.abs($.daysBetween(obj)); - }; - - getCalendarData = () => { - const { time, nongli, isLeapMonth, eday } = this.defaultData; - const _data = time.split("-"); - const opt = { - year: parseInt(_data[0]), - month: parseInt(_data[1]), - day: parseInt(_data[2]), - nongli, - isLeapMonth, - }; - - const response = {}; - response.birthdayText = $.birthday(opt); - response.nextBirthday = response.birthdayText[0]; - - const solarData = - nongli === "true" - ? $.lunar2solar(opt.year, opt.month, opt.day, isLeapMonth) - : $.solar2lunar(opt.year, opt.month, opt.day); - response.gregorian = solarData; - response.animal = `${$.getAnimalZodiacToEmoji(solarData.Animal)}-${ - solarData.Animal - }`; - response.astro = `${$.getAstroToEmoji(solarData.astro)}-${solarData.astro}`; - if ($.verifyTime(eday)) { - response.meetDay = this.getEdayNumber(eday); - } - this.contentText = response; - }; - - setRightCell = (text, rowCell) => { - const subContent = rowCell.addText(text); - subContent.font = Font.boldSystemFont(14); - subContent.textColor = this.widgetColor; - subContent.lineLimit = 1; - rowCell.addSpacer(5); - }; - - setLeftView = (w) => { - const leftImg = this.getLeftImage(); - const left = w.addStack(); - left.size = new Size(110, 110); - left.cornerRadius = 5; - left.borderWidth = 2; - left.borderColor = this.widgetColor; - if (leftImg) { - const widgetImg = left.addImage(leftImg); - widgetImg.imageSize = new Size(110, 110); - widgetImg.applyFillingContentMode(); - widgetImg.cornerRadius = 5; - } - return w; - }; - - setRightView = (right) => { - const { - animal, - astro, - gregorian, - nextBirthday, - meetDay, - birthdayText, - } = this.contentText; - const { IMonthCn, IDayCn } = gregorian; - right.layoutVertically(); - this.setRightCell(`🐽相:${animal}`, right); // 属相 - this.setRightCell(`🌠座:${astro}`, right); // 属相 - if (meetDay) { - this.setRightCell(`💖遇:${meetDay} 天`, right); - } - const _birth = `🎂生:${nextBirthday.cYear}-${nextBirthday.cMonth}-${nextBirthday.cDay} (${birthdayText[1]}天)`; - this.setRightCell(_birth, right); - this.setRightCell(`📆农:${IMonthCn}${IDayCn}`, right); - return right; - }; - - fetch = async () => { - const response = await this.$request.get( - "https://api.uomg.com/api/rand.qinghua?format=json" - ); - return response.content; - }; - - renderSmall = async (w) => { - this.setRightView(w.addStack()); - return w; - }; - - renderLarge = async (w) => { - w.addSpacer(20); - const body = w.addStack(); - const left = body.addStack(); - this.setLeftView(left); - body.addSpacer(20); - const right = body.addStack(); - this.setRightView(right); - - w.addSpacer(20); - const footer = w.addStack(); - const text = await this.fetch(); - const subContent = footer.addText(text); - subContent.font = Font.boldSystemFont(16); - subContent.textColor = this.widgetColor; - w.addSpacer(); - return w; - }; - - renderMedium = async (w) => { - const body = w.addStack(); - const left = body.addStack(); - this.setLeftView(left); - body.addSpacer(); - const right = body.addStack(); - this.setRightView(right); - body.addSpacer(); - w.addSpacer(); - return w; - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderMoreHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(10); - if (this.widgetFamily === "medium") { - await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - await this.renderLarge(widget); - } else { - await this.renderSmall(widget); - } - return widget; - } - - renderMoreHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(this.defaultData.username); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; - - /** - * 获取当前插件是否有自定义背景图片 - * @reutrn img | false - */ - getLeftImage() { - let result = null; - if (this.FILE_MGR_LOCAL.fileExists(this.LEFT_IMG_KEY)) { - result = Image.fromFile(this.LEFT_IMG_KEY); - } - return result; - } - - /** - * 设置当前组件的背景图片 - * @param {image} img - */ - setLeftImage(img, notify = true) { - if (!img) { - // 移除背景 - if (this.FILE_MGR_LOCAL.fileExists(this.LEFT_IMG_KEY)) { - this.FILE_MGR_LOCAL.remove(this.LEFT_IMG_KEY); - } - if (notify) this.notify("移除成功", "小组件图片已移除,稍后刷新生效"); - } else { - // 设置背景 - // 全部设置一遍, - this.FILE_MGR_LOCAL.writeImage(this.LEFT_IMG_KEY, img); - if (notify) this.notify("设置成功", "小组件图片已设置!稍后刷新生效"); - } - } - - setLeftWidgetImage = async () => { - const alert = new Alert(); - alert.title = "设置左侧图"; - alert.message = "显示左侧图片"; - alert.addAction("设置新图"); - alert.addAction("清空图片"); - alert.addCancelAction("取消"); - const actions = [ - async () => { - const backImage = await this.chooseImg(); - await this.setLeftImage(backImage, true); - }, - () => { - this.setLeftImage(false, true); - }, - ]; - const id = await alert.presentAlert(); - if (id === -1) return; - actions[id] && actions[id].call(this); - }; - - setWidgetConfig = async () => { - const a = new Alert(); - a.title = "🐣破壳日配置"; - a.message = "配置破壳日的基础信息"; - a.addTextField("昵称", this.defaultData.username); - a.addTextField("生日/ 年-月-日", this.defaultData.time); - a.addTextField("农历/ true | false", `${this.defaultData.nongli || ""}`); - a.addTextField("相识/ 年-月-日", this.defaultData.eday); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.defaultData.username = a.textFieldValue(0); - this.defaultData.time = a.textFieldValue(1); - this.defaultData.nongli = a.textFieldValue(2) === "true"; - this.defaultData.eday = a.textFieldValue(3); - // 保存到本地 - this.settings[this.en] = this.defaultData; - this.saveSettings(); - }; - - setWidgetBoxJSConfig = async () => { - try { - const datas = await this.getCache(); - Object.keys(this.defaultData).forEach((key) => { - this.defaultData[key] = datas[`@${this.en}.${key}`]; - }); - this.settings[this.en] = this.defaultData; - this.saveSettings(); - } catch (e) { - this.notify( - this.name, - "BoxJS教程", - "https://chavyleung.gitbook.io/boxjs/awesome/videos" - ); - } - }; -} - -let M = null; -// 判断hash是否和当前设备匹配 -if (config.runsInWidget) { - M = new Widget(args.widgetParameter || ""); - const W = await M.render(); - if (W) { - Script.setWidget(W); - Script.complete(); - } -} else { - let { act, __arg, __size } = args.queryParameters; - M = new Widget(__arg || ""); - if (__size) M.init(__size); - if (!act || !M["_actions"]) { - // 弹出选择菜单 - const actions = M["_actions"]; - const _actions = [ - // 预览组件 - async (debug = false) => { - let a = new Alert(); - a.title = "预览组件"; - a.message = "测试桌面组件在各种尺寸下的显示效果"; - a.addAction("小尺寸 Small"); - a.addAction("中尺寸 Medium"); - a.addAction("大尺寸 Large"); - a.addAction("全部 All"); - a.addCancelAction("取消操作"); - const funcs = []; - if (debug) { - for (let _ in actions) { - a.addAction(_); - funcs.push(actions[_].bind(M)); - } - a.addDestructiveAction("停止调试"); - } - let i = await a.presentSheet(); - if (i === -1) return; - let w; - switch (i) { - case 0: - M.widgetFamily = "small"; - w = await M.render(); - w && (await w.presentSmall()); - break; - case 1: - M.widgetFamily = "medium"; - w = await M.render(); - w && (await w.presentMedium()); - break; - case 2: - M.widgetFamily = "large"; - w = await M.render(); - w && (await w.presentLarge()); - break; - case 3: - M.widgetFamily = "small"; - w = await M.render(); - w && (await w.presentSmall()); - M.widgetFamily = "medium"; - w = await M.render(); - w && (await w.presentMedium()); - M.widgetFamily = "large"; - w = await M.render(); - w && (await w.presentLarge()); - break; - default: - const func = funcs[i - 4]; - if (func) await func(); - break; - } - - return i; - }, - ]; - const alert = new Alert(); - alert.title = M.name; - alert.message = M.desc; - alert.addAction("预览组件"); - for (let _ in actions) { - alert.addAction(_); - _actions.push(actions[_]); - } - alert.addCancelAction("取消操作"); - const idx = await alert.presentSheet(); - if (_actions[idx]) { - const func = _actions[idx]; - await func(); - } - } -} diff --git a/Scripts/CalendarFnc.js b/Scripts/CalendarFnc.js new file mode 100644 index 0000000..a8b5f21 --- /dev/null +++ b/Scripts/CalendarFnc.js @@ -0,0 +1 @@ +function calendarFun(){const calendar={lunarInfo:[0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50,0x06b20,0x1a6c4,0x0aae0,0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,0x0d520,],solarMonth:[31,28,31,30,31,30,31,31,30,31,30,31],Gan:["\u7532","\u4e59","\u4e19","\u4e01","\u620a","\u5df1","\u5e9a","\u8f9b","\u58ec","\u7678",],Zhi:["\u5b50","\u4e11","\u5bc5","\u536f","\u8fb0","\u5df3","\u5348","\u672a","\u7533","\u9149","\u620c","\u4ea5",],Animals:["\u9f20","\u725b","\u864e","\u5154","\u9f99","\u86c7","\u9a6c","\u7f8a","\u7334","\u9e21","\u72d7","\u732a",],solarTerm:["\u5c0f\u5bd2","\u5927\u5bd2","\u7acb\u6625","\u96e8\u6c34","\u60ca\u86f0","\u6625\u5206","\u6e05\u660e","\u8c37\u96e8","\u7acb\u590f","\u5c0f\u6ee1","\u8292\u79cd","\u590f\u81f3","\u5c0f\u6691","\u5927\u6691","\u7acb\u79cb","\u5904\u6691","\u767d\u9732","\u79cb\u5206","\u5bd2\u9732","\u971c\u964d","\u7acb\u51ac","\u5c0f\u96ea","\u5927\u96ea","\u51ac\u81f3",],sTermInfo:["9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","9778397bd19801ec9210c965cc920e","97b6b97bd19801ec95f8c965cc920f","97bd09801d98082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd197c36c9210c9274c91aa","97b6b97bd19801ec95f8c965cc920e","97bd09801d98082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec95f8c965cc920e","97bcf97c3598082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd07f595b0b6fc920fb0722","9778397bd097c36b0b6fc9210c8dc2","9778397bd19801ec9210c9274c920e","97b6b97bd19801ec95f8c965cc920f","97bd07f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c920e","97b6b97bd19801ec95f8c965cc920f","97bd07f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec9210c965cc920e","97bd07f1487f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c9274c920e","97bcf7f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c91aa","97b6b97bd197c36c9210c9274c920e","97bcf7f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c920e","97b6b7f0e47f531b0723b0b6fb0722","7f0e37f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36b0b70c9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e37f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc9210c8dc2","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0787b0721","7f0e27f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c91aa","97b6b7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c8dc2","977837f0e37f149b0723b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f5307f595b0b0bc920fb0722","7f0e397bd097c35b0b6fc9210c8dc2","977837f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0721","7f0e37f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc9210c8dc2","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0723b06bd","7f07e7f0e37f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f1487f595b0b0bb0b6fb0722","7f0e37f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e37f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0723b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0723b06bd","7f07e7f0e37f14998083b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14898082b0723b02d5","7f07e7f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e36665b66aa89801e9808297c35","665f67f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e36665b66a449801e9808297c35","665f67f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e36665b66a449801e9808297c35","665f67f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e26665b66a449801e9808297c35","665f67f0e37f1489801eb072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722",],nStr1:["\u65e5","\u4e00","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341",],nStr2:["\u521d","\u5341","\u5eff","\u5345"],nStr3:["\u6b63","\u4e8c","\u4e09","\u56db","\u4e94","\u516d","\u4e03","\u516b","\u4e5d","\u5341","\u51ac","\u814a",],lYearDays:function(y){var i,sum=348;for(i=0x8000;i>0x8;i>>=1){sum+=calendar.lunarInfo[y-1900]&i?1:0}return sum+calendar.leapDays(y)},leapMonth:function(y){return calendar.lunarInfo[y-1900]&0xf},leapDays:function(y){if(calendar.leapMonth(y)){return calendar.lunarInfo[y-1900]&0x10000?30:29}return 0},monthDays:function(y,m){if(m>12||m<1){return-1}return calendar.lunarInfo[y-1900]&(0x10000>>m)?30:29},solarDays:function(y,m){if(m>12||m<1){return-1}var ms=m-1;if(ms==1){return(y%4==0&&y%100!=0)||y%400==0?29:28}else{return calendar.solarMonth[ms]}},toGanZhiYear:function(lYear){var ganKey=(lYear-3)%10;var zhiKey=(lYear-3)%12;if(ganKey==0)ganKey=10;if(zhiKey==0)zhiKey=12;return calendar.Gan[ganKey-1]+calendar.Zhi[zhiKey-1]},toAstro:function(cMonth,cDay){var s="\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf";var arr=[20,19,21,21,21,22,23,23,23,23,22,22];return(s.substr(cMonth*2-(cDay2100){return-1}if(n<1||n>24){return-1}var _table=calendar.sTermInfo[y-1900];var _info=[parseInt("0x"+_table.substr(0,5)).toString(),parseInt("0x"+_table.substr(5,5)).toString(),parseInt("0x"+_table.substr(10,5)).toString(),parseInt("0x"+_table.substr(15,5)).toString(),parseInt("0x"+_table.substr(20,5)).toString(),parseInt("0x"+_table.substr(25,5)).toString(),];var _calday=[_info[0].substr(0,1),_info[0].substr(1,2),_info[0].substr(3,1),_info[0].substr(4,2),_info[1].substr(0,1),_info[1].substr(1,2),_info[1].substr(3,1),_info[1].substr(4,2),_info[2].substr(0,1),_info[2].substr(1,2),_info[2].substr(3,1),_info[2].substr(4,2),_info[3].substr(0,1),_info[3].substr(1,2),_info[3].substr(3,1),_info[3].substr(4,2),_info[4].substr(0,1),_info[4].substr(1,2),_info[4].substr(3,1),_info[4].substr(4,2),_info[5].substr(0,1),_info[5].substr(1,2),_info[5].substr(3,1),_info[5].substr(4,2),];return parseInt(_calday[n-1])},toChinaMonth:function(m){if(m>12||m<1){return-1}var s=calendar.nStr3[m-1];s+="\u6708";return s},toChinaDay:function(d){var s;switch(d){case 10:s="\u521d\u5341";break;case 20:s="\u4e8c\u5341";break;break;case 30:s="\u4e09\u5341";break;break;default:s=calendar.nStr2[Math.floor(d/10)];s+=calendar.nStr1[d%10]}return s},getAnimal:function(y){return calendar.Animals[(y-4)%12]},solar2lunar:function(y,m,d){if(y<1900||y>2100){return-1}if(y==1900&&m==1&&d<31){return-1}if(!y){var objDate=new Date()}else{var objDate=new Date(y,parseInt(m)-1,d)}var i,leap=0,temp=0;var y=objDate.getFullYear(),m=objDate.getMonth()+1,d=objDate.getDate();var offset=(Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate())-Date.UTC(1900,0,31))/86400000;for(i=1900;i<2101&&offset>0;i++){temp=calendar.lYearDays(i);offset-=temp}if(offset<0){offset+=temp;i--}var isTodayObj=new Date(),isToday=false;if(isTodayObj.getFullYear()==y&&isTodayObj.getMonth()+1==m&&isTodayObj.getDate()==d){isToday=true}var nWeek=objDate.getDay(),cWeek=calendar.nStr1[nWeek];if(nWeek==0){nWeek=7}var year=i;var leap=calendar.leapMonth(i);var isLeap=false;for(i=1;i<13&&offset>0;i++){if(leap>0&&i==leap+1&&isLeap==false){--i;isLeap=true;temp=calendar.leapDays(year)}else{temp=calendar.monthDays(year,i)}if(isLeap==true&&i==leap+1){isLeap=false}offset-=temp}if(offset==0&&leap>0&&i==leap+1){if(isLeap){isLeap=false}else{isLeap=true;--i}}if(offset<0){offset+=temp;--i}var month=i;var day=offset+1;var sm=m-1;var gzY=calendar.toGanZhiYear(year);var firstNode=calendar.getTerm(y,m*2-1);var secondNode=calendar.getTerm(y,m*2);var gzM=calendar.toGanZhi((y-1900)*12+m+11);if(d>=firstNode){gzM=calendar.toGanZhi((y-1900)*12+m+12)}var isTerm=false;var Term=null;if(firstNode==d){isTerm=true;Term=calendar.solarTerm[m*2-2]}if(secondNode==d){isTerm=true;Term=calendar.solarTerm[m*2-1]}var dayCyclical=Date.UTC(y,sm,1,0,0,0,0)/86400000+25567+10;var gzD=calendar.toGanZhi(dayCyclical+d-1);var astro=calendar.toAstro(m,d);return{lYear:year,lMonth:month,lDay:day,Animal:calendar.getAnimal(year),IMonthCn:(isLeap?"\u95f0":"")+calendar.toChinaMonth(month),IDayCn:calendar.toChinaDay(day),cYear:y,cMonth:m,cDay:d,gzYear:gzY,gzMonth:gzM,gzDay:gzD,isToday:isToday,isLeap:isLeap,nWeek:nWeek,ncWeek:"\u661f\u671f"+cWeek,isTerm:isTerm,Term:Term,astro:astro,}},lunar2solar:function(y,m,d,isLeapMonth){var isLeapMonth=!!isLeapMonth;var leapOffset=0;var leapMonth=calendar.leapMonth(y);var leapDay=calendar.leapDays(y);if(isLeapMonth&&leapMonth!=m){return-1}if((y==2100&&m==12&&d>1)||(y==1900&&m==1&&d<31)){return-1}var day=calendar.monthDays(y,m);var _day=day;if(isLeapMonth){_day=calendar.leapDays(y,m)}if(y<1900||y>2100||d>_day){return-1}var offset=0;for(var i=1900;i0){offset+=calendar.leapDays(y);isAdd=true}}offset+=calendar.monthDays(y,i)}if(isLeapMonth){offset+=day}var stmap=Date.UTC(1900,1,30,0,0,0);var calObj=new Date((offset+d-31)*86400000+stmap);var cY=calObj.getUTCFullYear();var cM=calObj.getUTCMonth()+1;var cD=calObj.getUTCDate();return calendar.solar2lunar(cY,cM,cD)},birthday:function(y,m,d,nongli,isLeapMonth){var date;var now=new Date();if(nongli){var now_d=this.solar2lunar(now.getFullYear(),now.getMonth()+1,now.getDate());var now_year=now_d.lYear;date=this.birthBylunar(now_year,m,d,isLeapMonth);if(this.daysBetween(date)<=0){now_year++;date=this.birthBylunar(now_year,m,d,isLeapMonth)}}else{var now_year=now.getFullYear();date=this.solar2lunar(now_year,m,d);if(this.daysBetween(date)<=0){now_year++;date=this.solar2lunar(now_year,m,d)}}return[date.cYear,date.cMonth,date.cDay,this.daysBetween(date)]},birthBylunar:function(y,m,d,isLeapMonth){if(isLeapMonth&&this.leapMonth(y)==m){d=this.lunar2solar(y,m,d,isLeapMonth)}else{d=this.lunar2solar(y,m,d,false)}return d},daysBetween:function(d){var now=new Date();var date=new Date(d.cYear,d.cMonth-1,d.cDay);return parseInt((date.getTime()-now.getTime())/(24*3600*1000))},verifyTime(date){var dateFormat=/^(\d{4})-(\d{1,2})-(\d{1,2})$/;return dateFormat.test(date)},getAnimalZodiacToEmoji(zodiac){var data={鼠:"🐭",牛:"🐂",虎:"🐯",兔:"🐇",龙:"🐲",蛇:"🐍",马:"🐴",羊:"🐑",猴:"🐵",鸡:"🐔",狗:"🐶",猪:"🐷",};return data[zodiac]||""},getAstroToEmoji(astro){var data={白羊座:"♈",金牛座:"♉",双子座:"♊",巨蟹座:"♋",狮子座:"♌",处女座:"♍",天秤座:"♎",天蝎座:"♏",射手座:"♐",摩羯座:"♑",水瓶座:"♒",双鱼座:"♓",蛇夫座:"⛎",};return data[astro]||""},};return calendar} \ No newline at end of file diff --git a/Scripts/CarWidget.js b/Scripts/CarWidget.js new file mode 100644 index 0000000..02cb286 --- /dev/null +++ b/Scripts/CarWidget.js @@ -0,0 +1,342 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: car; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'paperplane', color: '#722ed1' }, + type: 'input', + title: '油价设置', + desc: '89|92|95|0', + val: 'oilNumber', + }); + + this.registerAction({ + icon: { name: 'car', color: '#f5222d' }, + type: 'input', + title: '依赖插件', + desc: '汽车的依赖插件例如 Ftms', + val: 'filePath', + }); + + this.registerAction({ + icon: { name: 'plus.viewfinder', color: '#fa8c16' }, + type: 'input', + title: '缩放比例', + desc: '比例越大进度条越长', + placeholder: '取值 0~1', + val: 'scale', + }); + } + + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + this.cacheName = this.md5(`dataSouce_${this.en}`); + const filePath = this.settings.filePath || 'Ftms'; + const carModule = require(`./${filePath}`); + const carService = new carModule(this); + this.scale = parseFloat(this.settings.scale) || 1; // 柱状图比例高度,值越大,柱状范围越广 + this.init = carService.init; + this.name = carService.name; + this.logo = carService.logo; + this.viewColor = Color.dynamic( + new Color('#d9d9d9', parseFloat(this.settings.lightOpacity || 1)), + new Color('#8c8c8c', parseFloat(this.settings.darkOpacity || 1)) + ); + } + + widgetHeight = 145; + + serveInfo = { + carNumber: '', + }; + + dataSource = { + remoteInfo: { + datatime: '', + list: [], + userId: '', + carNumber: '', + }, + monitorInfo: { + km: '0', + oilRate: '0', + oilWaste: '0', + oilWasteText: '', + }, + safeText: '', + oilPriceText: '', + oilZDE: 0, + }; + + createProgressBar( + soFar, + total = 100, + width = 400, + height = 40, + showPercentage = false + ) { + const context = new DrawContext(); + context.size = new Size(width, height); + context.opaque = false; + context.respectScreenScale = true; + + // bar background + context.setFillColor(new Color('#48484b')); + const bgPath = new Path(); + bgPath.addRoundedRect( + new Rect(0, 0, width, height), + height / 2, + height / 2 - 1 + ); + context.addPath(bgPath); + context.fillPath(); + + // bar foreground + context.setFillColor(new Color('#e8e8e8')); + const fgPath = new Path(); + fgPath.addRoundedRect( + new Rect(0, 0, (width * soFar) / total, height), + height / 2, + height / 2 - 1 + ); + context.addPath(fgPath); + context.fillPath(); + + if (showPercentage) { + const percentage = ((soFar / total) * 100).toFixed(2); + let xPos = (width * soFar) / total + 5; + // if over 70%, show in foreground area + // to ensure that it doesn't overflow the display + if (percentage > 70) { + xPos = (width * soFar) / total - 55; + } + + context.setFont(Font.semiboldRoundedSystemFont(14)); + context.setTextColor(primaryTextColor); + context.drawText(`${percentage}%`, new Point(xPos, height / 14)); + } + + return context.getImage(); + } + + renderBorder = (stack) => { + stack.borderWidth = 1; + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + renderSmall = async (w) => { + w.addSpacer(); + + const stack = w.addStack(); + stack.layoutVertically(); + const headerStack = stack.addStack(); + headerStack.centerAlignContent(); + headerStack.addSpacer(10); + const gasImg = SFSymbol.named('fuelpump').image; + + const gasIcon = headerStack.addImage(gasImg); + gasIcon.imageSize = new Size(16, 16); + gasIcon.tintColor = this.widgetColor; + headerStack.addSpacer(5); + + const oilRateStackText = headerStack.addText( + `${this.dataSource.monitorInfo.oilRate}%` + ); + oilRateStackText.textColor = this.widgetColor; + oilRateStackText.font = Font.boldSystemFont(14); + + headerStack.addSpacer(); + const logImg = await this.renderImage(this.logo); + const logImgStack = headerStack.addImage(logImg); + logImgStack.imageSize = new Size(20, 20); + headerStack.addSpacer(10); + + const bodyStack = stack.addStack(); + bodyStack.centerAlignContent(); + bodyStack.addSpacer(); + const progressImg = this.createProgressBar( + this.dataSource.monitorInfo.oilRate + ); + const progressBar = bodyStack.addImage(progressImg); + progressBar.imageSize = new Size(this.widgetHeight * this.scale, 28); + bodyStack.addSpacer(); + + stack.addSpacer(); + + const oilWasteStack = stack.addStack(); + oilWasteStack.centerAlignContent(); + oilWasteStack.addSpacer(); + const oilWasteStackText = oilWasteStack.addText( + this.dataSource.monitorInfo.oilWasteText + ); + oilWasteStackText.textColor = this.widgetColor; + oilWasteStackText.font = Font.boldSystemFont(10); + oilWasteStack.addSpacer(5); + const oilPriceStackText = oilWasteStack.addText( + this.dataSource.oilPriceText + ); + oilPriceStackText.textColor = this.widgetColor; + oilPriceStackText.font = Font.boldSystemFont(10); + oilWasteStack.addSpacer(2); + const oilStatus = this.dataSource.oilZDE > 0; + const oilZdeImage = SFSymbol.named( + oilStatus ? 'arrow.up' : 'arrow.down' + ).image; + + const oilZdeWidgetImg = oilWasteStack.addImage(oilZdeImage); + oilZdeWidgetImg.tintColor = new Color(oilStatus ? '#f5222d' : '#a0d911'); + oilZdeWidgetImg.imageSize = new Size(10, 10); + + oilWasteStack.addSpacer(); + + const kilometerStack = stack.addStack(); + + kilometerStack.centerAlignContent(); + kilometerStack.addSpacer(); + const panoImg = SFSymbol.named('speedometer').image; + + const panoImgStack = kilometerStack.addStack(); + panoImgStack.setPadding(5, 0, 0, 0); + const panoStack = panoImgStack.addImage(panoImg); + panoStack.tintColor = this.widgetColor; + panoStack.imageSize = new Size(20, 20); + kilometerStack.addSpacer(5); + + const oilWasteText = kilometerStack.addText(this.dataSource.monitorInfo.km); + oilWasteText.font = Font.boldSystemFont(28); + oilWasteText.textColor = this.widgetColor; + kilometerStack.addSpacer(5); + const unitStack = kilometerStack.addStack(); + unitStack.setPadding(5, 0, 0, 0); + const oilWasteUnit = unitStack.addText('km'); + oilWasteUnit.font = Font.boldSystemFont(14); + oilWasteUnit.textColor = this.widgetColor; + kilometerStack.addSpacer(); + + stack.addSpacer(); + + const btBodyStack = stack.addStack(); + btBodyStack.addSpacer(); + const bottomStack = btBodyStack.addStack(); + bottomStack.setPadding(10, 0, 10, 0); + bottomStack.centerAlignContent(); + bottomStack.addSpacer(); + bottomStack.cornerRadius = 15; + bottomStack.backgroundColor = this.viewColor; + const dataTime = this.dataSource.remoteInfo.datatime; + const countKmText = bottomStack.addText(`上传:${dataTime || '-'}`); + countKmText.textColor = this.widgetColor; + countKmText.font = Font.boldSystemFont(12); + countKmText.centerAlignText(); + bottomStack.addSpacer(); + w.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return this.renderSmall(w); + }; + + renderMedium = async (w) => { + const containerStack = w.addStack(); + containerStack.centerAlignContent(); + const carStack = containerStack.addStack(); + carStack.addSpacer(); + carStack.backgroundColor = this.viewColor; + + carStack.layoutVertically(); + + carStack.centerAlignContent(); + carStack.size = new Size(this.widgetHeight, this.widgetHeight); + carStack.cornerRadius = 20; + const carImg = await this.renderImage(this.serveInfo.picUrl); + + const carImgStack = carStack.addStack(); + const carResStack = carImgStack.addImage(carImg); + carResStack.imageSize = new Size(137.5, 70); + + carStack.addSpacer(); + + const carNumberStack = carStack.addStack(); + carNumberStack.addSpacer(); + carNumberStack.centerAlignContent(); + const carNumberText = carNumberStack.addText(this.serveInfo.carNumber); + carNumberText.font = Font.boldSystemFont(24); + carNumberText.textColor = this.widgetColor; + carNumberText.centerAlignText(); + carNumberStack.addSpacer(); + + carStack.addSpacer(); + + const carSafeStack = carStack.addStack(); + carSafeStack.addSpacer(); + carSafeStack.centerAlignContent(); + + let safeIconImg; + if (this.dataSource.safeText) { + safeIconImg = carSafeStack.addImage(SFSymbol.named('lock.open').image); + } else { + safeIconImg = carSafeStack.addImage(SFSymbol.named('lock').image); + } + + carSafeStack.addSpacer(5); + const statusText = carSafeStack.addText( + this.dataSource.safeText || '已上锁' + ); + statusText.centerAlignText(); + statusText.font = Font.systemFont(12); + statusText.textColor = this.dataSource.safeText + ? new Color('#f5222d') + : this.widgetColor; + + safeIconImg.tintColor = statusText.textColor; + safeIconImg.imageSize = new Size(10, 14); + + carSafeStack.addSpacer(); + + carStack.addSpacer(); + + containerStack.addSpacer(); + const rightStack = containerStack.addStack(); + rightStack.layoutVertically(); + await this.renderSmall(rightStack); + + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(10, 10, 10, 10); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else { + return await this.notSupport(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/ChinaMobile.js b/Scripts/ChinaMobile.js new file mode 100644 index 0000000..3b00e90 --- /dev/null +++ b/Scripts/ChinaMobile.js @@ -0,0 +1,513 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: pink; icon-glyph: mobile-alt; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); +const CryptoJS = require('./crypto-js'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '中国移动'; + this.en = 'ChinaMobile'; + this.Run(); + } + + getfee = {}; + autologin = {}; + cookie = ''; + + fgCircleColor = Color.dynamic(new Color('#dddef3'), new Color('#fff')); + percentColor = this.widgetColor; + textColor1 = Color.dynamic(new Color('#333'), new Color('#fff')); + textColor2 = this.widgetColor; + + circleColor1 = new Color('#ffbb73'); + circleColor2 = new Color('#ff0029'); + circleColor3 = new Color('#00b800'); + circleColor4 = new Color('#8376f9'); + iconColor = new Color('#827af1'); + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + date = new Date(); + arrUpdateTime = [ + this.format(this.date.getMonth() + 1), + this.format(this.date.getDate()), + this.format(this.date.getHours()), + this.format(this.date.getMinutes()), + ]; + + // percent 的计算方式,剩余/总量 * 100 = 百分比| 百分比 * 3.6 ,为显示进度。 + phoneBill = { + percent: 0, + label: '话费剩余', + count: 0, + unit: '元', + icon: 'yensign.circle', + circleColor: this.circleColor1, + }; + + flow = { + percent: 0, + label: '流量剩余', + count: 0, + unit: 'M', + icon: 'waveform.path.badge.minus', + circleColor: this.circleColor2, + }; + + voice = { + percent: 0, + label: '语音剩余', + count: 0, + unit: '分钟', + icon: 'mic', + circleColor: this.circleColor3, + }; + + updateTime = { + percent: 0, + label: '移动更新', + count: `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + unit: '', + urlIcon: 'https://raw.githubusercontent.com/Orz-3/mini/master/10086.png', + circleColor: this.circleColor4, + }; + maxFee = 100; + canvSize = 100; + canvWidth = 5; // circle thickness + canvRadius = 100; // circle radius + dayRadiusOffset = 60; + canvTextSize = 40; + + init = async () => { + try { + const nowHours = this.date.getHours(); + const updateHours = nowHours > 12 ? 24 : 12; + this.updateTime.percent = Math.floor((nowHours / updateHours) * 100); + await this.login(); + await this.queryFee(); + await this.queryFlow(); + } catch (e) { + console.log(e); + } + }; + + async login() { + try { + const options = this.autologin; + const request = new Request(options.url); + Object.keys(options).forEach((key) => { + request[key] = options[key]; + }); + request.method = 'POST'; + await request.loadString(); + this.cookie = request.response.headers['Set-Cookie']; + if (this.cookie) { + console.log('✅登陆成功'); + } else { + console.log('❌登陆失败'); + } + } catch (e) { + console.log('❌登陆失败,请检查 Ck:' + e); + } + } + + async queryFee() { + try { + const options = this.getfee; + const body = JSON.parse(this.decrypt(options.body, 'bAIgvwAuA4tbDr9d')); + const cellNum = body.reqBody.cellNum; + const bodystr = `{"t":"${CryptoJS.MD5( + this.cookie, + ).toString()}","cv":"9.9.9","reqBody":{"cellNum":"${cellNum}"}}`; + options.body = this.encrypt(bodystr, 'bAIgvwAuA4tbDr9d'); + options.headers['Cookie'] = this.cookie; + options.headers['xs'] = CryptoJS.MD5( + options.url + '_' + bodystr + '_Leadeon/SecurityOrganization', + ).toString(); + const request = new Request(options.url); + request.method = 'POST'; + request.headers = options.headers; + request.body = options.body; + const webView = new WebView(); + await webView.loadRequest(request); + const response = await webView.evaluateJavaScript( + 'completion(document.body.innerText);', + true, + ); + const data = JSON.parse(this.decrypt(response, 'GS7VelkJl5IT1uwQ')); + if (data.retCode === '000000') { + console.log('✅费用信息获取成功'); + const {rspBody} = data; + this.phoneBill.count = rspBody.curFee; + this.phoneBill.percent = Math.floor((this.phoneBill.count / this.maxFee).toFixed(2) * 100); + } else { + console.log('❌费用信息获取失败,请检查 Ck 配置' + data.retDesc); + } + } catch (e) { + console.log('❌费用信息获取失败:' + e); + } + } + + async queryFlow() { + try { + const options = this.getfee; + const body = JSON.parse(this.decrypt(options.body, 'bAIgvwAuA4tbDr9d')); + const cellNum = body.reqBody.cellNum; + options.url = + 'https://clientaccess.10086.cn/biz-orange/BN/newComboMealResouceUnite/getNewComboMealResource'; + const bodystr = `{"t":"${CryptoJS.MD5( + this.cookie, + ). + toString()}","cv":"9.9.9","reqBody":{"cellNum":"${cellNum}","tag":"3"}}`; + options.body = this.encrypt(bodystr, 'bAIgvwAuA4tbDr9d'); + options.headers['Cookie'] = this.cookie; + options.headers['xs'] = CryptoJS.MD5( + options.url + '_' + bodystr + '_Leadeon/SecurityOrganization', + ).toString(); + + const request = new Request(options.url); + request.method = 'POST'; + request.headers = options.headers; + request.body = options.body; + const webView = new WebView(); + await webView.loadRequest(request); + const response = await webView.evaluateJavaScript( + 'completion(document.body.innerText);', + true, + ); + + const data = JSON.parse(this.decrypt(response, 'GS7VelkJl5IT1uwQ')); + if (data.retCode === '000000') { + console.log('✅套餐信息获取成功'); + const res = data.rspBody.qryInfoRsp[0].resourcesTotal; + const flowRes = res.find((r) => r.resourcesCode === '04'); + const voiceRes = res.find((r) => r.resourcesCode === '01'); + var flowResValue = '未开通', + voiceResValue = ''; + if (flowRes) { + const total = this.translateFlow({ + value: flowRes.allTotalRes, + code: flowRes.allUnit, + }); + const remain = this.translateFlow({ + value: flowRes.allRemainRes, + code: flowRes.remUnit, + }); + + this.flow.percent = Math.floor( + (remain.value / (total.value || 1)) * 100, + ); + this.flow.count = flowRes.allRemainRes; + this.flow.unit = remain.unit; + flowResValue = `${flowRes.allRemainRes}${remain.unit}`; + } + if (voiceRes) { + this.voice.percent = Math.floor( + (voiceRes.allRemainRes / (voiceRes.allTotalRes || 1)) * 100, + ); + this.voice.count = voiceRes.allRemainRes; + voiceResValue = voiceRes.allRemainRes; + } + console.log(`✅流量:` + flowResValue + '\n ✅语音:' + voiceResValue); + } else { + console.log('❌流量信息获取失败,请检查 Ck 配置' + data.retDesc); + } + } catch (e) { + console.log('❌流量信息获取失败:' + e); + } + } + + encrypt(str, key) { + return CryptoJS.AES.encrypt( + CryptoJS.enc.Utf8.parse(str), + CryptoJS.enc.Utf8.parse(key), + { + iv: CryptoJS.enc.Utf8.parse('9791027341711819'), + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }, + ).toString(); + } + + decrypt(str, key) { + return CryptoJS.AES.decrypt(str, CryptoJS.enc.Utf8.parse(key), { + iv: CryptoJS.enc.Utf8.parse('9791027341711819'), + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }).toString(CryptoJS.enc.Utf8); + } + + translateFlow(value) { + const unit = [ + {unit: 'G', value: 1024, code: '05'}, + {unit: 'M', value: 1, code: '04'}, + ]; + const data = {unit: '', ...value}; + unit.forEach((item) => { + if (value.code === item.code) { + data.unit = item.unit; + data.value = + Math.floor(parseFloat(data.value) * item.value * 100) / 100; + } + }); + data.value = parseInt(data.value); + return data; + } + + makeCanvas() { + const canvas = new DrawContext(); + canvas.opaque = false; + canvas.respectScreenScale = true; + canvas.size = new Size(this.canvSize, this.canvSize); + return canvas; + } + + makeCircle(canvas, radiusOffset, degree, color) { + let ctr = new Point(this.canvSize / 2, this.canvSize / 2); + // Outer circle + const bgx = ctr.x - (this.canvRadius - radiusOffset); + const bgy = ctr.y - (this.canvRadius - radiusOffset); + const bgd = 2 * (this.canvRadius - radiusOffset); + const bgr = new Rect(bgx, bgy, bgd, bgd); + canvas.setStrokeColor(this.fgCircleColor); + canvas.setLineWidth(2); + canvas.strokeEllipse(bgr); + // Inner circle + canvas.setFillColor(color); + for (let t = 0; t < degree; t++) { + const rect_x = + ctr.x + + (this.canvRadius - radiusOffset) * this.sinDeg(t) - + this.canvWidth / 2; + const rect_y = + ctr.y - + (this.canvRadius - radiusOffset) * this.cosDeg(t) - + this.canvWidth / 2; + const rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); + canvas.fillEllipse(rect_r); + } + } + + drawText(txt, canvas, txtOffset, fontSize) { + const txtRect = new Rect( + this.canvTextSize / 2 - 20, + txtOffset - this.canvTextSize / 2, + this.canvSize, + this.canvTextSize, + ); + canvas.setTextColor(this.percentColor); + canvas.setFont(Font.boldSystemFont(fontSize)); + canvas.setTextAlignedCenter(); + canvas.drawTextInRect(`${txt}`, txtRect); + } + + drawPointText(txt, canvas, txtPoint, fontSize) { + canvas.setTextColor(this.percentColor); + canvas.setFont(Font.boldSystemFont(fontSize)); + canvas.drawText(txt, txtPoint); + } + + sinDeg(deg) { + return Math.sin((deg * Math.PI) / 180); + } + + cosDeg(deg) { + return Math.cos((deg * Math.PI) / 180); + } + + setCircleText = (stack, data) => { + const stackCircle = stack.addStack(); + const canvas = this.makeCanvas(); + stackCircle.size = new Size(70, 70); + this.makeCircle( + canvas, + this.dayRadiusOffset, + data.percent * 3.6, + data.circleColor, + ); + + this.drawText(data.percent, canvas, 75, 18); + this.drawPointText(`%`, canvas, new Point(65, 50), 14); + stackCircle.backgroundImage = canvas.getImage(); + + stackCircle.setPadding(20, 0, 0, 0); + stackCircle.addSpacer(); + + const icon = data.urlIcon + ? {image: data.icon} + : SFSymbol.named(data.icon); + const imageIcon = stackCircle.addImage(icon.image); + + imageIcon.tintColor = this.iconColor; + imageIcon.imageSize = new Size(15, 15); + // canvas.drawImageInRect(icon.image, new Rect(110, 80, 60, 60)); + stackCircle.addSpacer(); + + stack.addSpacer(5); + const stackDesc = stack.addStack(); + stackDesc.size = new Size(70, 60); + stackDesc.centerAlignContent(); + stackDesc.layoutVertically(); + stackDesc.addSpacer(10); + const textLabel = this.textFormat.defaultText; + textLabel.size = 12; + textLabel.color = this.textColor2; + this.provideText(data.label, stackDesc, textLabel); + stackDesc.addSpacer(10); + + const stackDescFooter = stackDesc.addStack(); + stackDescFooter.centerAlignContent(); + const textCount = this.textFormat.title; + textCount.size = 16; + textCount.color = this.textColor1; + this.provideText(`${data.count}`, stackDescFooter, textCount); + stackDescFooter.addSpacer(2); + this.provideText(data.unit, stackDescFooter, textLabel); + }; + + renderSmall = async (w) => { + w.setPadding(5, 5, 5, 5); + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackTop = stackBody.addStack(); + this.setCircleText(stackTop, this.phoneBill); + const stackBottom = stackBody.addStack(); + this.setCircleText(stackBottom, this.flow); + + const stackFooter = stackBody.addStack(); + stackFooter.addSpacer(); + const text = this.textFormat.defaultText; + text.color = new Color('#aaa'); + this.provideText( + `移动更新:${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + stackFooter, + text, + ); + stackFooter.addSpacer(); + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackTop = stackBody.addStack(); + this.setCircleText(stackTop, this.phoneBill); + this.setCircleText(stackTop, this.flow); + const stackBottom = stackBody.addStack(); + this.setCircleText(stackBottom, this.voice); + this.updateTime.icon = await this.$request.get( + this.updateTime.urlIcon, + 'IMG', + ); + this.setCircleText(stackBottom, this.updateTime); + return w; + }; + + renderLarge = async (w) => { + return await this.renderMedium(w); + }; + + Run() { + if (config.runsInApp) { + this.registerAction('费用进度', async () => { + await this.setAlertInput(`${this.name}`, '预计当月费用使用值', { + maxFee: '默认 100 元', + }); + }); + const widgetInitConfig = { + getfee: 'chavy_getfee_cmcc', + autologin: 'chavy_autologin_cmcc', + }; + this.registerAction('颜色配置', async () => { + await this.setAlertInput( + `${this.name}颜色配置`, + '进度条颜色|底圈颜色\n图标颜色|比值颜色|值颜色', + { + step1: '进度颜色 1', + step2: '进度颜色 2', + step3: '进度颜色 3', + step4: '进度颜色 4', + inner: '底圈颜色', + icon: '图标颜色', + percent: '比值颜色', + value: '值颜色', + }, + ); + }); + this.registerAction('账号设置', async () => { + await this.setAlertInput( + `${this.name}账号`, + '读取 BoxJS 缓存信息', + widgetInitConfig, + ); + }); + this.registerAction('代理缓存', async () => { + await this.setCacheBoxJSData(widgetInitConfig); + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + try { + const { + getfee, + autologin, + step1, + step2, + step3, + step4, + inner, + icon, + percent, + value, + maxFee, + } = this.settings; + this.fgCircleColor = inner ? new Color(inner) : this.fgCircleColor; + this.textColor1 = value ? new Color(value) : this.textColor1; + + this.phoneBill.circleColor = step1 ? new Color(step1) : this.circleColor1; + this.flow.circleColor = step2 ? new Color(step2) : this.circleColor2; + this.voice.circleColor = step3 ? new Color(step3) : this.circleColor3; + this.updateTime.circleColor = step4 + ? new Color(step4) + : this.circleColor4; + + this.iconColor = icon ? new Color(icon) : this.iconColor; + this.percentColor = percent ? new Color(percent) : this.percentColor; + this.maxFee = parseFloat(maxFee) || this.maxFee; + + this.getfee = JSON.parse(getfee || '{}'); + this.autologin = JSON.parse(autologin || '{}'); + } catch (e) { + console.log(e); + } + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/ChinaTelecom.js b/Scripts/ChinaTelecom.js new file mode 100644 index 0000000..03fa63f --- /dev/null +++ b/Scripts/ChinaTelecom.js @@ -0,0 +1,444 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: pink; icon-glyph: paper-plane; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = "中国电信"; + this.en = "ChinaTelecom"; + this.Run(); + } + + cookie = ""; + authToken = ""; + fgCircleColor = Color.dynamic(new Color("#dddef3"), new Color("#fff")); + percentColor = this.widgetColor; + textColor1 = Color.dynamic(new Color("#333"), new Color("#fff")); + textColor2 = this.widgetColor; + + circleColor1 = new Color("#ffbb73"); + circleColor2 = new Color("#ff0029"); + circleColor3 = new Color("#00b800"); + circleColor4 = new Color("#8376f9"); + iconColor = new Color("#827af1"); + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + date = new Date(); + arrUpdateTime = [ + this.format(this.date.getMonth() + 1), + this.format(this.date.getDate()), + this.format(this.date.getHours()), + this.format(this.date.getMinutes()), + ]; + + // percent 的计算方式,剩余/总量 * 100 = 百分比| 百分比 * 3.6 ,为显示进度。 + phoneBill = { + percent: 0, + label: "话费剩余", + count: 0, + unit: "元", + icon: "yensign.circle", + circleColor: this.circleColor1, + }; + + flow = { + percent: 0, + label: "流量剩余", + count: 0, + unit: "M", + icon: "waveform.path.badge.minus", + circleColor: this.circleColor2, + }; + + voice = { + percent: 0, + label: "语音剩余", + count: 0, + unit: "分钟", + icon: "mic", + circleColor: this.circleColor3, + }; + + updateTime = { + percent: 0, + label: "电信更新", + count: `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + unit: "", + urlIcon: "https://raw.githubusercontent.com/Orz-3/mini/master/10000.png", + circleColor: this.circleColor4, + }; + + canvSize = 100; + canvWidth = 5; // circle thickness + canvRadius = 100; // circle radius + dayRadiusOffset = 60; + canvTextSize = 40; + + options = { + headers: { + // type: "alipayMiniApp", + // "User-Agent": "TYUserCenter/2.8 (iPhone; iOS 14.0; Scale/3.00)", + }, + // body: "t=tysuit", + method: "POST", + }; + + fetchUri = { + detail: "https://e.189.cn/store/user/package_detail.do", + balance: "https://e.189.cn/store/user/balance_new.do", + }; + + init = async () => { + try { + const nowHours = this.date.getHours(); + const updateHours = nowHours > 12 ? 24 : 12; + this.updateTime.percent = Math.floor((nowHours / updateHours) * 100); + await this.getData(); + } catch (e) { + console.log(e); + } + }; + + // MB 和 GB 自动转换 + formatFlow(number) { + const n = number / 1024; + if (n < 1024) { + return { count: n.toFixed(2), unit: "M" }; + } + return { count: (n / 1024).toFixed(2), unit: "G" }; + } + + getData = async () => { + const detail = await this.http({ + url: this.fetchUri.detail, + ...this.options, + }); + console.log(detail); + const balance = await this.http({ + url: this.fetchUri.balance, + ...this.options, + }); + + if (detail.result === 0) { + // 套餐分钟数 + this.voice.percent = Math.floor( + (parseInt(detail.voiceBalance) / parseInt(detail.voiceAmount)) * 100 + ); + this.voice.count = detail.voiceBalance; + console.log(detail.items); + if (!detail.count && !detail.total) { + detail.items.forEach((data) => { + if (data.offerType !== 19) { + data.items.forEach((item) => { + if (item.unitTypeId === "3") { + if (item.usageAmount !== "0" && item.balanceAmount !== "0") { + this.flow.percent = Math.floor( + (item.balanceAmount / (item.ratableAmount || 1)) * 100 + ); + const flow = this.formatFlow(item.balanceAmount); + this.flow.count = flow.count; + this.flow.unit = flow.unit; + this.flow.max = item.ratableAmount; + } + } + }); + } + }); + } else { + this.flow.percent = Math.floor( + (detail.balance / (detail.total || 1)) * 100 + ); + const flow = this.formatFlow(detail.balance); + this.flow.count = flow.count; + this.flow.unit = flow.unit; + this.flow.max = detail.total; + } + } + if (balance.result === 0) { + // 余额 + this.phoneBill.count = parseFloat( + parseInt(balance.totalBalanceAvailable) / 100 + ).toFixed(2); + } + this.phoneBill.percent = Math.floor((this.phoneBill.count / 100) * 100); + }; + + makeCanvas() { + const canvas = new DrawContext(); + canvas.opaque = false; + canvas.respectScreenScale = true; + canvas.size = new Size(this.canvSize, this.canvSize); + return canvas; + } + + makeCircle(canvas, radiusOffset, degree, color) { + let ctr = new Point(this.canvSize / 2, this.canvSize / 2); + // Outer circle + const bgx = ctr.x - (this.canvRadius - radiusOffset); + const bgy = ctr.y - (this.canvRadius - radiusOffset); + const bgd = 2 * (this.canvRadius - radiusOffset); + const bgr = new Rect(bgx, bgy, bgd, bgd); + canvas.setStrokeColor(this.fgCircleColor); + canvas.setLineWidth(2); + canvas.strokeEllipse(bgr); + // Inner circle + canvas.setFillColor(color); + for (let t = 0; t < degree; t++) { + const rect_x = + ctr.x + + (this.canvRadius - radiusOffset) * this.sinDeg(t) - + this.canvWidth / 2; + const rect_y = + ctr.y - + (this.canvRadius - radiusOffset) * this.cosDeg(t) - + this.canvWidth / 2; + const rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); + canvas.fillEllipse(rect_r); + } + } + + drawText(txt, canvas, txtOffset, fontSize) { + const txtRect = new Rect( + this.canvTextSize / 2 - 20, + txtOffset - this.canvTextSize / 2, + this.canvSize, + this.canvTextSize + ); + canvas.setTextColor(this.percentColor); + canvas.setFont(Font.boldSystemFont(fontSize)); + canvas.setTextAlignedCenter(); + canvas.drawTextInRect(`${txt}`, txtRect); + } + + drawPointText(txt, canvas, txtPoint, fontSize) { + canvas.setTextColor(this.percentColor); + canvas.setFont(Font.boldSystemFont(fontSize)); + canvas.drawText(txt, txtPoint); + } + + sinDeg(deg) { + return Math.sin((deg * Math.PI) / 180); + } + + cosDeg(deg) { + return Math.cos((deg * Math.PI) / 180); + } + + setCircleText = (stack, data) => { + const stackCircle = stack.addStack(); + const canvas = this.makeCanvas(); + stackCircle.size = new Size(70, 70); + this.makeCircle( + canvas, + this.dayRadiusOffset, + data.percent * 3.6, + data.circleColor + ); + + this.drawText(data.percent, canvas, 75, 18); + this.drawPointText(`%`, canvas, new Point(65, 50), 14); + stackCircle.backgroundImage = canvas.getImage(); + + stackCircle.setPadding(20, 0, 0, 0); + stackCircle.addSpacer(); + const icon = data.urlIcon + ? { image: data.icon } + : SFSymbol.named(data.icon); + const imageIcon = stackCircle.addImage(icon.image); + imageIcon.tintColor = this.iconColor; + imageIcon.imageSize = new Size(15, 15); + // canvas.drawImageInRect(icon.image, new Rect(110, 80, 60, 60)); + stackCircle.addSpacer(); + + stack.addSpacer(5); + const stackDesc = stack.addStack(); + stackDesc.size = new Size(70, 60); + stackDesc.centerAlignContent(); + stackDesc.layoutVertically(); + stackDesc.addSpacer(10); + const textLabel = this.textFormat.defaultText; + textLabel.size = 12; + textLabel.color = this.textColor2; + this.provideText(data.label, stackDesc, textLabel); + stackDesc.addSpacer(10); + + const stackDescFooter = stackDesc.addStack(); + stackDescFooter.centerAlignContent(); + const textCount = this.textFormat.title; + textCount.size = 16; + textCount.color = this.textColor1; + this.provideText(`${data.count}`, stackDescFooter, textCount); + stackDescFooter.addSpacer(2); + this.provideText(data.unit, stackDescFooter, textLabel); + }; + + renderSmall = async (w) => { + w.setPadding(5, 5, 5, 5); + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackTop = stackBody.addStack(); + this.setCircleText(stackTop, this.phoneBill); + const stackBottom = stackBody.addStack(); + this.setCircleText(stackBottom, this.flow); + + const stackFooter = stackBody.addStack(); + stackFooter.addSpacer(); + const text = this.textFormat.defaultText; + text.color = new Color("#aaa"); + this.provideText( + `电信更新:${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + stackFooter, + text + ); + stackFooter.addSpacer(); + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackTop = stackBody.addStack(); + this.setCircleText(stackTop, this.phoneBill); + this.setCircleText(stackTop, this.flow); + const stackBottom = stackBody.addStack(); + this.setCircleText(stackBottom, this.voice); + this.updateTime.icon = await this.$request.get( + this.updateTime.urlIcon, + "IMG" + ); + this.setCircleText(stackBottom, this.updateTime); + return w; + }; + + renderLarge = async (w) => { + return await this.renderMedium(w); + }; + + renderWebView = async () => { + const webView = new WebView(); + const url = "https://e.189.cn/index.do"; + await webView.loadURL(url); + await webView.present(false); + + const request = new Request(this.fetchUri.detail); + request.method = "POST"; + const response = await request.loadJSON(); + console.log(response); + if (response.result === -10001) { + const index = await this.generateAlert("未获取到用户信息", [ + "取消", + "重试", + ]); + if (index === 0) return; + await this.renderWebView(); + } else { + const cookies = request.response.cookies; + let cookie = []; + cookie = cookies.map((item) => `${item.name}=${item.value}`); + cookie = cookie.join("; "); + this.settings.cookie = cookie; + this.saveSettings(); + } + }; + + Run() { + if (config.runsInApp) { + const widgetInitConfig = { cookie: "china_telecom_cookie" }; + this.registerAction("颜色配置", async () => { + await this.setAlertInput( + `${this.name}颜色配置`, + "进度条颜色|底圈颜色\n图标颜色|比值颜色|值颜色", + { + step1: "进度颜色 1", + step2: "进度颜色 2", + step3: "进度颜色 3", + step4: "进度颜色 4", + inner: "底圈颜色", + icon: "图标颜色", + percent: "比值颜色", + value: "值颜色", + } + ); + }); + this.registerAction("账号设置", async () => { + const index = await this.generateAlert("设置账号信息", [ + "取消", + "手动输入", + "网站登录", + ]); + if (index === 0) return; + + if (index === 1) + return this.setAlertInput( + `${this.name}账号设置`, + "手动输入电信 COOKIE", + { + cookie: "电信 cookie", + } + ); + return this.renderWebView(); + }); + this.registerAction("代理缓存", async () => { + await this.setCacheBoxJSData(widgetInitConfig); + }); + this.registerAction("基础设置", this.setWidgetConfig); + } + const { + step1, + step2, + step3, + step4, + inner, + icon, + percent, + value, + // authToken, + cookie, + } = this.settings; + this.fgCircleColor = inner ? new Color(inner) : this.fgCircleColor; + this.textColor1 = value ? new Color(value) : this.textColor1; + this.phoneBill.circleColor = step1 ? new Color(step1) : this.circleColor1; + this.flow.circleColor = step2 ? new Color(step2) : this.circleColor2; + this.voice.circleColor = step3 ? new Color(step3) : this.circleColor3; + this.updateTime.circleColor = step4 ? new Color(step4) : this.circleColor4; + + this.iconColor = icon ? new Color(icon) : this.iconColor; + this.percentColor = percent ? new Color(percent) : this.percentColor; + + this.cookie = cookie; + if (this.cookie) this.options.headers.cookie = this.cookie; + // this.authToken = authToken; + // if (this.authToken) this.options.headers.authToken = this.authToken; + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "medium") { + return await this.renderMedium(widget); + } else if (this.widgetFamily === "large") { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/ChinaUnicom.js b/Scripts/ChinaUnicom.js new file mode 100644 index 0000000..f36db83 --- /dev/null +++ b/Scripts/ChinaUnicom.js @@ -0,0 +1,402 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: pink; icon-glyph: paper-plane; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '中国联通'; + this.en = 'ChinaUnicom'; + this.Run(); + } + + loginheader = {}; + + fgCircleColor = Color.dynamic(new Color('#dddef3'), new Color('#fff')); + percentColor = this.widgetColor; + textColor1 = Color.dynamic(new Color('#333'), new Color('#fff')); + textColor2 = this.widgetColor; + + circleColor1 = new Color('#ffbb73'); + circleColor2 = new Color('#ff0029'); + circleColor3 = new Color('#00b800'); + circleColor4 = new Color('#8376f9'); + iconColor = new Color('#827af1'); + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + date = new Date(); + arrUpdateTime = [ + this.format(this.date.getMonth() + 1), + this.format(this.date.getDate()), + this.format(this.date.getHours()), + this.format(this.date.getMinutes()), + ]; + + maxFee = 100; + // percent 的计算方式,剩余/总量 * 100 = 百分比| 百分比 * 3.6 ,为显示进度。 + phoneBill = { + percent: 0, + label: '话费剩余', + count: 0, + unit: '元', + icon: 'yensign.circle', + circleColor: this.circleColor1, + }; + + flow = { + percent: 0, + label: '已用流量', + count: 0, + unit: 'M', + icon: 'waveform.path.badge.minus', + circleColor: this.circleColor2, + }; + + voice = { + percent: 0, + label: '语音剩余', + count: 0, + unit: '分钟', + icon: 'mic', + circleColor: this.circleColor3, + }; + + updateTime = { + percent: 0, + label: '联通更新', + count: `${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + unit: '', + urlIcon: 'https://raw.githubusercontent.com/Orz-3/mini/master/10010.png', + circleColor: this.circleColor4, + }; + + canvSize = 100; + canvWidth = 5; // circle thickness + canvRadius = 100; // circle radius + dayRadiusOffset = 60; + canvTextSize = 40; + + init = async () => { + try { + const nowHours = this.date.getHours(); + const updateHours = nowHours > 12 ? 24 : 12; + this.updateTime.percent = Math.floor((nowHours / updateHours) * 100); + await this.getinfo(); + } catch (e) { + console.log(e); + } + }; + + async getinfo() { + try { + const telNum = this.gettel(); + const url = { + url: `https://m.client.10010.com/mobileService/home/queryUserInfoSeven.htm?version=iphone_c@7.0403&desmobiel=${telNum}&showType=3`, + headers: { + Cookie: this.loginheader.Cookie, + }, + }; + const signinfo = await this.$request.get(url); + if (signinfo.code === 'Y') { + console.log('✅获取信息成功'); + console.log(signinfo.data); + signinfo.data.dataList.forEach((item) => { + let percent = 0; + if (item.usedTitle.includes('剩余')) + percent = item.usedTitle.replace('剩余', '').replace('%'); + if (item.usedTitle.includes('已用')) + percent = ( + 100 - parseFloat(item.usedTitle.replace('已用', '').replace('%')) + ).toFixed(2); + + if (item.type === 'flow') { + this.flow.count = item.number; + this.flow.unit = item.unit; + this.flow.percent = percent; + this.flow.label = item.remainTitle; + } + if (item.type === 'fee') { + this.phoneBill.count = item.number; + this.phoneBill.unit = item.unit; + this.phoneBill.percent = + Math.floor((item.number / this.maxFee).toFixed(2) * 100); + this.phoneBill.label = item.remainTitle; + } + if (item.type === 'voice') { + this.voice.count = item.number; + this.voice.unit = item.unit; + this.voice.percent = percent; + this.voice.label = item.remainTitle; + } + }); + } else { + throw 'cookie错误'; + } + } catch (e) { + console.log('❌获取信息失败:' + e); + } + } + + gettel() { + const reqCookie = this.loginheader.Cookie; + let tel = ''; + if (tel === '' && reqCookie.indexOf(`u_account=`) >= 0) + tel = reqCookie.match(/u_account=(.*?);/)[1]; + if (tel === '' && reqCookie.indexOf(`c_mobile=`) >= 0) { + tel = reqCookie.match(/c_mobile=(.*?);/)[1]; + } + return tel; + } + + makeCanvas() { + const canvas = new DrawContext(); + canvas.opaque = false; + canvas.respectScreenScale = true; + canvas.size = new Size(this.canvSize, this.canvSize); + return canvas; + } + + makeCircle(canvas, radiusOffset, degree, color) { + let ctr = new Point(this.canvSize / 2, this.canvSize / 2); + // Outer circle + const bgx = ctr.x - (this.canvRadius - radiusOffset); + const bgy = ctr.y - (this.canvRadius - radiusOffset); + const bgd = 2 * (this.canvRadius - radiusOffset); + const bgr = new Rect(bgx, bgy, bgd, bgd); + canvas.setStrokeColor(this.fgCircleColor); + canvas.setLineWidth(2); + canvas.strokeEllipse(bgr); + // Inner circle + canvas.setFillColor(color); + for (let t = 0; t < degree; t++) { + const rect_x = + ctr.x + + (this.canvRadius - radiusOffset) * this.sinDeg(t) - + this.canvWidth / 2; + const rect_y = + ctr.y - + (this.canvRadius - radiusOffset) * this.cosDeg(t) - + this.canvWidth / 2; + const rect_r = new Rect(rect_x, rect_y, this.canvWidth, this.canvWidth); + canvas.fillEllipse(rect_r); + } + } + + drawText(txt, canvas, txtOffset, fontSize) { + const txtRect = new Rect( + this.canvTextSize / 2 - 20, + txtOffset - this.canvTextSize / 2, + this.canvSize, + this.canvTextSize, + ); + canvas.setTextColor(this.percentColor); + canvas.setFont(Font.boldSystemFont(fontSize)); + canvas.setTextAlignedCenter(); + canvas.drawTextInRect(`${txt}`, txtRect); + } + + drawPointText(txt, canvas, txtPoint, fontSize) { + canvas.setTextColor(this.percentColor); + canvas.setFont(Font.boldSystemFont(fontSize)); + canvas.drawText(txt, txtPoint); + } + + sinDeg(deg) { + return Math.sin((deg * Math.PI) / 180); + } + + cosDeg(deg) { + return Math.cos((deg * Math.PI) / 180); + } + + setCircleText = (stack, data) => { + const stackCircle = stack.addStack(); + const canvas = this.makeCanvas(); + stackCircle.size = new Size(70, 70); + this.makeCircle( + canvas, + this.dayRadiusOffset, + data.percent * 3.6, + data.circleColor, + ); + + this.drawText(data.percent, canvas, 75, 18); + this.drawPointText(`%`, canvas, new Point(65, 50), 14); + stackCircle.backgroundImage = canvas.getImage(); + + stackCircle.setPadding(20, 0, 0, 0); + stackCircle.addSpacer(); + const icon = data.urlIcon + ? {image: data.icon} + : SFSymbol.named(data.icon); + const imageIcon = stackCircle.addImage(icon.image); + imageIcon.tintColor = this.iconColor; + imageIcon.imageSize = new Size(15, 15); + // canvas.drawImageInRect(icon.image, new Rect(110, 80, 60, 60)); + stackCircle.addSpacer(); + + stack.addSpacer(5); + const stackDesc = stack.addStack(); + stackDesc.size = new Size(70, 60); + stackDesc.centerAlignContent(); + stackDesc.layoutVertically(); + stackDesc.addSpacer(10); + const textLabel = this.textFormat.defaultText; + textLabel.size = 12; + textLabel.color = this.textColor2; + this.provideText(data.label, stackDesc, textLabel); + stackDesc.addSpacer(10); + + const stackDescFooter = stackDesc.addStack(); + stackDescFooter.centerAlignContent(); + const textCount = this.textFormat.title; + textCount.size = 16; + textCount.color = this.textColor1; + this.provideText(`${data.count}`, stackDescFooter, textCount); + stackDescFooter.addSpacer(2); + this.provideText(data.unit, stackDescFooter, textLabel); + }; + + renderSmall = async (w) => { + w.setPadding(5, 5, 5, 5); + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackTop = stackBody.addStack(); + this.setCircleText(stackTop, this.phoneBill); + const stackBottom = stackBody.addStack(); + this.setCircleText(stackBottom, this.flow); + + const stackFooter = stackBody.addStack(); + stackFooter.addSpacer(); + const text = this.textFormat.defaultText; + text.color = new Color('#aaa'); + this.provideText( + `联通更新:${this.arrUpdateTime[2]}:${this.arrUpdateTime[3]}`, + stackFooter, + text, + ); + stackFooter.addSpacer(); + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackTop = stackBody.addStack(); + this.setCircleText(stackTop, this.phoneBill); + this.setCircleText(stackTop, this.flow); + const stackBottom = stackBody.addStack(); + this.setCircleText(stackBottom, this.voice); + this.updateTime.icon = await this.$request.get( + this.updateTime.urlIcon, + 'IMG', + ); + this.setCircleText(stackBottom, this.updateTime); + return w; + }; + + renderLarge = async (w) => { + return await this.renderMedium(w); + }; + + Run() { + if (config.runsInApp) { + this.registerAction('费用进度', async () => { + await this.setAlertInput(`${this.name}`, '预计当月费用使用值', { + maxFee: '默认 100 元', + }); + }); + const widgetInitConfig = { + loginheader: 'chavy_tokenheader_10010', + }; + this.registerAction('颜色配置', async () => { + await this.setAlertInput( + `${this.name}颜色配置`, + '进度条颜色|底圈颜色\n图标颜色|比值颜色|值颜色', + { + step1: '进度颜色 1', + step2: '进度颜色 2', + step3: '进度颜色 3', + step4: '进度颜色 4', + inner: '底圈颜色', + icon: '图标颜色', + percent: '比值颜色', + value: '值颜色', + }, + ); + }); + this.registerAction('账号设置', async () => { + await this.setAlertInput( + `${this.name}账号`, + '读取 BoxJS 缓存信息', + widgetInitConfig, + ); + }); + this.registerAction('代理缓存', async () => { + await this.setCacheBoxJSData(widgetInitConfig); + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + + try { + const { + loginheader, + step1, + step2, + step3, + step4, + inner, + icon, + percent, + value, + maxFee, + } = this.settings; + this.fgCircleColor = inner ? new Color(inner) : this.fgCircleColor; + this.textColor1 = value ? new Color(value) : this.textColor1; + + this.phoneBill.circleColor = step1 ? new Color(step1) : this.circleColor1; + this.flow.circleColor = step2 ? new Color(step2) : this.circleColor2; + this.voice.circleColor = step3 ? new Color(step3) : this.circleColor3; + this.updateTime.circleColor = step4 + ? new Color(step4) + : this.circleColor4; + + this.iconColor = icon ? new Color(icon) : this.iconColor; + this.percentColor = percent ? new Color(percent) : this.percentColor; + this.loginheader = loginheader ? JSON.parse(loginheader) : {}; + this.maxFee = parseFloat(maxFee) || this.maxFee; + } catch (e) { + console.log(e); + } + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/Contact.js b/Scripts/Contact.js new file mode 100644 index 0000000..9237e34 --- /dev/null +++ b/Scripts/Contact.js @@ -0,0 +1,243 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-green; icon-glyph: mobile-alt; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '桌面联系人'; + this.en = 'ContactTable'; + this.userName = arg || 'Ya'; + this.Run(); + } + + today = ''; + useBoxJS = false; + dataSource = {}; + phoneNumber = {}; + size1 = new Size(15, 15); + size2 = new Size(30, 30); + + init = async () => { + try { + const cardAll = await ContactsContainer.all(); + const data = await Contact.all(cardAll); + if (!this.userName) { + this.dataSource = data[0]; + } else { + this.dataSource = data.find((item) => { + return ( + item.familyName === this.userName || + item.givenName === this.userName || + item.nickname === this.userName || + `${item.familyName}${item.givenName}` === this.userName + ); + }); + } + if (!this.dataSource) + return this.notify(this.name, '未找到通讯录相关联系人,请重新设置'); + this.userName = `${this.dataSource.familyName}${this.dataSource.givenName}`; + const phoneNumbers = this.dataSource.phoneNumbers; + if (phoneNumbers.length) { + this.phoneNumber = phoneNumbers[0]; + this.phoneNumber.value = this.phoneNumber.value.replaceAll(' ', ''); + } + } catch (e) { + console.log(e); + } + }; + + setAvatar = (w) => { + const stackBody = w.addStack(); + const stackLeft = stackBody.addStack(); + stackLeft.setPadding(10, 10, 10, 0); + stackLeft.layoutVertically(); + stackLeft.addSpacer(); + const stackAvatar = stackLeft.addStack(); + stackAvatar.centerAlignContent(); + stackAvatar.size = new Size(80, 80); + stackAvatar.borderWidth = 7; + stackAvatar.borderColor = new Color('#222', 0.7); + stackAvatar.cornerRadius = 40; + if (this.dataSource.image) { + const imgAvatar = stackAvatar.addImage(this.dataSource.image); + imgAvatar.imageSize = new Size(80, 80); + } else { + let textFormat = this.textFormat.title; + textFormat.color = this.widgetColor; + textFormat.size = 42; + this.provideText( + this.userName.substr(0, 1) || '', + stackAvatar, + textFormat + ); + } + stackLeft.addSpacer(); + stackBody.addSpacer(5); + return stackBody; + }; + + setContentCenter = (stackBody) => { + const stackCenter = stackBody.addStack(); + stackCenter.setPadding(10, 0, 10, 10); + stackCenter.layoutVertically(); + stackCenter.addSpacer(); + const stackUsername = stackCenter.addStack(); + stackUsername.centerAlignContent(); + stackCenter.addSpacer(15); + const stackPhoneNumber = stackCenter.addStack(); + stackPhoneNumber.centerAlignContent(); + stackCenter.addSpacer(15); + const stackNote = stackCenter.addStack(); + stackNote.centerAlignContent(); + stackCenter.addSpacer(); + + let textFormat = this.textFormat.defaultText; + textFormat.color = this.widgetColor; + textFormat.size = 18; + const phoneNumber = this.phoneNumber.value || ''; + + const iconPerson = SFSymbol.named('person'); + const imgPerson = stackUsername.addImage(iconPerson.image); + imgPerson.tintColor = this.widgetColor; + imgPerson.imageSize = this.size1; + stackUsername.addSpacer(5); + + const iconPhone = SFSymbol.named('iphone'); + const imgIphone = stackPhoneNumber.addImage(iconPhone.image); + imgIphone.tintColor = this.widgetColor; + imgIphone.imageSize = this.size1; + stackPhoneNumber.addSpacer(5); + + const iconNote = SFSymbol.named('envelope'); + const imgNote = stackNote.addImage(iconNote.image); + imgNote.tintColor = this.widgetColor; + imgNote.imageSize = this.size1; + stackNote.addSpacer(5); + + const data = this.dataSource.emailAddresses; + const email = data.length ? data[0] : {}; + this.provideText(this.userName || '', stackUsername, textFormat); + this.provideText(phoneNumber || '', stackPhoneNumber, textFormat); + const mailTextItem = this.provideText( + email.value || '', + stackNote, + textFormat + ); + mailTextItem.lineLimit = 1; + + stackBody.addSpacer(); + return stackBody; + }; + + stepActionRight = (stackBody) => { + const stackRight = stackBody.addStack(); + stackRight.setPadding(10, 20, 10, 20); + stackRight.layoutVertically(); + stackRight.backgroundColor = this.widgetOpacityColor; + + stackRight.addSpacer(); + const stackCallPhone = stackRight.addStack(); + stackRight.addSpacer(); + const stackSendMessage = stackRight.addStack(); + stackRight.addSpacer(); + const stackDetail = stackRight.addStack(); + stackRight.addSpacer(); + + const phone = this.phoneNumber.value || ''; + const data = this.dataSource.emailAddresses; + const email = data.length ? data[0] : {}; + + stackCallPhone.url = `tel:${phone}`; + stackSendMessage.url = `sms:${phone}`; + stackDetail.url = `mailto:${email.value || ''}`; + + const iconVideo = SFSymbol.named('video'); + const imgVideo = stackCallPhone.addImage(iconVideo.image); + imgVideo.tintColor = this.backGroundColor; + imgVideo.imageSize = this.size2; + + const iconMessage = SFSymbol.named('message'); + const imgMessage = stackSendMessage.addImage(iconMessage.image); + imgMessage.tintColor = this.backGroundColor; + imgMessage.imageSize = this.size2; + + const iconEnvelope = SFSymbol.named('envelope.open'); + const imgEnvelope = stackDetail.addImage(iconEnvelope.image); + imgEnvelope.tintColor = this.backGroundColor; + imgEnvelope.imageSize = this.size2; + + return stackBody; + }; + + renderSmall = (w) => { + this.setContentCenter(stackBody); + this.stepActionRight(stackBody); + return w; + }; + + renderLarge = (w) => { + const stackBody = this.setAvatar(w); + this.setContentCenter(stackBody); + this.stepActionRight(stackBody); + return w; + }; + + renderMedium = (w) => { + const stackBody = this.setAvatar(w); + this.setContentCenter(stackBody); + this.stepActionRight(stackBody); + return w; + }; + + Run() { + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'phone', color: '#722ed1' }, + type: 'input', + title: '右侧透明', + desc: '若不需要右侧背景设置透明度 0 即可', + placeholder: '透明度 0~1', + val: 'rightOpacity', + }); + + this.registerAction('基础设置', this.setWidgetConfig); + } + const light = new Color( + this.settings.lightColor, + parseInt(this.settings.rightOpacity || 1) + ); + const dark = new Color( + this.settings.darkColor, + parseInt(this.settings.rightOpacity || 1) + ); + this.widgetOpacityColor = Color.dynamic(light, dark); + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/DmYY.js b/Scripts/DmYY.js index 2fdc05f..04bee30 100644 --- a/Scripts/DmYY.js +++ b/Scripts/DmYY.js @@ -5,787 +5,2234 @@ /* * Author: 2Ya * Github: https://github.com/dompling + * UI 配置升级 感谢 @LSP 大佬提供代码 */ class DmYY { - constructor(arg) { - this.arg = arg; - this._actions = {}; - this.init(); - this.backgroundImage = this.getBackgroundImage(); - this.isNight = Device.isUsingDarkAppearance(); - this.widgetColor = this.isNight ? Color.white() : Color.black(); - if (this.backgroundImage) this.widgetColor = Color.white(); - } - - prefix = "boxjs.net"; - - // 获取 Request 对象 - getRequest = (url = "") => { - return new Request(url); - }; - - // 发起请求 - http = async (options = { headers: {}, url: "" }, type = "JSON") => { - try { - let request; - if (type !== "IMG") { - request = this.getRequest(); - Object.keys(options).forEach((key) => { - request[key] = options[key]; - }); - request.headers = { ...this.defaultHeaders, ...options.headers }; - } else { - request = this.getRequest(options.url); - return await request.loadImage(); - } - if (type === "JSON") { - return await request.loadJSON(); - } - if (type === "STRING") { - return await request.loadString(); - } - return await request.loadJSON(); - } catch (e) { - console.log("error:" + e); - } - }; - - //request 接口请求 - $request = { - get: async (url = "", options = {}, type = "JSON") => { - const params = { url, ...options, method: "GET" }; - let _type = type; - if (typeof options === "string") _type = options; - return await this.http(params, _type); - }, - post: async (url = "", options = {}, type = "JSON") => { - const params = { url, ...options, method: "POST" }; - let _type = type; - if (typeof options === "string") _type = options; - return await this.http(params, _type); - }, - }; - - // 获取 boxJS 缓存 - getCache = async (key) => { - try { - const url = `http://${this.prefix}/query/boxdata`; - const boxdata = await this.$request.get(url); - console.log(boxdata.datas[key]); - if (key) return boxdata.datas[key]; - return boxdata.datas; - } catch (e) { - console.log(e); - return false; - } - }; - - transforJSON = (str) => { - if (typeof str == "string") { - try { - return JSON.parse(str); - } catch (e) { - console.log(e); - return str; - } - } - console.log("It is not a string!"); - }; - - // 选择图片并缓存 - chooseImg = async () => { - const photoLibrary = await Photos.fromLibrary(); - return photoLibrary; - }; - - // 设置 widget 背景图片 - getWidgetBackgroundImage = async (widget) => { - if (this.backgroundImage) { - const opacity = this.isNight - ? Number(this.settings.opacity[0]) - : Number(this.settings.opacity[1]); - const bg = await this.shadowImage(this.backgroundImage, "#000", opacity); - widget.backgroundImage = bg; - return true; - } else { - return false; - } - }; - - setWidgetBackground = async () => { - const alert = new Alert(); - alert.title = "设置背景图"; - alert.message = "清空或设置新的背景图"; - alert.addAction("设置新背景图"); - alert.addAction("设置背景透明"); - alert.addAction("清空背景"); - alert.addCancelAction("取消"); - const actions = [ - async () => { - const backImage = await this.chooseImg(); - await this.setBackgroundImage(backImage, true); - }, - async () => { - try { - const a = new Alert(); - a.title = "设置背景透明"; - a.message = "白天和夜间透明"; - a.addTextField("白天", `${Number(this.settings.opacity[1])}`); - a.addTextField("夜间", `${Number(this.settings.opacity[0])}`); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.settings.opacity[1] = Number(a.textFieldValue(0)); - this.settings.opacity[0] = Number(a.textFieldValue(1)); - // 保存到本地 - this.settings[this.en] = this.JDCookie; - this.saveSettings(); - } catch (e) { - console.log(e); - } - }, - () => { - this.setBackgroundImage(false, true); - }, - ]; - const id = await alert.presentAlert(); - if (id === -1) return; - actions[id] && actions[id].call(this); - }; - - init(widgetFamily = config.widgetFamily) { - // 组件大小:small,medium,large - this.widgetFamily = widgetFamily; - this.SETTING_KEY = this.md5(Script.name()); - // 文件管理器 - // 提示:缓存数据不要用这个操作,这个是操作源码目录的,缓存建议存放在local temp目录中 - this.FILE_MGR = FileManager[ - module.filename.includes("Documents/iCloud~") ? "iCloud" : "local" - ](); - // 本地,用于存储图片等 - this.FILE_MGR_LOCAL = FileManager.local(); - this.BACKGROUND_KEY = this.FILE_MGR_LOCAL.joinPath( - this.FILE_MGR_LOCAL.documentsDirectory(), - `bg_${this.SETTING_KEY}.jpg`, - ); - this.settings = this.getSettings(); - if (this.settings.opacity) { - this.settings.opacity[0] = Number(this.settings.opacity[0]); - this.settings.opacity[1] = Number(this.settings.opacity[1]); - } else { - this.settings.opacity = [0.7, 0.4]; - } - } - - /** - * 注册点击操作菜单 - * @param {string} name 操作函数名 - * @param {func} func 点击后执行的函数 - */ - registerAction(name, func) { - this._actions[name] = func.bind(this); - } - - /** - * base64 编码字符串 - * @param {string} str 要编码的字符串 - */ - base64Encode(str) { - const data = Data.fromString(str); - return data.toBase64String(); - } - - /** - * base64解码数据 返回字符串 - * @param {string} b64 base64编码的数据 - */ - base64Decode(b64) { - const data = Data.fromBase64String(b64); - return data.toRawString(); - } - - /** - * md5 加密字符串 - * @param {string} str 要加密成md5的数据 - */ - md5(str) { - function d(n, t) { - var r = (65535 & n) + (65535 & t); - return (((n >> 16) + (t >> 16) + (r >> 16)) << 16) | (65535 & r); - } - - function f(n, t, r, e, o, u) { - return d(((c = d(d(t, n), d(e, u))) << (f = o)) | (c >>> (32 - f)), r); - var c, f; - } - - function l(n, t, r, e, o, u, c) { - return f((t & r) | (~t & e), n, t, o, u, c); - } - - function v(n, t, r, e, o, u, c) { - return f((t & e) | (r & ~e), n, t, o, u, c); - } - - function g(n, t, r, e, o, u, c) { - return f(t ^ r ^ e, n, t, o, u, c); - } - - function m(n, t, r, e, o, u, c) { - return f(r ^ (t | ~e), n, t, o, u, c); - } - - function i(n, t) { - var r, e, o, u; - (n[t >> 5] |= 128 << t % 32), (n[14 + (((t + 64) >>> 9) << 4)] = t); - for ( - var c = 1732584193, - f = -271733879, - i = -1732584194, - a = 271733878, - h = 0; - h < n.length; - h += 16 - ) - (c = l((r = c), (e = f), (o = i), (u = a), n[h], 7, -680876936)), - (a = l(a, c, f, i, n[h + 1], 12, -389564586)), - (i = l(i, a, c, f, n[h + 2], 17, 606105819)), - (f = l(f, i, a, c, n[h + 3], 22, -1044525330)), - (c = l(c, f, i, a, n[h + 4], 7, -176418897)), - (a = l(a, c, f, i, n[h + 5], 12, 1200080426)), - (i = l(i, a, c, f, n[h + 6], 17, -1473231341)), - (f = l(f, i, a, c, n[h + 7], 22, -45705983)), - (c = l(c, f, i, a, n[h + 8], 7, 1770035416)), - (a = l(a, c, f, i, n[h + 9], 12, -1958414417)), - (i = l(i, a, c, f, n[h + 10], 17, -42063)), - (f = l(f, i, a, c, n[h + 11], 22, -1990404162)), - (c = l(c, f, i, a, n[h + 12], 7, 1804603682)), - (a = l(a, c, f, i, n[h + 13], 12, -40341101)), - (i = l(i, a, c, f, n[h + 14], 17, -1502002290)), - (c = v( - c, - (f = l(f, i, a, c, n[h + 15], 22, 1236535329)), - i, - a, - n[h + 1], - 5, - -165796510, - )), - (a = v(a, c, f, i, n[h + 6], 9, -1069501632)), - (i = v(i, a, c, f, n[h + 11], 14, 643717713)), - (f = v(f, i, a, c, n[h], 20, -373897302)), - (c = v(c, f, i, a, n[h + 5], 5, -701558691)), - (a = v(a, c, f, i, n[h + 10], 9, 38016083)), - (i = v(i, a, c, f, n[h + 15], 14, -660478335)), - (f = v(f, i, a, c, n[h + 4], 20, -405537848)), - (c = v(c, f, i, a, n[h + 9], 5, 568446438)), - (a = v(a, c, f, i, n[h + 14], 9, -1019803690)), - (i = v(i, a, c, f, n[h + 3], 14, -187363961)), - (f = v(f, i, a, c, n[h + 8], 20, 1163531501)), - (c = v(c, f, i, a, n[h + 13], 5, -1444681467)), - (a = v(a, c, f, i, n[h + 2], 9, -51403784)), - (i = v(i, a, c, f, n[h + 7], 14, 1735328473)), - (c = g( - c, - (f = v(f, i, a, c, n[h + 12], 20, -1926607734)), - i, - a, - n[h + 5], - 4, - -378558, - )), - (a = g(a, c, f, i, n[h + 8], 11, -2022574463)), - (i = g(i, a, c, f, n[h + 11], 16, 1839030562)), - (f = g(f, i, a, c, n[h + 14], 23, -35309556)), - (c = g(c, f, i, a, n[h + 1], 4, -1530992060)), - (a = g(a, c, f, i, n[h + 4], 11, 1272893353)), - (i = g(i, a, c, f, n[h + 7], 16, -155497632)), - (f = g(f, i, a, c, n[h + 10], 23, -1094730640)), - (c = g(c, f, i, a, n[h + 13], 4, 681279174)), - (a = g(a, c, f, i, n[h], 11, -358537222)), - (i = g(i, a, c, f, n[h + 3], 16, -722521979)), - (f = g(f, i, a, c, n[h + 6], 23, 76029189)), - (c = g(c, f, i, a, n[h + 9], 4, -640364487)), - (a = g(a, c, f, i, n[h + 12], 11, -421815835)), - (i = g(i, a, c, f, n[h + 15], 16, 530742520)), - (c = m( - c, - (f = g(f, i, a, c, n[h + 2], 23, -995338651)), - i, - a, - n[h], - 6, - -198630844, - )), - (a = m(a, c, f, i, n[h + 7], 10, 1126891415)), - (i = m(i, a, c, f, n[h + 14], 15, -1416354905)), - (f = m(f, i, a, c, n[h + 5], 21, -57434055)), - (c = m(c, f, i, a, n[h + 12], 6, 1700485571)), - (a = m(a, c, f, i, n[h + 3], 10, -1894986606)), - (i = m(i, a, c, f, n[h + 10], 15, -1051523)), - (f = m(f, i, a, c, n[h + 1], 21, -2054922799)), - (c = m(c, f, i, a, n[h + 8], 6, 1873313359)), - (a = m(a, c, f, i, n[h + 15], 10, -30611744)), - (i = m(i, a, c, f, n[h + 6], 15, -1560198380)), - (f = m(f, i, a, c, n[h + 13], 21, 1309151649)), - (c = m(c, f, i, a, n[h + 4], 6, -145523070)), - (a = m(a, c, f, i, n[h + 11], 10, -1120210379)), - (i = m(i, a, c, f, n[h + 2], 15, 718787259)), - (f = m(f, i, a, c, n[h + 9], 21, -343485551)), - (c = d(c, r)), - (f = d(f, e)), - (i = d(i, o)), - (a = d(a, u)); - return [c, f, i, a]; - } - - function a(n) { - for (var t = "", r = 32 * n.length, e = 0; e < r; e += 8) - t += String.fromCharCode((n[e >> 5] >>> e % 32) & 255); - return t; - } - - function h(n) { - var t = []; - for (t[(n.length >> 2) - 1] = void 0, e = 0; e < t.length; e += 1) - t[e] = 0; - for (var r = 8 * n.length, e = 0; e < r; e += 8) - t[e >> 5] |= (255 & n.charCodeAt(e / 8)) << e % 32; - return t; - } - - function e(n) { - for (var t, r = "0123456789abcdef", e = "", o = 0; o < n.length; o += 1) - (t = n.charCodeAt(o)), - (e += r.charAt((t >>> 4) & 15) + r.charAt(15 & t)); - return e; - } - - function r(n) { - return unescape(encodeURIComponent(n)); - } - - function o(n) { - return a(i(h((t = r(n))), 8 * t.length)); - var t; - } - - function u(n, t) { - return (function (n, t) { - var r, - e, - o = h(n), - u = [], - c = []; - for ( - u[15] = c[15] = void 0, - 16 < o.length && (o = i(o, 8 * n.length)), - r = 0; - r < 16; - r += 1 - ) - (u[r] = 909522486 ^ o[r]), (c[r] = 1549556828 ^ o[r]); - return ( - (e = i(u.concat(h(t)), 512 + 8 * t.length)), a(i(c.concat(e), 640)) - ); - })(r(n), r(t)); - } - - function t(n, t, r) { - return t ? (r ? u(t, n) : e(u(t, n))) : r ? o(n) : e(o(n)); - } - - return t(str); - } - - /** - * 渲染标题内容 - * @param {object} widget 组件对象 - * @param {string} icon 图标地址 - * @param {string} title 标题内容 - * @param {bool|color} color 字体的颜色(自定义背景时使用,默认系统) - */ - async renderHeader(widget, icon, title, color = false) { - let header = widget.addStack(); - header.centerAlignContent(); - const image = await this.$request.get(icon, "IMG"); - let _icon = header.addImage(image); - _icon.imageSize = new Size(14, 14); - _icon.cornerRadius = 4; - header.addSpacer(10); - let _title = header.addText(title); - if (color) _title.textColor = color; - _title.textOpacity = 0.7; - _title.font = Font.boldSystemFont(12); - _title.lineLimit = 1; - widget.addSpacer(15); - return widget; - } - - /** - * 弹出一个通知 - * @param {string} title 通知标题 - * @param {string} body 通知内容 - * @param {string} url 点击后打开的URL - */ - async notify(title, body, url, opts = {}) { - let n = new Notification(); - n = Object.assign(n, opts); - n.title = title; - n.body = body; - if (url) n.openURL = url; - return await n.schedule(); - } - - /** - * 给图片加一层半透明遮罩 - * @param {Image} img 要处理的图片 - * @param {string} color 遮罩背景颜色 - * @param {float} opacity 透明度 - */ - async shadowImage(img, color = "#000000", opacity = 0.7) { - let ctx = new DrawContext(); - // 获取图片的尺寸 - ctx.size = img.size; - - ctx.drawImageInRect( - img, - new Rect(0, 0, img.size["width"], img.size["height"]), - ); - ctx.setFillColor(new Color(color, opacity)); - ctx.fillRect(new Rect(0, 0, img.size["width"], img.size["height"])); - - let res = await ctx.getImage(); - return res; - } - - /** - * 获取当前插件的设置 - * @param {boolean} json 是否为json格式 - */ - getSettings(json = true) { - let res = json ? {} : ""; - let cache = ""; - if (Keychain.contains(this.SETTING_KEY)) { - cache = Keychain.get(this.SETTING_KEY); - } - if (json) { - try { - res = JSON.parse(cache); - } catch (e) { - } - } else { - res = cache; - } - - return res; - } - - /** - * 存储当前设置 - * @param {bool} notify 是否通知提示 - */ - saveSettings(notify = true) { - let res = - typeof this.settings === "object" - ? JSON.stringify(this.settings) - : String(this.settings); - Keychain.set(this.SETTING_KEY, res); - if (notify) this.notify("设置成功", "桌面组件稍后将自动刷新"); - } - - /** - * 获取当前插件是否有自定义背景图片 - * @reutrn img | false - */ - getBackgroundImage() { - let result = null; - if (this.FILE_MGR_LOCAL.fileExists(this.BACKGROUND_KEY)) { - result = Image.fromFile(this.BACKGROUND_KEY); - } - return result; - } - - /** - * 设置当前组件的背景图片 - * @param {image} img - */ - setBackgroundImage(img, notify = true) { - if (!img) { - // 移除背景 - if (this.FILE_MGR_LOCAL.fileExists(this.BACKGROUND_KEY)) { - this.FILE_MGR_LOCAL.remove(this.BACKGROUND_KEY); - } - if (notify) this.notify("移除成功", "小组件背景图片已移除,稍后刷新生效"); - } else { - // 设置背景 - // 全部设置一遍, - this.FILE_MGR_LOCAL.writeImage(this.BACKGROUND_KEY, img); - if (notify) this.notify("设置成功", "小组件背景图片已设置!稍后刷新生效"); - } - } - - randomNum(minNum, maxNum) { - switch (arguments.length) { - case 1: - return parseInt(Math.random() * minNum + 1, 10); - break; - case 2: - return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10); - break; - default: - return 0; - break; - } - } - - getRandomArrayElements(arr, count) { - let shuffled = arr.slice(0), i = arr.length, min = i - count, temp, index; - min = min > 0 ? min : 0; - while (i-- > min) { - index = Math.floor((i + 1) * Math.random()); - temp = shuffled[index]; - shuffled[index] = shuffled[i]; - shuffled[i] = temp; - } - return shuffled.slice(min); - } + constructor(arg, defaultSettings) { + this.arg = arg; + this.defaultSettings = defaultSettings || {}; + this.SETTING_KEY = this.md5(Script.name()); + this._init(); + this.isNight = Device.isUsingDarkAppearance(); + } + + BaseCacheKey = 'DmYY'; + _actions = []; + _menuActions = []; + widgetColor; + backGroundColor; + isNight; + + userConfigKey = ['avatar', 'nickname', 'homePageDesc']; + + // 获取 Request 对象 + getRequest = (url = '') => { + return new Request(url); + }; + + // 发起请求 + http = async ( + options = { headers: {}, url: '' }, + type = 'JSON', + onError = () => { + return SFSymbol.named('photo').image; + } + ) => { + let request; + try { + if (type === 'IMG') { + const fileName = `${this.cacheImage}/${this.md5(options.url)}`; + request = this.getRequest(options.url); + let response; + if (await this.FILE_MGR.fileExistsExtra(fileName)) { + request.loadImage().then((res) => { + this.FILE_MGR.writeImage(fileName, res); + }); + return Image.fromFile(fileName); + } else { + response = await request.loadImage(); + this.FILE_MGR.writeImage(fileName, response); + } + return response; + } + request = this.getRequest(); + Object.keys(options).forEach((key) => { + request[key] = options[key]; + }); + request.headers = { ...this.defaultHeaders, ...options.headers }; + + if (type === 'JSON') { + return await request.loadJSON(); + } + if (type === 'STRING') { + return await request.loadString(); + } + return await request.loadJSON(); + } catch (e) { + console.log('error:' + e); + if (type === 'IMG') return onError?.(); + } + }; + + //request 接口请求 + $request = { + get: (url = '', options = {}, type = 'JSON') => { + let params = { ...options, method: 'GET' }; + if (typeof url === 'object') { + params = { ...params, ...url }; + } else { + params.url = url; + } + let _type = type; + if (typeof options === 'string') _type = options; + return this.http(params, _type); + }, + post: (url = '', options = {}, type = 'JSON') => { + let params = { ...options, method: 'POST' }; + if (typeof url === 'object') { + params = { ...params, ...url }; + } else { + params.url = url; + } + let _type = type; + if (typeof options === 'string') _type = options; + return this.http(params, _type); + }, + }; + + // 获取 boxJS 缓存 + getCache = async (key = '', notify = true) => { + try { + let url = 'http://' + this.prefix + '/query/boxdata'; + if (key) url = 'http://' + this.prefix + '/query/data/' + key; + const boxdata = await this.$request.get( + url, + key ? { timeoutInterval: 1 } : {} + ); + if (key) { + this.settings.BoxJSData = { + ...this.settings.BoxJSData, + [key]: boxdata.val, + }; + this.saveSettings(false); + } + if (boxdata.val) return boxdata.val; + + return boxdata.datas; + } catch (e) { + if (key && this.settings.BoxJSData[key]) { + return this.settings.BoxJSData[key]; + } + if (notify) + await this.notify( + `${this.name} - BoxJS 数据读取失败`, + '请检查 BoxJS 域名是否为代理复写的域名,如(boxjs.net 或 boxjs.com)。\n若没有配置 BoxJS 相关模块,请点击通知查看教程', + 'https://chavyleung.gitbook.io/boxjs/awesome/videos' + ); + return false; + } + }; + + transforJSON = (str) => { + if (typeof str == 'string') { + try { + return JSON.parse(str); + } catch (e) { + console.log(e); + return str; + } + } + console.log('It is not a string!'); + }; + + // 选择图片并缓存 + chooseImg = async (verify = false) => { + const response = await Photos.fromLibrary().catch((err) => { + console.log('图片选择异常:' + err); + }); + if (verify) { + const bool = await this.verifyImage(response); + if (bool) return response; + return null; + } + return response; + }; + + // 设置 widget 背景图片 + getWidgetBackgroundImage = async (widget) => { + const backgroundImage = await this.getBackgroundImage(); + if (backgroundImage) { + const opacity = Device.isUsingDarkAppearance() + ? Number(this.settings.darkOpacity) + : Number(this.settings.lightOpacity); + widget.backgroundImage = await this.shadowImage( + backgroundImage, + '#000', + opacity + ); + return true; + } else { + if (this.backGroundColor.colors) { + widget.backgroundGradient = this.backGroundColor; + } else { + widget.backgroundColor = this.backGroundColor; + } + return false; + } + }; + + /** + * 验证图片尺寸: 图片像素超过 1000 左右的时候会导致背景无法加载 + * @param img Image + */ + verifyImage = async (img = {}) => { + const { width, height } = img.size; + const direct = true; + if (width > 1000) { + const options = ['取消', '打开图像处理']; + const message = + '您的图片像素为' + + width + + ' x ' + + height + + '\n' + + '请将图片' + + (direct ? '宽度' : '高度') + + '调整到 1000 以下\n' + + (!direct ? '宽度' : '高度') + + '自动适应'; + const index = await this.generateAlert(message, options); + if (index === 1) + Safari.openInApp('https://www.sojson.com/image/change.html', false); + return false; + } + return true; + }; + + /** + * 获取截图中的组件剪裁图 + * 可用作透明背景 + * 返回图片image对象 + * 代码改自:https://gist.github.com/mzeryck/3a97ccd1e059b3afa3c6666d27a496c9 + * @param {string} title 开始处理前提示用户截图的信息,可选(适合用在组件自定义透明背景时提示) + */ + async getWidgetScreenShot(title = null) { + // Crop an image into the specified rect. + function cropImage(img, rect) { + let draw = new DrawContext(); + draw.size = new Size(rect.width, rect.height); + + draw.drawImageAtPoint(img, new Point(-rect.x, -rect.y)); + return draw.getImage(); + } + + function phoneSizes(inputHeight) { + return { + /* + + Supported devices + ================= + The following device measurements have been confirmed in iOS 18. + + */ + + // 16 Pro Max + 2868: { + text: { + small: 510, + medium: 1092, + large: 1146, + left: 114, + right: 696, + top: 276, + middle: 912, + bottom: 1548, + }, + notext: { + small: 530, + medium: 1138, + large: 1136, + left: 91, + right: 699, + top: 276, + middle: 882, + bottom: 1488, + }, + }, + + // 16 Plus, 15 Plus, 15 Pro Max, 14 Pro Max + 2796: { + text: { + small: 510, + medium: 1092, + large: 1146, + left: 98, + right: 681, + top: 252, + middle: 888, + bottom: 1524, + }, + notext: { + small: 530, + medium: 1139, + large: 1136, + left: 75, + right: 684, + top: 252, + middle: 858, + bottom: 1464, + }, + }, + + // 16 Pro + 2622: { + text: { + small: 486, + medium: 1032, + large: 1098, + left: 87, + right: 633, + top: 261, + middle: 872, + bottom: 1485, + }, + notext: { + small: 495, + medium: 1037, + large: 1035, + left: 84, + right: 626, + top: 270, + middle: 810, + bottom: 1350, + }, + }, + + // 16, 15, 15 Pro, 14 Pro + 2556: { + text: { + small: 474, + medium: 1017, + large: 1062, + left: 81, + right: 624, + top: 240, + middle: 828, + bottom: 1416, + }, + notext: { + small: 495, + medium: 1047, + large: 1047, + left: 66, + right: 618, + top: 243, + middle: 795, + bottom: 1347, + }, + }, + + // SE3, SE2 + 1334: { + text: { + small: 296, + medium: 642, + large: 648, + left: 54, + right: 400, + top: 60, + middle: 412, + bottom: 764, + }, + notext: { + small: 309, + medium: 667, + large: 667, + left: 41, + right: 399, + top: 67, + middle: 425, + bottom: 783, + }, + }, + + /* + + In-limbo devices + ================= + The following device measurements were confirmed in older versions of iOS. + Please comment if you can confirm these for iOS 18. + + */ + + // 14 Plus, 13 Pro Max, 12 Pro Max + 2778: { + small: 510, + medium: 1092, + large: 1146, + left: 96, + right: 678, + top: 246, + middle: 882, + bottom: 1518, + }, + + // 11 Pro Max, XS Max + 2688: { + small: 507, + medium: 1080, + large: 1137, + left: 81, + right: 654, + top: 228, + middle: 858, + bottom: 1488, + }, + + // 14, 13, 13 Pro, 12, 12 Pro + 2532: { + small: 474, + medium: 1014, + large: 1062, + left: 78, + right: 618, + top: 231, + middle: 819, + bottom: 1407, + }, + + // 13 mini, 12 mini / 11 Pro, XS, X + 2436: { + x: { + small: 465, + medium: 987, + large: 1035, + left: 69, + right: 591, + top: 213, + middle: 783, + bottom: 1353, + }, + mini: { + small: 465, + medium: 987, + large: 1035, + left: 69, + right: 591, + top: 231, + middle: 801, + bottom: 1371, + }, + }, + + // 11, XR + 1792: { + small: 338, + medium: 720, + large: 758, + left: 55, + right: 437, + top: 159, + middle: 579, + bottom: 999, + }, + + // 11 and XR in Display Zoom mode + 1624: { + small: 310, + medium: 658, + large: 690, + left: 46, + right: 394, + top: 142, + middle: 522, + bottom: 902, + }, + + /* + + Older devices + ================= + The following devices cannot be updated to iOS 18 or later. + + */ + + // Home button Plus phones + 2208: { + small: 471, + medium: 1044, + large: 1071, + left: 99, + right: 672, + top: 114, + middle: 696, + bottom: 1278, + }, + + // Home button Plus in Display Zoom mode + 2001: { + small: 444, + medium: 963, + large: 972, + left: 81, + right: 600, + top: 90, + middle: 618, + bottom: 1146, + }, + + // SE1 + 1136: { + small: 282, + medium: 584, + large: 622, + left: 30, + right: 332, + top: 59, + middle: 399, + bottom: 399, + }, + }[inputHeight]; + } + + let message = + title || '开始之前,请先前往桌面,截取空白界面的截图。然后回来继续'; + let exitOptions = ['我已截图', '前去截图 >']; + let shouldExit = await this.generateAlert(message, exitOptions); + if (shouldExit) return; + + // Get screenshot and determine phone size. + let img = await Photos.fromLibrary(); + let height = img.size.height; + let phone = phoneSizes(height); + if (!phone) { + message = '好像您选择的照片不是正确的截图,请先前往桌面'; + await this.generateAlert(message, ['我已知晓']); + return; + } + // Extra setup needed for 2436-sized phones. + if (height === 2436) { + const files = this.FILE_MGR_LOCAL; + let cacheName = 'mz-phone-type'; + let cachePath = files.joinPath(files.libraryDirectory(), cacheName); + + // If we already cached the phone size, load it. + if (files.fileExists(cachePath)) { + let typeString = files.readString(cachePath); + phone = phone[typeString]; + // Otherwise, prompt the user. + } else { + message = '您的📱型号是?'; + let types = ['iPhone 12 mini', 'iPhone 11 Pro, XS, or X']; + let typeIndex = await this.generateAlert(message, types); + let type = typeIndex === 0 ? 'mini' : 'x'; + phone = phone[type]; + files.writeString(cachePath, type); + } + } + + // If supported, check whether home screen has text labels or not. + if (phone.text) { + message = '主屏幕是否有文本标签?'; + const textOptions = ['有', '无']; + const _textOptions = ['text', 'notext']; + const textResponse = await this.generateAlert(message, textOptions); + phone = phone[_textOptions[textResponse]]; + } + + // Prompt for widget size and position. + message = '截图中要设置透明背景组件的尺寸类型是?'; + let sizes = ['小尺寸', '中尺寸', '大尺寸']; + let size = await this.generateAlert(message, sizes); + let widgetSize = sizes[size]; + + message = '要设置透明背景的小组件在哪个位置?'; + message += + height === 1136 + ? ' (备注:当前设备只支持两行小组件,所以下边选项中的「中间」和「底部」的选项是一致的)' + : ''; + + // Determine image crop based on phone size. + let crop = { w: '', h: '', x: '', y: '' }; + if (widgetSize === '小尺寸') { + crop.w = phone.small; + crop.h = phone.small; + let positions = [ + '左上角', + '右上角', + '中间左', + '中间右', + '左下角', + '右下角', + ]; + let _posotions = [ + 'Top left', + 'Top right', + 'Middle left', + 'Middle right', + 'Bottom left', + 'Bottom right', + ]; + let position = await this.generateAlert(message, positions); + + // Convert the two words into two keys for the phone size dictionary. + let keys = _posotions[position].toLowerCase().split(' '); + crop.y = phone[keys[0]]; + crop.x = phone[keys[1]]; + } else if (widgetSize === '中尺寸') { + crop.w = phone.medium; + crop.h = phone.small; + + // Medium and large widgets have a fixed x-value. + crop.x = phone.left; + let positions = ['顶部', '中间', '底部']; + let _positions = ['Top', 'Middle', 'Bottom']; + let position = await this.generateAlert(message, positions); + let key = _positions[position].toLowerCase(); + crop.y = phone[key]; + } else if (widgetSize === '大尺寸') { + crop.w = phone.medium; + crop.h = phone.large; + crop.x = phone.left; + let positions = ['顶部', '底部']; + let position = await this.generateAlert(message, positions); + + // Large widgets at the bottom have the "middle" y-value. + crop.y = position ? phone.middle : phone.top; + } + + // Crop image and finalize the widget. + return cropImage(img, new Rect(crop.x, crop.y, crop.w, crop.h)); + } + + setLightAndDark = async (title, desc, val, placeholder = '') => { + try { + const a = new Alert(); + a.title = title; + a.message = desc; + a.addTextField(placeholder, `${this.settings[val] || ''}`); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return false; + this.settings[val] = a.textFieldValue(0) || ''; + this.saveSettings(); + return true; + } catch (e) { + console.log(e); + } + }; + + /** + * 弹出输入框 + * @param title 标题 + * @param desc 描述 + * @param opt 属性 + * @returns {Promise} + */ + setAlertInput = async (title, desc, opt = {}, isSave = true) => { + const a = new Alert(); + a.title = title; + a.message = !desc ? '' : desc; + Object.keys(opt).forEach((key) => { + a.addTextField(opt[key], this.settings[key]); + }); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return; + const data = {}; + Object.keys(opt).forEach((key, index) => { + data[key] = a.textFieldValue(index) || ''; + }); + // 保存到本地 + if (isSave) { + this.settings = { ...this.settings, ...data }; + return this.saveSettings(); + } + return data; + }; + + setBaseAlertInput = async (title, desc, opt = {}, isSave = true) => { + const a = new Alert(); + a.title = title; + a.message = !desc ? '' : desc; + Object.keys(opt).forEach((key) => { + a.addTextField(opt[key], this.baseSettings[key] || ''); + }); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return; + const data = {}; + Object.keys(opt).forEach((key, index) => { + data[key] = a.textFieldValue(index) || ''; + }); + // 保存到本地 + if (isSave) return this.saveBaseSettings(data); + return data; + }; + + /** + * 设置当前项目的 boxJS 缓存 + * @param opt key value + * @returns {Promise} + */ + setCacheBoxJSData = async (opt = {}) => { + const options = ['取消', '确定']; + const message = '代理缓存仅支持 BoxJS 相关的代理!'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + try { + const boxJSData = await this.getCache(); + Object.keys(opt).forEach((key) => { + this.settings[key] = boxJSData[opt[key]] || ''; + }); + // 保存到本地 + this.saveSettings(); + } catch (e) { + console.log(e); + this.notify( + this.name, + 'BoxJS 缓存读取失败!点击查看相关教程', + 'https://chavyleung.gitbook.io/boxjs/awesome/videos' + ); + } + }; + + /** + * 设置组件内容 + * @returns {Promise} + */ + setWidgetConfig = async () => { + const basic = [ + { + icon: { name: 'arrow.clockwise', color: '#1890ff' }, + type: 'input', + title: '刷新时间', + desc: '刷新时间仅供参考,具体刷新时间由系统判断,单位:分钟', + val: 'refreshAfterDate', + }, + { + icon: { name: 'sun.max.fill', color: '#d48806' }, + type: 'color', + title: '白天字体颜色', + desc: '请自行去网站上搜寻颜色(Hex 颜色)', + val: 'lightColor', + }, + { + icon: { name: 'moon.stars.fill', color: '#d4b106' }, + type: 'color', + title: '晚上字体颜色', + desc: '请自行去网站上搜寻颜色(Hex 颜色)', + val: 'darkColor', + }, + ]; + + return this.renderAppView([ + { title: '基础设置', menu: basic }, + { + title: '背景设置', + menu: [ + { + icon: { name: 'photo', color: '#13c2c2' }, + type: 'color', + title: '白天背景颜色', + desc: '请自行去网站上搜寻颜色(Hex 颜色)\n支持渐变色,各颜色之间以英文逗号分隔', + val: 'lightBgColor', + }, + { + icon: { name: 'photo.fill', color: '#52c41a' }, + type: 'color', + title: '晚上背景颜色', + desc: '请自行去网站上搜寻颜色(Hex 颜色)\n支持渐变色,各颜色之间以英文逗号分隔', + val: 'darkBgColor', + }, + ], + }, + { + menu: [ + { + icon: { name: 'photo.on.rectangle', color: '#fa8c16' }, + name: 'dayBg', + type: 'img', + title: '日间背景', + val: this.cacheImage, + verify: true, + }, + { + icon: { name: 'photo.fill.on.rectangle.fill', color: '#fa541c' }, + name: 'nightBg', + type: 'img', + title: '夜间背景', + val: this.cacheImage, + verify: true, + }, + { + icon: { name: 'text.below.photo', color: '#faad14' }, + type: 'img', + name: 'transparentBg', + title: '透明背景', + val: this.cacheImage, + onClick: async (item, __, previewWebView) => { + const backImage = await this.getWidgetScreenShot(); + if (!backImage || !(await this.verifyImage(backImage))) return; + const cachePath = `${item.val}/${item.name}`; + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: item.name, + }); + }, + }, + ], + }, + { + menu: [ + { + icon: { name: 'record.circle', color: '#722ed1' }, + type: 'input', + title: '日间蒙层', + desc: '完全透明请设置为0', + val: 'lightOpacity', + }, + { + icon: { name: 'record.circle.fill', color: '#eb2f96' }, + type: 'input', + title: '夜间蒙层', + desc: '完全透明请设置为0', + val: 'darkOpacity', + }, + ], + }, + { + menu: [ + { + icon: { name: 'clear', color: '#f5222d' }, + name: 'removeBackground', + title: '清空背景图片', + val: `${this.cacheImage}/`, + onClick: async (_, __, previewWebView) => { + const ids = ['dayBg', 'nightBg', 'transparentBg']; + const options = [ + '清空日间', + '清空夜间', + '清空透明', + `清空全部`, + '取消', + ]; + const message = '该操作不可逆,会清空背景图片!'; + const index = await this.generateAlert(message, options); + if (index === 4) return; + switch (index) { + case 3: + await this.htmlChangeImage(false, `${_.val}${ids[0]}`, { + previewWebView, + id: ids[0], + }); + await this.htmlChangeImage(false, `${_.val}${ids[1]}`, { + previewWebView, + id: ids[1], + }); + await this.htmlChangeImage(false, `${_.val}${ids[2]}`, { + previewWebView, + id: ids[2], + }); + return; + default: + await this.htmlChangeImage(false, `${_.val}${ids[index]}`, { + previewWebView, + id: ids[index], + }); + break; + } + }, + }, + ], + }, + { + title: '重置组件', + menu: [ + { + icon: { name: 'trash', color: '#D85888' }, + title: '重置', + desc: '重置当前组件配置', + name: 'reset', + val: 'reset', + onClick: () => { + this.settings = {}; + this.saveSettings(); + this.reopenScript(); + }, + }, + ], + }, + ]).catch((e) => { + console.log(e); + }); + }; + + drawTableIcon = async ( + icon = 'square.grid.2x2', + color = '#504ED5', + cornerWidth = 42 + ) => { + let sfi = SFSymbol.named('square.grid.2x2'); + try { + sfi = SFSymbol.named(icon); + sfi.applyFont(Font.mediumSystemFont(30)); + } catch (e) { + console.log(`图标(${icon})异常:` + e); + } + const imgData = Data.fromPNG(sfi.image).toBase64String(); + const html = ` + + + + `; + const js = ` + var canvas = document.createElement("canvas"); + var sourceImg = document.getElementById("sourceImg"); + var silhouetteImg = document.getElementById("silhouetteImg"); + var ctx = canvas.getContext('2d'); + var size = sourceImg.width > sourceImg.height ? sourceImg.width : sourceImg.height; + canvas.width = size; + canvas.height = size; + ctx.drawImage(sourceImg, (canvas.width - sourceImg.width) / 2, (canvas.height - sourceImg.height) / 2); + var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var pix = imgData.data; + //convert the image into a silhouette + for (var i=0, n = pix.length; i < n; i+= 4){ + //set red to 0 + pix[i] = 255; + //set green to 0 + pix[i+1] = 255; + //set blue to 0 + pix[i+2] = 255; + //retain the alpha value + pix[i+3] = pix[i+3]; + } + ctx.putImageData(imgData,0,0); + silhouetteImg.src = canvas.toDataURL(); + output=canvas.toDataURL() + `; + + let wv = new WebView(); + await wv.loadHTML(html); + const base64Image = await wv.evaluateJavaScript(js); + const iconImage = await new Request(base64Image).loadImage(); + const size = new Size(160, 160); + const ctx = new DrawContext(); + ctx.opaque = false; + ctx.respectScreenScale = true; + ctx.size = size; + const path = new Path(); + const rect = new Rect(0, 0, size.width, size.width); + + path.addRoundedRect(rect, cornerWidth, cornerWidth); + path.closeSubpath(); + ctx.setFillColor(new Color(color)); + ctx.addPath(path); + ctx.fillPath(); + const rate = 36; + const iw = size.width - rate; + const x = (size.width - iw) / 2; + ctx.drawImageInRect(iconImage, new Rect(x, x, iw, iw)); + return ctx.getImage(); + }; + + dismissLoading = (webView) => { + webView.evaluateJavaScript( + "window.dispatchEvent(new CustomEvent('JWeb', { detail: { code: 'finishLoading' } }))", + false + ); + }; + + insertTextByElementId = (webView, elementId, text) => { + const scripts = `document.getElementById("${elementId}_val").innerHTML=\`${text}\`;`; + webView.evaluateJavaScript(scripts, false); + }; + + loadSF2B64 = async ( + icon = 'square.grid.2x2', + color = '#56A8D6', + cornerWidth = 42 + ) => { + const sfImg = await this.drawTableIcon(icon, color, cornerWidth); + return `data:image/png;base64,${Data.fromPNG(sfImg).toBase64String()}`; + }; + + setUserInfo = async () => { + const baseOnClick = async (item, _, previewWebView) => { + const data = await this.setBaseAlertInput(item.title, item.desc, { + [item.val]: item.placeholder, + }); + if (!data) return; + this.insertTextByElementId(previewWebView, item.name, data[item.val]); + }; + + return this.renderAppView([ + { + title: '个性设置', + menu: [ + { + icon: { name: 'person', color: '#fa541c' }, + name: this.userConfigKey[0], + title: '首页头像', + type: 'img', + val: this.baseImage, + onClick: async (_, __, previewWebView) => { + const options = ['相册选择', '在线链接', '取消']; + const message = '设置个性化头像'; + const index = await this.generateAlert(message, options); + if (index === 2) return; + const cachePath = `${_.val}/${_.name}`; + switch (index) { + case 0: + const albumOptions = ['选择图片', '清空图片', '取消']; + + const albumIndex = await this.generateAlert('', albumOptions); + if (albumIndex === 2) return; + if (albumIndex === 1) { + await this.htmlChangeImage(false, cachePath, { + previewWebView, + id: _.name, + }); + return; + } + + const backImage = await this.chooseImg(); + if (backImage) { + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: _.name, + }); + } + + break; + case 1: + const data = await this.setBaseAlertInput( + '在线链接', + '首页头像在线链接', + { + avatar: '🔗请输入 URL 图片链接', + } + ); + if (!data) return; + + if (data[_.name] !== '') { + const backImage = await this.$request.get( + data[_.name], + 'IMG' + ); + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: _.name, + }); + } else { + await this.htmlChangeImage(false, cachePath, { + previewWebView, + id: _.name, + }); + } + + break; + default: + break; + } + }, + }, + { + icon: { name: 'pencil', color: '#fa8c16' }, + type: 'input', + title: '首页昵称', + desc: '个性化首页昵称', + placeholder: '👤请输入头像昵称', + val: this.userConfigKey[1], + name: this.userConfigKey[1], + defaultValue: this.baseSettings.nickname, + onClick: baseOnClick, + }, + { + icon: { name: 'lineweight', color: '#a0d911' }, + type: 'input', + title: '首页昵称描述', + desc: '个性化首页昵称描述', + placeholder: '请输入描述', + val: this.userConfigKey[2], + name: this.userConfigKey[2], + defaultValue: this.baseSettings.homePageDesc, + onClick: baseOnClick, + }, + ], + }, + { + menu: [ + { + icon: { name: 'shippingbox', color: '#f7bb10' }, + type: 'input', + title: 'BoxJS 域名', + desc: '设置BoxJS访问域名,如:boxjs.net 或 boxjs.com', + val: 'boxjsDomain', + name: 'boxjsDomain', + placeholder: 'boxjs.net', + defaultValue: this.baseSettings.boxjsDomain, + onClick: baseOnClick, + }, + { + icon: { name: 'clear', color: '#f5222d' }, + title: '恢复默认设置', + name: 'reset', + onClick: async () => { + const options = ['取消', '确定']; + const message = '确定要恢复当前所有配置吗?'; + const index = await this.generateAlert(message, options); + if (index === 1) { + this.settings = {}; + this.baseSettings = {}; + + this.FILE_MGR.remove(this.cacheImage); + + for (const item of this.cacheImageBgPath) { + await this.setBackgroundImage(false, item, false); + } + + this.saveSettings(false); + this.saveBaseSettings(); + await this.notify( + '重置成功', + '请关闭窗口之后,重新运行当前脚本' + ); + this.reopenScript(); + } + }, + }, + ], + }, + ]); + }; + + htmlChangeImage = async (image, path, { previewWebView, id }) => { + const base64Img = await this.setBackgroundImage(image, path, false); + console.log(path); + this.insertTextByElementId( + previewWebView, + id, + base64Img ? `` : '' + ); + }; + + reopenScript = () => { + Safari.open(`scriptable:///run/${encodeURIComponent(Script.name())}`); + }; + + async renderAppView( + options = [], + renderAvatar = false, + previewWebView = new WebView() + ) { + const settingItemFontSize = 14, + authorNameFontSize = 20, + authorDescFontSize = 12; + // ================== 配置界面样式 =================== + const style = ` + :root { + --color-primary: #007aff; + --divider-color: rgba(60,60,67,0.16); + --card-background: #fff; + --card-radius: 8px; + --list-header-color: rgba(60,60,67,0.6); + } + * { + -webkit-user-select: none; + user-select: none; + } + body { + margin: 10px 0; + -webkit-font-smoothing: antialiased; + font-family: "SF Pro Display","SF Pro Icons","Helvetica Neue","Helvetica","Arial",sans-serif; + accent-color: var(--color-primary); + background: #f6f6f6; + } + .list { + margin: 15px; + } + .list__header { + margin: 0 18px; + color: var(--list-header-color); + font-size: 13px; + } + .list__body { + margin-top: 10px; + background: var(--card-background); + border-radius: var(--card-radius); + overflow: hidden; + } + .form-item-auth { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 4em; + padding: 0.5em 18px; + position: relative; + } + .form-item-auth-name { + margin: 0px 12px; + font-size: ${authorNameFontSize}px; + font-weight: 430; + } + .form-item-auth-desc { + margin: 0px 12px; + font-size: ${authorDescFontSize}px; + font-weight: 400; + } + .form-label-author-avatar { + width: 62px; + height: 62px; + border-radius:50%; + border: 1px solid #F6D377; + } + .form-item, .form-item-switch { + display: flex; + align-items: center; + justify-content: space-between; + font-size: ${settingItemFontSize}px; + font-weight: 400; + min-height: 2.2em; + padding: 0.5em 10px; + position: relative; + } + label > * { + pointer-events: none; + } + .form-label { + display: flex; + align-items: center; + flex-wrap:nowrap + } + .form-label-img { + height: 30px; + } + .form-label-title { + margin-left: 8px; + white-space: nowrap; + } + .bottom-bg { + margin: 30px 15px 15px 15px; + } + .form-item--link .icon-arrow-right { + color: #86868b; + } + + .form-item-right-desc { + font-size: 13px; + color: #86868b; + margin: 0 4px 0 auto; + max-width: 130px; + overflow: hidden; + text-overflow: ellipsis; + display:flex; + align-items: center; + white-space: nowrap; + } + + .form-item-right-desc img{ + width:30px; + height:30px; + border-radius:3px; + } + + .form-item + .form-item::before, + .form-item + .form-item-switch::before, + .form-item-switch + .form-item::before, + .form-item-switch + .form-item-switch::before + { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + border-top: 0.5px solid var(--divider-color); + } + + .form-item input[type="checkbox"] { + width: 2em; + height: 2em; + } + input[type='input'],select,input[type='date'] { + width: 100%; + height: 2.3em; + outline-style: none; + text-align: right; + padding: 0px 10px; + border: 1px solid #ddd; + font-size: 14px; + color: #86868b; + border-radius:4px; + } + input[type='checkbox'][role='switch'] { + position: relative; + display: inline-block; + appearance: none; + width: 40px; + height: 24px; + border-radius: 24px; + background: #ccc; + transition: 0.3s ease-in-out; + } + input[type='checkbox'][role='switch']::before { + content: ''; + position: absolute; + left: 2px; + top: 2px; + width: 20px; + height: 20px; + border-radius: 50%; + background: #fff; + transition: 0.3s ease-in-out; + } + input[type='checkbox'][role='switch']:checked { + background: var(--color-primary); + } + input[type='checkbox'][role='switch']:checked::before { + transform: translateX(16px); + } + .copyright { + display: flex; + align-items: center; + justify-content: space-between; + margin: 15px; + font-size: 10px; + color: #86868b; + } + .copyright a { + color: #515154; + text-decoration: none; + } + .preview.loading { + pointer-events: none; + } + .icon-loading { + display: inline-block; + animation: 1s linear infinite spin; + } + .normal-loading { + display: inline-block; + animation: 20s linear infinite spin; + } + @keyframes spin { + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(1turn); + } + } + @media (prefers-color-scheme: dark) { + :root { + --divider-color: rgba(84,84,88,0.65); + --card-background: #1c1c1e; + --list-header-color: rgba(235,235,245,0.6); + } + body { + background: #000; + color: #fff; + } + }`; + + const js = ` + (() => { + + window.invoke = (code, data) => { + window.dispatchEvent( + new CustomEvent( + 'JBridge', + { detail: { code, data } } + ) + ) + } + + // 切换ico的loading效果 + const toggleIcoLoading = (e) => { + try{ + const target = e.currentTarget + target.classList.add('loading') + const icon = e.currentTarget.querySelector('.iconfont') + const className = icon.className + icon.className = 'iconfont icon-loading' + const listener = (event) => { + const { code } = event.detail + if (code === 'finishLoading') { + target.classList.remove('loading') + icon.className = className + window.removeEventListener('JWeb', listener); + } + } + window.addEventListener('JWeb', listener) + }catch(e){ + for (const loading of document.querySelectorAll('.icon-loading')) { + loading.classList.remove('loading'); + loading.className = "iconfont icon-arrow-right"; + } + } + }; + + for (const btn of document.querySelectorAll('.form-item')) { + btn.addEventListener('click', (e) => { + if(!e.target.id)return; + toggleIcoLoading(e); + invoke(e.target.id); + }) + } + + for (const btn of document.querySelectorAll('.form-item__input')) { + btn.addEventListener('change', (e) => { + if(!e.target.name)return; + invoke(e.target.name,e.target.type==="checkbox"?\`\${e.target.checked}\`: e.target.value); + }) + } + + if(${renderAvatar}){ + document.querySelectorAll('.form-item-auth')[0].addEventListener('click', (e) => { + toggleIcoLoading(e); + invoke("userInfo"); + }) + } + + })()`; + + let configList = ``; + let actionsConfig = []; + + for (const key in options) { + const item = options[key]; + actionsConfig = [...item.menu, ...actionsConfig]; + configList += ` +
+
${item.title || ''}
+
+ `; + + for (const menuItem of item.menu) { + let iconBase64 = ``; + if (menuItem.children) { + menuItem.onClick = () => { + return this.renderAppView( + typeof menuItem.children === 'function' + ? menuItem.children() + : menuItem.children + ); + }; + } + if (menuItem.url) { + const imageIcon = await this.http( + { url: menuItem.url }, + 'IMG', + () => { + return this.drawTableIcon('gear'); + } + ); + + if (menuItem.url.indexOf('png') !== -1) { + iconBase64 = `data:image/png;base64,${Data.fromPNG( + imageIcon + ).toBase64String()}`; + } else { + iconBase64 = `data:image/png;base64,${Data.fromJPEG( + imageIcon + ).toBase64String()}`; + } + } else { + const icon = menuItem.icon || {}; + iconBase64 = await this.loadSF2B64(icon.name, icon.color); + } + const idName = menuItem.name || menuItem.val; + + let defaultHtml = ``; + menuItem.defaultValue = + this.settings[idName] || menuItem.defaultValue || ''; + + if (menuItem.type === 'input') { + defaultHtml = menuItem.defaultValue || ''; + } else if (menuItem.type === 'img') { + const cachePath = `${menuItem.val}/${menuItem.name}`; + if (await this.FILE_MGR.fileExistsExtra(cachePath)) { + const imageSrc = `data:image/png;base64,${Data.fromFile( + cachePath + ).toBase64String()}`; + defaultHtml = ``; + } + } else if (menuItem.type === 'select') { + let selectOptions = ''; + menuItem.options.forEach((option) => { + let selected = `selected="selected"`; + selectOptions += ``; + }); + defaultHtml = ``; + } else if (menuItem.type === 'switch') { + const checked = + menuItem.defaultValue == 'true' ? `checked="checked"` : ''; + defaultHtml += ``; + } else if (menuItem.type) { + defaultHtml = ``; + } + + let addLable = ''; + if (menuItem.type === 'switch' || menuItem.type === 'checkbox') { + addLable = `
`; + } + + let avatarHtml = ''; + if (renderAvatar) { + const cachePath = `${this.baseImage}/${this.userConfigKey[0]}`; + const avatarConfig = { + avatar: `https://avatars.githubusercontent.com/u/23498579?v=4`, + nickname: this.baseSettings[this.userConfigKey[1]] || 'Dompling', + homPageDesc: + this.baseSettings[this.userConfigKey[2]] || + '18岁,来自九仙山的设计师', + }; + + if (await this.FILE_MGR.fileExistsExtra(cachePath)) { + avatarConfig.avatar = `data:image/png;base64,${Data.fromFile( + cachePath + ).toBase64String()}`; + } + + avatarHtml = ` +
+
+ +
+
+ `; + } + + const html = ` + + + + + + + + ${avatarHtml} + ${configList} + + + + `; + + // 预览web + await previewWebView.loadHTML(html); + + const injectListener = async () => { + const event = await previewWebView.evaluateJavaScript( + `(() => { + try { + window.addEventListener( + 'JBridge', + (e)=>{ + completion(JSON.stringify(e.detail||{})) + } + ) + } catch (e) { + alert("预览界面出错:" + e); + throw new Error("界面处理出错: " + e); + return; + } + })()`, + true + ); + + const { code, data } = JSON.parse(event); + try { + const actionItem = actionsConfig.find( + (item) => (item.name || item.val) === code + ); + + if (code === 'userInfo') await this.setUserInfo(); + + if (actionItem) { + const idName = actionItem?.name || actionItem?.val; + if (actionItem?.onClick) { + await actionItem?.onClick?.(actionItem, data, previewWebView); + } else if (actionItem.type == 'input') { + if ( + await this.setLightAndDark( + actionItem['title'], + actionItem['desc'], + idName, + actionItem['placeholder'] + ) + ) + this.insertTextByElementId( + previewWebView, + idName, + this.settings[idName] || '' + ); + } else if (actionItem.type === 'img') { + const cachePath = `${actionItem.val}/${actionItem.name}`; + const options = ['相册选择', '清空图片', '取消']; + const message = '相册图片选择,请选择合适图片大小'; + const index = await this.generateAlert(message, options); + switch (index) { + case 0: + const backImage = await this.chooseImg(actionItem.verify); + if (backImage) { + const cachePath = `${actionItem.val}/${actionItem.name}`; + await this.htmlChangeImage(backImage, cachePath, { + previewWebView, + id: idName, + }); + } + break; + case 1: + await this.htmlChangeImage(false, cachePath, { + previewWebView, + id: idName, + }); + break; + default: + break; + } + } else { + if (data !== undefined) { + this.settings[idName] = data; + this.saveSettings(false); + } + } + } + } catch (error) { + console.log('异常操作:' + error); + } + this.dismissLoading(previewWebView); + injectListener(); + }; + + injectListener().catch((e) => { + console.error(e); + this.dismissLoading(previewWebView); + if (!config.runsInApp) { + this.notify('主界面', `🚫 ${e}`); + } + }); + + previewWebView.present(); + } + + initSFSymbol() { + const named = SFSymbol.named; + SFSymbol.named = (str) => { + const current = named(str); + if (!current) { + console.log(`图标异常,请在文中搜索并替换图标:${str}`); + return named('photo'); + } + return current; + }; + return SFSymbol; + } + + _init(widgetFamily = config.widgetFamily) { + this.initSFSymbol(); + // 组件大小:small,medium,large + this.widgetFamily = widgetFamily; + //用于配置所有的组件相关设置 + + // 文件管理器 + // 提示:缓存数据不要用这个操作,这个是操作源码目录的,缓存建议存放在local temp目录中 + this.FILE_MGR = + FileManager[ + module.filename.includes('Documents/iCloud~') ? 'iCloud' : 'local' + ](); + + this.FILE_MGR.fileExistsExtra = async (filePath) => { + const file = this.FILE_MGR.fileExists(filePath); + if (file) await this.FILE_MGR.downloadFileFromiCloud(filePath); + return file; + }; + + this.cacheImage = this.FILE_MGR.joinPath( + this.FILE_MGR.documentsDirectory(), + `/images/${Script.name()}` + ); + + this.baseImage = this.FILE_MGR.joinPath( + this.FILE_MGR.documentsDirectory(), + `/images/` + ); + + this.cacheImageBgPath = [ + `${this.cacheImage}/transparentBg`, + `${this.cacheImage}/dayBg`, + `${this.cacheImage}/nightBg`, + `${this.baseImage}/avatar`, + ]; + + if (!this.FILE_MGR.fileExists(this.cacheImage)) { + this.FILE_MGR.createDirectory(this.cacheImage, true); + } + + // 本地,用于存储图片等 + this.FILE_MGR_LOCAL = FileManager.local(); + + this.settings = this.getSettings(); + + this.baseSettings = this.getBaseSettings(); + + this.settings = { ...this.defaultSettings, ...this.settings }; + + this.settings.lightColor = this.settings.lightColor || '#000000'; + this.settings.darkColor = this.settings.darkColor || '#ffffff'; + this.settings.lightBgColor = this.settings.lightBgColor || '#ffffff'; + this.settings.darkBgColor = this.settings.darkBgColor || '#000000'; + this.settings.boxjsDomain = this.baseSettings.boxjsDomain || 'boxjs.net'; + this.settings.refreshAfterDate = this.settings.refreshAfterDate || '30'; + this.settings.lightOpacity = this.settings.lightOpacity || '0.4'; + this.settings.darkOpacity = this.settings.darkOpacity || '0.7'; + + this.prefix = this.settings.boxjsDomain; + + config.runsInApp && this.saveSettings(false); + + this.backGroundColor = Color.dynamic( + new Color(this.settings.lightBgColor), + new Color(this.settings.darkBgColor) + ); + + // const lightBgColor = this.getColors(this.settings.lightBgColor); + // const darkBgColor = this.getColors(this.settings.darkBgColor); + // if (lightBgColor.length > 1 || darkBgColor.length > 1) { + // this.backGroundColor = !Device.isUsingDarkAppearance() + // ? this.getBackgroundColor(lightBgColor) + // : this.getBackgroundColor(darkBgColor); + // } else if (lightBgColor.length > 0 && darkBgColor.length > 0) { + // this.backGroundColor = Color.dynamic( + // new Color(this.settings.lightBgColor), + // new Color(this.settings.darkBgColor) + // ); + // } + + this.widgetColor = Color.dynamic( + new Color(this.settings.lightColor), + new Color(this.settings.darkColor) + ); + } + + getColors = (color = '') => { + const colors = typeof color === 'string' ? color.split(',') : color; + return colors; + }; + + getBackgroundColor = (colors) => { + const locations = []; + const linearColor = new LinearGradient(); + const cLen = colors.length; + linearColor.colors = colors.map((item, index) => { + locations.push(Math.floor(((index + 1) / cLen) * 100) / 100); + return new Color(item, 1); + }); + linearColor.locations = locations; + return linearColor; + }; + + /** + * 注册点击操作菜单 + * @param {string} name 操作函数名 + * @param {func} func 点击后执行的函数 + */ + registerAction(name, func, icon = { name: 'gear', color: '#096dd9' }, type) { + if (typeof name === 'object' && !name.menu) return this._actions.push(name); + if (typeof name === 'object' && name.menu) + return this._menuActions.push(name); + + const action = { + name, + type, + title: name, + onClick: func?.bind(this), + }; + + if (typeof icon === 'string') { + action.url = icon; + } else { + action.icon = icon; + } + + this._actions.push(action); + } + + /** + * base64 编码字符串 + * @param {string} str 要编码的字符串 + */ + base64Encode(str) { + const data = Data.fromString(str); + return data.toBase64String(); + } + + /** + * base64解码数据 返回字符串 + * @param {string} b64 base64编码的数据 + */ + base64Decode(b64) { + const data = Data.fromBase64String(b64); + return data.toRawString(); + } + + /** + * md5 加密字符串 + * @param {string} str 要加密成md5的数据 + */ + // prettier-ignore + md5(str){function d(n,t){var r=(65535&n)+(65535&t);return(((n>>16)+(t>>16)+(r>>16))<<16)|(65535&r)}function f(n,t,r,e,o,u){return d(((c=d(d(t,n),d(e,u)))<<(f=o))|(c>>>(32-f)),r);var c,f}function l(n,t,r,e,o,u,c){return f((t&r)|(~t&e),n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f((t&e)|(r&~e),n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function i(n,t){var r,e,o,u;(n[t>>5]|=128<>>9)<<4)]=t);for(var c=1732584193,f=-271733879,i=-1732584194,a=271733878,h=0;h>5]>>>e%32)&255);return t}function h(n){var t=[];for(t[(n.length>>2)-1]=void 0,e=0;e>5]|=(255&n.charCodeAt(e/8))<>>4)&15)+r.charAt(15&t));return e}function r(n){return unescape(encodeURIComponent(n))}function o(n){return a(i(h((t=r(n))),8*t.length));var t}function u(n,t){return(function(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,16} + */ + + async generateAlert(message, options) { + let alert = new Alert(); + alert.message = message; + + for (const option of options) { + alert.addAction(option); + } + return await alert.presentAlert(); + } + + /** + * 弹出一个通知 + * @param {string} title 通知标题 + * @param {string} body 通知内容 + * @param {string} url 点击后打开的URL + */ + async notify(title, body, url, opts = {}) { + let n = new Notification(); + n = Object.assign(n, opts); + n.title = title; + n.body = body; + if (url) n.openURL = url; + return await n.schedule(); + } + + /** + * 给图片加一层半透明遮罩 + * @param {Image} img 要处理的图片 + * @param {string} color 遮罩背景颜色 + * @param {float} opacity 透明度 + */ + async shadowImage(img, color = '#000000', opacity = 0.7) { + if (!img) return; + if (opacity === 0) return img; + let ctx = new DrawContext(); + // 获取图片的尺寸 + ctx.size = img.size; + + ctx.drawImageInRect( + img, + new Rect(0, 0, img.size['width'], img.size['height']) + ); + ctx.setFillColor(new Color(color, opacity)); + ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height'])); + return await ctx.getImage(); + } + + /** + * 获取当前插件的设置 + * @param {boolean} json 是否为json格式 + */ + getSettings(json = true) { + let res = json ? {} : ''; + let cache = ''; + if (Keychain.contains(this.SETTING_KEY)) { + cache = Keychain.get(this.SETTING_KEY); + } + + if (json) { + try { + res = JSON.parse(cache); + } catch (e) {} + } else { + res = cache; + } + + return res; + } + + getBaseSettings(json = true) { + let res = json ? {} : ''; + let cache = ''; + if (Keychain.contains(this.BaseCacheKey)) { + cache = Keychain.get(this.BaseCacheKey); + } + + if (json) { + try { + res = JSON.parse(cache); + } catch (e) {} + } else { + res = cache; + } + + return res; + } + + saveBaseSettings(res = {}, notify = true) { + const data = { ...(this.baseSettings || {}), ...res }; + this.baseSettings = data; + Keychain.set(this.BaseCacheKey, JSON.stringify(data)); + if (notify) this.notify('设置成功', '通用设置需重新运行脚本生效'); + return data; + } + + /** + * 存储当前设置 + * @param {bool} notify 是否通知提示 + */ + saveSettings(notify = true) { + let res = + typeof this.settings === 'object' + ? JSON.stringify(this.settings) + : String(this.settings); + Keychain.set(this.SETTING_KEY, res); + + if (notify) this.notify('设置成功', '桌面组件稍后将自动刷新'); + + return res; + } + + /** + * 获取当前插件是否有自定义背景图片 + * @reutrn img | false + */ + async getBackgroundImage() { + if (await this.FILE_MGR.fileExistsExtra(this.cacheImageBgPath[0])) + return Image.fromFile(this.cacheImageBgPath[0]); + + if (!this.isNight) + return (await this.FILE_MGR.fileExistsExtra(this.cacheImageBgPath[1])) + ? Image.fromFile(this.cacheImageBgPath[1]) + : undefined; + else + return (await this.FILE_MGR.fileExistsExtra(this.cacheImageBgPath[2])) + ? Image.fromFile(this.cacheImageBgPath[2]) + : undefined; + } + + /** + * 设置当前组件的背景图片 + * @param {Image} img + */ + async setBackgroundImage(img, filePath = this.baseImage, notify = true) { + const cacheKey = filePath; + if (!img) { + // 移除背景 + if (this.FILE_MGR.fileExists(cacheKey)) this.FILE_MGR.remove(cacheKey); + if (notify) this.notify('移除成功', '背景图片已移除,稍后刷新生效'); + } else { + // 设置背景 + this.FILE_MGR.writeImage(cacheKey, img); + + if (notify) this.notify('设置成功', '背景图片已设置!稍后刷新生效'); + return `data:image/png;base64,${Data.fromFile( + cacheKey + ).toBase64String()}`; + } + } + + getRandomArrayElements(arr, count) { + let shuffled = arr.slice(0), + i = arr.length, + min = i - count, + temp, + index; + min = min > 0 ? min : 0; + while (i-- > min) { + index = Math.floor((i + 1) * Math.random()); + temp = shuffled[index]; + shuffled[index] = shuffled[i]; + shuffled[i] = temp; + } + return shuffled.slice(min); + } + + textFormat = { + defaultText: { size: 14, font: 'regular', color: this.widgetColor }, + battery: { size: 10, font: 'bold', color: this.widgetColor }, + title: { size: 16, font: 'semibold', color: this.widgetColor }, + SFMono: { size: 12, font: 'SF Mono', color: this.widgetColor }, + }; + + provideFont = (fontName, fontSize) => { + const fontGenerator = { + ultralight: function () { + return Font.ultraLightSystemFont(fontSize); + }, + light: function () { + return Font.lightSystemFont(fontSize); + }, + regular: function () { + return Font.regularSystemFont(fontSize); + }, + medium: function () { + return Font.mediumSystemFont(fontSize); + }, + semibold: function () { + return Font.semiboldSystemFont(fontSize); + }, + bold: function () { + return Font.boldSystemFont(fontSize); + }, + heavy: function () { + return Font.heavySystemFont(fontSize); + }, + black: function () { + return Font.blackSystemFont(fontSize); + }, + italic: function () { + return Font.italicSystemFont(fontSize); + }, + }; + + const systemFont = fontGenerator[fontName]; + if (systemFont) { + return systemFont(); + } + return new Font(fontName, fontSize); + }; + + provideText = (string, container, format) => { + format = { + font: 'light', + size: 14, + color: this.widgetColor, + opacity: 1, + minimumScaleFactor: 1, + ...format, + }; + const textItem = container.addText(string); + const textFont = format.font; + const textSize = format.size; + const textColor = format.color; + + textItem.font = this.provideFont(textFont, textSize); + textItem.textColor = textColor; + textItem.textOpacity = format.opacity || 1; + textItem.minimumScaleFactor = format.minimumScaleFactor || 1; + return textItem; + }; } // @base.end -const Runing = async (Widget, default_args = "", isDebug = true, extra) => { - let M = null; - // 判断hash是否和当前设备匹配 - if (config.runsInWidget) { - M = new Widget(args.widgetParameter || ""); - if (extra) { - Object.keys(extra).forEach((key) => { - M[key] = extra[key]; - }); - } - const W = await M.render(); - if (W) { - Script.setWidget(W); - Script.complete(); - } - } else { - let { act, data, __arg, __size } = args.queryParameters; - M = new Widget(__arg || default_args || ""); - if (extra) { - Object.keys(extra).forEach((key) => { - M[key] = extra[key]; - }); - } - if (__size) M.init(__size); - if (!act || !M["_actions"]) { - // 弹出选择菜单 - const actions = M["_actions"]; - const _actions = [ - ...(isDebug - ? [ - // 远程开发 - async () => { - // 1. 获取服务器ip - const a = new Alert(); - a.title = "服务器 IP"; - a.message = "请输入远程开发服务器(电脑)IP地址"; - let xjj_debug_server = "192.168.1.3"; - if (Keychain.contains("xjj_debug_server")) { - xjj_debug_server = Keychain.get("xjj_debug_server"); - } - a.addTextField("server-ip", xjj_debug_server); - a.addAction("连接"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - const ip = a.textFieldValue(0); - // 保存到本地 - Keychain.set("xjj_debug_server", ip); - const server_api = `http://${ip}:5566`; - // 2. 发送当前文件到远程服务器 - const SELF_FILE = module.filename.replace( - "DmYY", - Script.name(), - ); - const req = new Request(`${server_api}/sync`); - req.method = "POST"; - req.addFileToMultipart(SELF_FILE, "Widget", Script.name()); - try { - const res = await req.loadString(); - if (res !== "ok") { - return M.notify("连接失败", res); - } - } catch (e) { - return M.notify("连接错误", e.message); - } - M.notify("连接成功", "编辑文件后保存即可进行下一步预览操作"); - // 重写console.log方法,把数据传递到nodejs - const rconsole_log = async (data, t = "log") => { - const _req = new Request(`${server_api}/console`); - _req.method = "POST"; - _req.headers = { - "Content-Type": "application/json", - }; - _req.body = JSON.stringify({ - t, - data, - }); - return await _req.loadString(); - }; - const lconsole_log = console.log.bind(console); - const lconsole_warn = console.warn.bind(console); - const lconsole_error = console.error.bind(console); - console.log = (d) => { - lconsole_log(d); - rconsole_log(d, "log"); - }; - console.warn = (d) => { - lconsole_warn(d); - rconsole_log(d, "warn"); - }; - console.error = (d) => { - lconsole_error(d); - rconsole_log(d, "error"); - }; - // 3. 同步 - while (1) { - let _res = ""; - try { - const _req = new Request( - `${server_api}/sync?name=${encodeURIComponent( - Script.name(), - )}`, - ); - _res = await _req.loadString(); - } catch (e) { - M.notify("停止调试", "与开发服务器的连接已终止"); - break; - } - if (_res === "stop") { - console.log("[!] 停止同步"); - break; - } else if (_res === "no") { - // console.log("[-] 没有更新内容") - } else if (_res.length > 0) { - M.notify("同步成功", "新文件已同步,大小:" + _res.length); - // 重新加载组件 - // 1. 读取当前源码 - const _code = _res - .split("// @组件代码开始")[1] - .split("// @组件代码结束")[0]; - // 2. 解析 widget class - let NewWidget = null; - try { - const _func = new Function( - `const _Debugger = DmYY => {\n${_code}\nreturn Widget\n}\nreturn _Debugger`, - ); - NewWidget = _func()(DmYY); - } catch (e) { - M.notify("解析失败", e.message); - } - if (!NewWidget) continue; - // 3. 重新执行 widget class - delete M; - M = new NewWidget(__arg || default_args || ""); - if (__size) M.init(__size); - // 写入文件 - FileManager.local().writeString(SELF_FILE, _res); - // 执行预览 - let i = await _actions[1](true); - if (i === 4 + Object.keys(actions).length) break; - } - } - }, - ] - : []), - // 预览组件 - async (debug = false) => { - let a = new Alert(); - a.title = "预览组件"; - a.message = "测试桌面组件在各种尺寸下的显示效果"; - a.addAction("小尺寸 Small"); - a.addAction("中尺寸 Medium"); - a.addAction("大尺寸 Large"); - a.addAction("全部 All"); - a.addCancelAction("取消操作"); - const funcs = []; - if (debug) { - for (let _ in actions) { - a.addAction(_); - funcs.push(actions[_].bind(M)); - } - a.addDestructiveAction("停止调试"); - } - let i = await a.presentSheet(); - if (i === -1) return; - let w; - switch (i) { - case 0: - M.widgetFamily = "small"; - w = await M.render(); - w && (await w.presentSmall()); - break; - case 1: - M.widgetFamily = "medium"; - w = await M.render(); - w && (await w.presentMedium()); - break; - case 2: - M.widgetFamily = "large"; - w = await M.render(); - w && (await w.presentLarge()); - break; - case 3: - M.widgetFamily = "small"; - w = await M.render(); - w && (await w.presentSmall()); - M.widgetFamily = "medium"; - w = await M.render(); - w && (await w.presentMedium()); - M.widgetFamily = "large"; - w = await M.render(); - w && (await w.presentLarge()); - break; - default: - const func = funcs[i - 4]; - if (func) await func(); - break; - } - - return i; - }, - ]; - const alert = new Alert(); - alert.title = M.name; - alert.message = M.desc; - if (isDebug) { - alert.addAction("远程开发"); - } - alert.addAction("预览组件"); - for (let _ in actions) { - alert.addAction(_); - _actions.push(actions[_]); - } - alert.addCancelAction("取消操作"); - const idx = await alert.presentSheet(); - if (_actions[idx]) { - const func = _actions[idx]; - await func(); - } - return; - } - let _tmp = act - .split("-") - .map((_) => _[0].toUpperCase() + _.substr(1)) - .join(""); - let _act = `action${_tmp}`; - if (M[_act] && typeof M[_act] === "function") { - const func = M[_act].bind(M); - await func(data); - } - } -}; +const Runing = async (Widget, default_args = '', isDebug = true, extra) => { + let M = null; + // 判断hash是否和当前设备匹配 + if (config.runsInWidget) { + M = new Widget(args.widgetParameter || ''); + + if (extra) { + Object.keys(extra).forEach((key) => { + M[key] = extra[key]; + }); + } + const W = await M.render(); + try { + if (M.settings.refreshAfterDate) { + const refreshTime = parseInt(M.settings.refreshAfterDate) * 1000 * 60; + const timeStr = new Date().getTime() + refreshTime; + W.refreshAfterDate = new Date(timeStr); + } + } catch (e) { + console.log(e); + } + if (W) { + Script.setWidget(W); + Script.complete(); + } + } else { + let { act, __arg, __size } = args.queryParameters; + M = new Widget(__arg || default_args || ''); + if (extra) { + Object.keys(extra).forEach((key) => { + M[key] = extra[key]; + }); + } + if (__size) M._init(__size); + if (!act || !M['_actions']) { + // 弹出选择菜单 + const actions = M['_actions']; + const onClick = async (item) => { + M.widgetFamily = item.val; + try { + M._init(item.val); + } catch (error) { + console.log('初始化异常:' + error); + } + w = await M.render(); + const fnc = item.val + .toLowerCase() + .replace(/( |^)[a-z]/g, (L) => L.toUpperCase()); + if (w) return w[`present${fnc}`](); + }; + const preview = [], + lockView = []; + if (M.renderSmall) { + preview.push({ + url: `https://raw.githubusercontent.com/dompling/Scriptable/master/images/small.png`, + title: '小尺寸', + val: 'small', + name: 'small', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderMedium) { + preview.push({ + url: `https://raw.githubusercontent.com/dompling/Scriptable/master/images/medium.png`, + title: '中尺寸', + val: 'medium', + name: 'medium', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderLarge) { + preview.push({ + url: `https://raw.githubusercontent.com/dompling/Scriptable/master/images/large.png`, + title: '大尺寸', + val: 'large', + name: 'large', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderAccessoryInline) { + lockView.push({ + icon: { + color: '#4676EE', + name: 'list.triangle', + }, + title: '锁屏列表', + val: 'accessoryInline', + name: 'accessoryInline', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderAccessoryRectangular) { + lockView.push({ + icon: { + color: '#4676EE', + name: 'arrow.rectanglepath', + }, + title: '锁屏 2x', + val: 'accessoryRectangular', + name: 'accessoryRectangular', + dismissOnSelect: true, + onClick, + }); + } + + if (M.renderAccessoryCircular) { + lockView.push({ + icon: { + color: '#4676EE', + name: 'circle.circle', + }, + title: '锁屏 1x', + val: 'accessoryCircular', + name: 'accessoryCircular', + dismissOnSelect: true, + onClick, + }); + } + + const menuConfig = [ + ...(preview ? [{ title: '预览组件', menu: preview }] : []), + ...(lockView.length ? [{ title: '锁屏组件', menu: lockView }] : []), + ...M['_menuActions'], + ]; + + if (actions.length) menuConfig.push({ title: '组件配置', menu: actions }); + + await M.renderAppView(menuConfig, true); + } + } +}; +// await new DmYY().setWidgetConfig(); module.exports = { DmYY, Runing }; diff --git a/Scripts/Ftms.js b/Scripts/Ftms.js new file mode 100644 index 0000000..59acc51 --- /dev/null +++ b/Scripts/Ftms.js @@ -0,0 +1,146 @@ +class Ftms { + constructor(carWidget) { + this.$ = carWidget; + } + + name = '一汽丰田'; + en = 'ftms'; + logo = 'https://www.toyota.com.cn/favicon.ico'; + + baseOpt = { + headers: { + Connection: `keep-alive`, + Host: `appiov.ftms.com.cn`, + 'Content-Type': `application/json`, + }, + body: ``, + }; + + init = async () => { + if (this.$.settings.dataSource) { + this.$.serveInfo = this.$.settings.serveInfo; + this.$.dataSource = this.$.settings.dataSource; + } else { + await this.cacheData(); + } + this.cacheData(); + }; + + cacheData = async () => { + try { + await this.getOilPrice(); + await this.getBmuServeHicleInfo(); + await this.getRemoteInfoDetail(); + } catch (e) { + console.log(e); + } + }; + + getBaseOptions(api) { + const baseURL = `https://appiov.ftms.com.cn`; + console.log({ url: `${baseURL}/${api}`, ...this.baseOpt }); + return { url: `${baseURL}/${api}`, ...this.baseOpt }; + } + + getRemoteInfoDetail = async () => { + const options = this.getBaseOptions( + 'ftms-iov-app-gbook/api/gbook/getRemoteInfoDetail' + ); + const response = await this.$.$request.post(options); + if (response.msg === 'success') { + this.$.dataSource.remoteInfo = response.result; + const safeData = + response.result.list.filter((item) => item.security !== 'safe') || []; + if (safeData.length > 0) { + this.$.dataSource.safeText = `${safeData[0].typeName}:${safeData[0].dataName}`; + } else { + this.$.dataSource.safeText = ``; + } + const dataTime = this.$.dataSource.remoteInfo.datatime.split('-'); + this.$.dataSource.remoteInfo.datatime = `${dataTime[1] || ''}-${ + dataTime[2] || '' + }`; + } else { + this.$.notify(this.name, response.msg); + } + await this.getDrivingMonitorInfo(); + }; + + getDrivingMonitorInfo = async () => { + const options = this.getBaseOptions( + 'ftms-iov-app-gbook/api/gbook/getDrivingMonitorInfo' + ); + const response = await this.$.$request.post(options); + console.log(response); + if (response.msg === 'success') { + this.$.dataSource.monitorInfo = response.result; + } + this.$.dataSource.monitorInfo.oilWasteText = `油耗:${this.$.dataSource.monitorInfo.oilWaste}L/100km`; + this.$.settings.dataSource = this.$.dataSource; + this.$.saveSettings(false); + }; + + getBmuServeHicleInfo = async () => { + let headers = await this.$.getCache('@ftms.headers'); + headers = JSON.parse(headers || '{}'); + this.baseOpt.headers = { + token: headers.token, + 'User-Agent': headers['User-Agent'], + ...this.baseOpt.headers, + }; + const options = { + url: `https://superapp.ftms.com.cn/superapp/users/wt/getbmuservehicleinfo?scriptable=1`, + headers, + }; + if (!this.$.settings.serveInfo) { + const response = await this.$.$request.post(options); + console.log(response); + if (response.code === '200') { + this.$.settings.serveInfo = response.data; + this.$.serveInfo = response.data; + this.$.saveSettings(false); + } else { + this.$.notify(this.name, response.msg); + } + } else { + this.$.serveInfo = this.$.settings.serveInfo || {}; + } + + this.baseOpt.headers.userId = this.$.serveInfo.userId; + this.baseOpt.headers['USER-ID'] = this.$.serveInfo.userId; + this.baseOpt.body = JSON.stringify({ vin: this.$.serveInfo.vin }); + this.baseOpt.headers.Authorization = `Bearer ${this.baseOpt.headers.token}`; + this.baseOpt.headers.accessToken = this.baseOpt.headers.token; + this.baseOpt.headers['ACCESS-TOKEN'] = this.baseOpt.headers.token; + }; + + getOilPrice = async () => { + const location = await Location.current(); + const locationText = await Location.reverseGeocode( + location.latitude, + location.longitude + ); + const { administrativeArea = '' } = locationText[0] || {}; + + const oilNumber = `${this.$.settings.oilNumber || '92'}`; + + const filter = `(CITYNAME="${administrativeArea.replace('省', '')}")`; + const time = Date.now(); + const url = `https://datacenter-web.eastmoney.com/api/data/v1/get?reportName=RPTA_WEB_YJ_JH&columns=ALL&filter=${encodeURIComponent( + filter + )}&sortColumns=DIM_DATE&sortTypes=-1&pageNumber=1&pageSize=1&source=WEB&_=${time}`; + + const options = { url }; + const response = await this.$.$request.post(options); + console.log(response); + if (response.result) { + this.$.dataSource.oilPrice = response.result.data[0]; + this.$.dataSource.oilZDE = response.result.data[0][`ZDE${oilNumber}`]; + this.$.dataSource.oilPriceText = `油价:${ + response.result.data[0][`V${oilNumber}`] + }`; + } + }; +} + +module.exports = Ftms; diff --git a/Scripts/Health.js b/Scripts/Health.js new file mode 100644 index 0000000..582bbbe --- /dev/null +++ b/Scripts/Health.js @@ -0,0 +1,404 @@ +// letiables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: orange; icon-glyph: user-plus; + +/** + * https://www.icloud.com/shortcuts/2be502d8e9694068ae982cd3a70dea89:快捷指令 + * 组件必须配合快捷指令使用,运行快捷指令时,保存的路径是 Scriptable 下 + */ + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 + +if (typeof require === 'undefined') require = importModule +const { DmYY, Runing } = require('./DmYY') + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg) + this.name = '健康行走步数' + this.en = 'healthCenter' + this.maxMonthDist = parseInt(this.settings.maxMonthDist) || 5 // 柱状图比例高度,值越大,柱状范围越广 + this.Run() + } + + widgetFamily = 'medium' + maxYearDist = 1500 + + color1 = Color.orange() + lineColor = new Color('#48484b') + useBoxJS = false + + running = {} + stepsCount = 0 + stepsToday = 0 + + init = async () => { + try { + await this.getData() + } catch (e) { + console.log(e) + } + } + + numberFormat(value) { + try { + const param = {} + let k = 10000 + const size = ['', '万', '亿', '万亿'] + let i + if (value < k) { + param.value = value + param.unit = '' + } else { + i = Math.floor(Math.log(value) / Math.log(k)) + param.value = (value / Math.pow(k, i)).toFixed(2) + param.unit = size[i] + } + return param + } catch (e) { + console.log(e) + } + } + + getData = async () => { + try { + const fileICloud = FileManager.iCloud() + const dir = fileICloud.documentsDirectory() + const path = fileICloud.joinPath(dir, 'health.txt') + const response = fileICloud.readString(path) + let data = JSON.parse(response) + const dateToday = new Date() + const year = dateToday.getFullYear() + let month = dateToday.getMonth() + 1 + let day = dateToday.getDate() + month = month >= 10 ? month : `0${month}` + day = day >= 10 ? day : `0${day}` + const today = `${year}-${month}-${day}` + + data.forEach((item) => { + if (item.health_type === 'Walking + Running Distance') { + item.samples.forEach((run, index) => { + if (item.samples.length - 1 === index) return + const date = run.date + if (!this.running[date]) this.running[date] = 0 + this.running[date] += parseFloat(run.value) + }) + } + if (item.health_type === 'Steps') { + item.samples.forEach((step) => { + if (step.date === today) this.stepsToday = step.value + this.stepsCount += parseInt(step.value) + }) + } + }) + Object.keys(this.running).forEach((key) => { + this.running[key] = Math.floor(this.running[key] * 100) / 100 + }) + } catch (e) { + this.notify( + this.name, + '健康数据读取失败,请点击使用健康数据快捷指令更新步数', + 'https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf' + ) + return false + } + } + + /*------------------------------------------------------------------------------ +50 km Linien +------------------------------------------------------------------------------*/ + createLines(stack) { + let canvas, path + // 50km Linien + canvas = new DrawContext() + canvas.size = new Size(292, 82) + canvas.opaque = false + canvas.respectScreenScale = true + canvas.setFillColor(this.lineColor) + path = new Path() + path.addRect(new Rect(0, 0, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + path = new Path() + path.addRect(new Rect(0, 15, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + path = new Path() + path.addRect(new Rect(0, 30, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + path = new Path() + path.addRect(new Rect(0, 45, 292, 1)) + canvas.addPath(path) + canvas.fillPath() + stack.backgroundImage = canvas.getImage() + } + + async buildWidget(widget) { + // // Stacks definieren + let stackYear = widget.addStack() + widget.addSpacer() + let stackMonth = widget.addStack() + // Stacks für Symbol und Jahresauswertung aufbereiten + let stackYear1 = stackYear.addStack() + stackYear.addSpacer(10) + let stackYear2 = stackYear.addStack() + let sym = SFSymbol.named('figure.walk') + let img = stackYear1.addImage(sym.image) + img.tintColor = this.color1 + img.imageSize = new Size(25, 25) + stackYear2.layoutVertically() + let stackYearCurr = stackYear2.addStack() + let stackThemItem = stackYear2.addStack() + let stackToday = stackYear2.addStack() + + let data = 0 + const runningData = Object.keys(this.running) + if (runningData.length > 12) runningData.splice(0, runningData.length - 12) + runningData.forEach((date) => { + const [_, month, day] = date.split('-') + const stackDay = stackMonth.addStack() + const value = this.running[date] + this.createProgressMonth(stackDay, `${month}.${day}`, value) + stackMonth.addSpacer(2) + data += value + }) + this.createProgressYear(stackYearCurr, '运动', data, this.color1) + + const count = (18 * this.stepsCount) / ((20000 * this.maxMonthDist) / 4) + this.createProgressSteps( + stackThemItem, + '步数', + this.stepsCount, + this.color1, + count + ) + const today = (18 * this.stepsToday) / ((2000 * this.maxMonthDist) / 4) + this.createProgressSteps( + stackToday, + '今日', + this.stepsToday, + this.color1, + today + ) + + // 50km Linie + this.createLines(stackMonth) + return widget + } + + createProgressYear(stack, year, dist, color) { + let stackDesc, stackPBar, stackDist, canvas, path, txt, img + + // Initialisierung + stack.centerAlignContent() + + // Stacks definieren + stackDesc = stack.addStack() + stackPBar = stack.addStack() + stackDist = stack.addStack() + + // Beschreibung + stackDesc.size = new Size(30, 0) + txt = stackDesc.addText(year) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + stackDesc.addSpacer() + + // Progress-Bar + canvas = new DrawContext() + canvas.size = new Size(180, 7) + canvas.opaque = false + canvas.respectScreenScale = true + canvas.setFillColor(new Color('#48484b')) + path = new Path() + path.addRoundedRect(new Rect(0, 0, 180, 5), 3, 2) + canvas.addPath(path) + canvas.fillPath() + canvas.setFillColor(color) + path = new Path() + path.addRoundedRect( + new Rect(0, 0, (180 * dist) / ((200 * this.maxMonthDist) / 4), 5), + 3, + 2 + ) + canvas.addPath(path) + canvas.fillPath() + img = stackPBar.addImage(canvas.getImage()) + img.imageSize = new Size(180, 7) + + // Distanz + stackDist.addSpacer(10) + txt = stackDist.addText(Math.round(dist).toString() + ' km') + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + } + + createProgressSteps(stack, year, dist, color, rectScale) { + let stackDesc, stackPBar, stackDist, canvas, path, txt, img + + // Initialisierung + stack.centerAlignContent() + + // Stacks definieren + stackDesc = stack.addStack() + stackPBar = stack.addStack() + stackDist = stack.addStack() + + // Beschreibung + stackDesc.size = new Size(30, 0) + txt = stackDesc.addText(year) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + stackDesc.addSpacer() + + // Progress-Bar + canvas = new DrawContext() + canvas.size = new Size(180, 7) + canvas.opaque = false + canvas.respectScreenScale = true + canvas.setFillColor(new Color('#48484b')) + path = new Path() + path.addRoundedRect(new Rect(0, 0, 180, 5), 3, 2) + canvas.addPath(path) + canvas.fillPath() + canvas.setFillColor(color) + path = new Path() + const numberText = this.numberFormat(dist) + + path.addRoundedRect(new Rect(0, 0, rectScale, 5), 3, 2) + canvas.addPath(path) + canvas.fillPath() + img = stackPBar.addImage(canvas.getImage()) + img.imageSize = new Size(180, 7) + + // Distanz + stackDist.addSpacer(10) + + txt = stackDist.addText(numberText.value + ` ${numberText.unit}步`) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + } + + createTemplateItem(stack, desc) { + // Stacks + const txt = stack.addText(desc) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + } + + /*------------------------------------------------------------------------------ +Balkenanzeige für Monatsauswertung aufbereiten +------------------------------------------------------------------------------*/ + createProgressMonth(stack, month, dist3) { + let stackDist, stackPBar, stackDesc, canvas, path, s, img, txt + + // Stacks definieren + stack.layoutVertically() + stackPBar = stack.addStack() + stack.addSpacer(5) + stackDesc = stack.addStack() + stackDist = stack.addStack() + + // Progress-Bar + canvas = new DrawContext() + canvas.size = new Size(17, 60) + canvas.opaque = false + canvas.respectScreenScale = true + + canvas.setFillColor(this.color1) + path = new Path() + s = (50 * dist3) / this.maxMonthDist + path.addRect(new Rect(6, 60 - s, 8, s)) + canvas.addPath(path) + canvas.fillPath() + img = stackPBar.addImage(canvas.getImage()) + img.imageSize = new Size(17, 60) + + // Monat + stackDesc.size = new Size(23, 10) + txt = stackDesc.addText(month) + txt.font = Font.systemFont(7) + txt.textColor = this.widgetColor + txt.centerAlignText() + + // Distanz aktuelle Jahr + stackDist.size = new Size(20, 8) + txt = stackDist.addText(Math.round(dist3).toString()) + txt.font = Font.systemFont(6) + txt.textColor = this.widgetColor + txt.centerAlignText() + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init() + const widget = new ListWidget() + await this.getWidgetBackgroundImage(widget) + await this.buildWidget(widget) + await widget.presentMedium() + if (config.runsFromHomeScreen) return widget + } + + Run = () => { + if (config.runsInApp) { + this.registerAction('捷径安装', async () => { + await this.notify( + this.name, + '点击安装捷径', + 'https://www.icloud.com/shortcuts/beb65db5ea0a474abe7ff080410b9ddf' + ) + }) + this.registerAction('柱状比例', async () => { + await this.setAlertInput( + '设置柱状比例', + ' 柱状图比例高度,值越大,柱状范围越广', + { maxMonthDist: '比例默认值,5' } + ) + }) + this.registerAction('皮肤颜色', this.setWidgetSkin) + this.registerAction('刻度颜色', this.setWidgetScale) + this.registerAction('基础设置', this.setWidgetConfig) + } + const skinColor = !this.isNight + ? this.settings.lightSkinColor + : this.settings.darkSkinColor + this.color1 = skinColor ? new Color(skinColor) : this.color1 + const scaleColor = !this.isNight + ? this.settings.lightScaleColor + : this.settings.darkScaleColor + this.lineColor = scaleColor ? new Color(scaleColor) : this.lineColor + } + + setWidgetSkin = async () => { + await this.setLightAndDark( + '柱状颜色', + false, + 'lightSkinColor', + 'darkSkinColor' + ) + } + + setWidgetScale = async () => { + await this.setLightAndDark( + '刻度颜色', + false, + 'lightScaleColor', + 'darkScaleColor' + ) + } +} +let params = args.shortcutParameter +if (params) { + const fileICloud = FileManager.iCloud() + const path = fileICloud.documentsDirectory() + fileICloud.writeString(path + '/health.txt', JSON.stringify(params)) + Script.complete() +} else { + await Runing(Widget, '', false) +} +// @组件代码结束 diff --git a/Scripts/HistoryToday.js b/Scripts/HistoryToday.js index c62f25b..56a4cde 100644 --- a/Scripts/HistoryToday.js +++ b/Scripts/HistoryToday.js @@ -3,160 +3,155 @@ // icon-color: green; icon-glyph: calendar-minus; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "历史上的今天"; - this.en = "historyToday"; - this.logo = - "https://raw.githubusercontent.com/Orz-3/task/master/historyToday.png"; - config.runsInApp && - this.registerAction("设置背景图", this.setWidgetBackground); - this.cacheName = this.md5(`dataSouce_${this.en}`); - } - - today = ""; - dataSource = []; - - init = async () => { - try { - const today = new Date(); - const month = today.getMonth() + 1; - const day = today.getDate(); - this.today = `${month}.${day}`; - await this.getHistoryList(); - } catch (e) { - console.log(e); - } - }; - - getHistoryList = async () => { - const url = `http://code.lssdjt.com/jsondata/history.${this.today}.js`; - const response = await this.$request.get(url); - try { - if (response.d.length > 0) { - const dataSource = response.d; - const result = dataSource.filter((item) => item.j[0]).map((item) => ({ ...item, j: item.j[0] })); - this.dataSource = result; - } - return false; - } catch (e) { - return false; - } - }; - - setListCell = async (cell, data) => { - let { j, t, f } = data; - const [year, desc] = t.split(' '); - const imgUri = "http://img.lssdjt.com"; - let body = cell.addStack(); - body.url = `https://www.lssdjt.com/d/${f}.htm`; - - const textView = body.addStack(); - textView.layoutVertically(); - - const descText = textView.addText(desc); - descText.font = Font.boldSystemFont(14); - descText.textColor = this.widgetColor; - descText.lineLimit = 1; - - textView.addSpacer(); - const subContent = textView.addText(year); - subContent.font = Font.lightSystemFont(10); - subContent.textColor = this.widgetColor; - subContent.lineLimit = 1; - - if (this.widgetFamily !== "small") { - body.addSpacer(); - const imageView = body.addStack(); - imageView.centerAlignContent(); - imageView.size = new Size(43, 43); - imageView.cornerRadius = 5; - imageView.borderWidth = 1; - imageView.borderColor = this.widgetColor; - const image = await this.$request.get(`${imgUri}/${j}`, "IMG"); - const imageItem = imageView.addImage(image); - imageItem.centerAlignImage(); - body.addSpacer(10); - } - return cell; - }; - - setWidget = async (body, size) => { - const container = body.addStack(); - container.layoutVertically(); - const dataSource = this.getRandomArrayElements(this.dataSource, size); - for (let index = 0; index < dataSource.length; index++) { - const data = dataSource[index]; - let listItem = container.addStack(); - await this.setListCell(listItem, data); - container.addSpacer(10); - } - body.addSpacer(); - return body; - }; - - renderSmall = async (w) => { - return await this.setWidget(w, 2); - }; - - renderLarge = async (w) => { - return await this.setWidget(w, 5); - }; - - renderMedium = async (w) => { - return await this.setWidget(w, 2); - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderNotSmallHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(10); - if (this.widgetFamily === "medium") { - return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - return await this.renderLarge(widget); - } else { - return await this.renderSmall(widget); - } - } - - renderNotSmallHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - let [month, day] = this.today.split("."); - month = month >= 10 ? month : `0${month}`; - day = day >= 10 ? day : `0${day}`; - headerMore.url = `https://m.8684.cn/today_d${month}${day}`; - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(`更多`); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; + constructor(arg) { + super(arg); + this.name = '历史上的今天'; + this.en = 'historyToday'; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/historyToday.png'; + config.runsInApp && this.registerAction('基础设置', this.setWidgetConfig); + this.cacheName = this.md5(`dataSouce_${this.en}`); + } + + useBoxJS = false; + today = ''; + dataSource = []; + + init = async () => { + try { + const today = new Date(); + const month = today.getMonth() + 1; + const day = today.getDate(); + this.today = `${month}.${day}`; + await this.getHistoryList(); + } catch (e) { + console.log(e); + } + }; + + getHistoryList = async () => { + const url = `http://api.sodion.net/api_v1/grap/todayinhistory`; + const response = await this.$request.get(url); + if (!response || !response.length) + console.log('接口数据异常,请稍后再试!'); + this.dataSource = response; + }; + + setListCell = async (cell, data) => { + let { year, title, href, img } = data; + let body = cell.addStack(); + body.url = href; + + const box = body.addStack(); + box.addSpacer(); + box.setPadding(10, 10, 10, 10); + box.backgroundColor = new Color('#000', 0.1); + box.cornerRadius = 20; + box.layoutVertically(); + + const boxTopStack = box.addStack(); + boxTopStack.addSpacer(); + const avatarStack = boxTopStack.addStack(); + avatarStack.size = new Size(50, 50); + avatarStack.cornerRadius = 25; + const image = await this.$request.get(img, 'IMG'); + const imageItem = avatarStack.addImage(image); + imageItem.centerAlignImage(); + boxTopStack.addSpacer(); + + box.addSpacer(); + const titleStack = box.addStack(); + titleStack.size = new Size(0, 30); + const descText = titleStack.addText(title); + descText.font = Font.boldSystemFont(8); + descText.textColor = this.widgetColor; + descText.lineLimit = 3; + + box.addSpacer(5); + const yearStack = box.addStack(); + yearStack.addSpacer(); + const yearText = yearStack.addText(year); + yearText.font = Font.boldSystemFont(10); + yearText.textColor = this.widgetColor; + yearStack.addSpacer(); + + return cell; + }; + + group(array, subNum) { + let index = 0; + let newArray = []; + while (index < array.length) { + newArray.push(array.slice(index, (index += subNum))); + } + return newArray; + } + + setWidget = async (body, size) => { + const container = body.addStack(); + container.setPadding(10, 10, 10, 10); + const data = this.getRandomArrayElements(this.dataSource, size); + if (size === 6) { + const source = this.group(data, 3); + container.layoutVertically(); + for (const item of source) { + const boxStack = container.addStack(); + container.addSpacer(); + for (let index = 0; index < item.length; index++) { + const data = item[index]; + let listItem = boxStack.addStack(); + await this.setListCell(listItem, data); + if (index !== item.length - 1) boxStack.addSpacer(); + } + } + } else { + const dataSource = data; + for (let index = 0; index < dataSource.length; index++) { + const data = dataSource[index]; + let listItem = container.addStack(); + await this.setListCell(listItem, data); + if (index !== dataSource.length - 1) container.addSpacer(); + } + } + return body; + }; + + renderSmall = async (w) => { + return await this.setWidget(w, 1); + }; + + renderLarge = async (w) => { + return await this.setWidget(w, 6); + }; + + renderMedium = async (w) => { + return await this.setWidget(w, 3); + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.setPadding(0, 0, 0, 0); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/JD-all-one-v2.js b/Scripts/JD-all-one-v2.js new file mode 100644 index 0000000..19321c9 --- /dev/null +++ b/Scripts/JD-all-one-v2.js @@ -0,0 +1,1677 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: pink; icon-glyph: shopping-cart; +// Author: 脑瓜 +// 电报群: https://t.me/Scriptable_JS @anker1209 +// 采用了2Ya美女的京豆收支脚本及DmYY依赖 https://github.com/dompling/Scriptable/tree/master/Scripts +// version:2.2.5 +// update:2021/11/08 + +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); + +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '京东多合一'; + this.en = 'jd_in_one'; + this.run(module.filename, args); + } + fm = FileManager.local(); + CACHE_FOLDER = 'JD_in_one'; + cachePath = null; + + logo = 'https://pic.imgdb.cn/item/6187994b2ab3f51d919028cc.png'; + JDImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/43300bf7-61a2-4bd1-94a1-bf2faa2ed9e8.png'; + beanImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/7ea91cf8-6dea-477c-ae72-cb4d3f646c34.png'; + plusFG = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cd0d2b80-0857-4202-8d12-af4eb7d241d6.png'; + plusBG = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/24fc5a14-edea-4b1b-8e30-bdcc1a27a037.png'; + baitiaoImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/30c40f5b-7428-46c3-a2c0-d81b2b95ec41.png'; + plusIcon = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/06f78540-a5a4-462e-b8c5-98cb8059efc1.png'; + walletImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/cd89ceec-7895-41ee-a1a3-3d3e7223035f.png'; + jingtieImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/16a7038e-6082-4ad8-b17f-fdd08266fb22.png'; + gangbengImg = 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-b1ebbd3c-ca49-405b-957b-effe60782276/9704e332-9e7f-47e8-b09a-1f1991d4aa84.png'; + userImage = 'https://img11.360buyimg.com/jdphoto/s120x120_jfs/t21160/90/706848746/2813/d1060df5/5b163ef9N4a3d7aa6.png'; + nameImg = 'https://pic.imgdb.cn/item/6188bfb62ab3f51d91bca276.png'; + tagImg = 'https://pic.imgdb.cn/item/6188bfc72ab3f51d91bcbcb0.png'; + + // 请勿在此修改参数值 + + version = '2.2.5'; + basicSetting = { + scale: 1.00, + logo: 30, + userImage: 69, + userStack: 103, + division: 25, + interval: 10, + directory: 'Local', + customizeName: '', + customizeAvatar: '', + smallShowType: '京豆、钱包数据', + walletShowType: '红包', + }; + chartSetting = { + height: 130, + daySize: 9, + dayText: '', + textSize: 18, + textDayColor: '999999', + textNightColor: '999999', + lineColor: '#FA6859', + linePadding: 15, + barPadding: 5, + smallShowType: '双日视图', + showType: '双日视图', + countBean: '收入-支出', + colorful: '关闭', + }; + funcSetting = { + showBaitiao: '打开', + showPackage: '关闭', + showFruit: '打开', + logable: '关闭', + alwaysRefreshChart: '打开', + }; + package = { + number: 0, + title: '', + desc: '', + time: '', + status: '', + }; + baitiao = { + title: '', + number: 0, + desc: '', + }; + redPackage = { + title: '红包', + number: 0, + desc: '今日无过期', + }; + extra = { + jingtie: 0, + gangbeng: 0, + }; + bean = { + todayIncome: 0, + todayExpense: 0, + ydayIncome: 0, + ydayExpense: 0, + }; + + nickName = '未知用户'; + jValue = '0'; + isPlus = false; + + cookie = ''; + userName = ''; + CookiesData = []; + cacheChart = false; + beanCount = 0; + maxDays = 6; + rangeTimer = {}; + timerKeys = []; + fruitState = "😢"; + + doubleDate = this.getDay(1); + doubleDay = Object.keys(this.doubleDate); + yestoday = this.doubleDay[0]; + today = this.doubleDay[1]; + CACHES = []; + + lineChart(labels = [], datas = [], chartTextSize, topPadding) { + let chartTextColor = Color.dynamic(new Color(this.chartSetting.textDayColor),new Color(this.chartSetting.textNightColor),); + let lineColor = this.chartSetting.lineColor.split(','); + const chartStr = ` + { + type: 'bar', + data: { + labels: ${JSON.stringify(labels)}, + datasets: [ + { + type: 'line', + backgroundColor: '#FFFFFF', + borderColor: getGradientFillHelper('horizontal', ${JSON.stringify(lineColor)}), + borderWidth: ${this.isSmall(true) ? 4 : 3}, + pointRadius: ${this.isSmall(true) ? 8 : 6}, + fill: false, + showLine: true, + data: ${JSON.stringify(datas)}, + }, + ], + }, + options: { + plugins: { + datalabels: { + display: true, + align: 'top', + color: '#${chartTextColor.hex}', + font: { + family: 'ArialMT', + size: ${chartTextSize} + } + }, + }, + layout: { + padding: { + left: -20, + right: 0, + top: ${topPadding}, + bottom: 0 + } + }, + responsive: true, + maintainAspectRatio: true, + legend: { + display: false, + }, + scales: { + xAxes: [ + { + gridLines: { + display: false, + }, + ticks: { + display: false, + }, + }, + ], + yAxes: [ + { + ticks: { + display: false, + beginAtZero: false, + }, + gridLines: { + display: false, + }, + }, + ], + }, + }, + }`; + return chartStr; + } + + barChart(labels = [], datas = [], chartTextSize, topPadding, showType) { + let chartTextColor = Color.dynamic(new Color(this.chartSetting.textDayColor),new Color(this.chartSetting.textNightColor),); + let backgroundColor = []; + if (this.chartSetting.colorful === '打开') backgroundColor = JSON.stringify(this.colorfulBar()) + else backgroundColor = `getGradientFillHelper('vertical', ${JSON.stringify(this.chartColors())})` + const chartStr = ` + { + type: 'bar', + data: { + labels: ${JSON.stringify(labels)}, + datasets: [ + { + type: '${showType}', + borderWidth: 0, + pointRadius: 0, + barPercentage: 0.5, + backgroundColor: ${backgroundColor}, + borderColor: false, + data: ${JSON.stringify(datas)}, + }, + ], + }, + options: { + plugins: { + datalabels: { + display: true, + align: 'top', + offset: -4, + anchor:'end', + color: '#${chartTextColor.hex}', + font: { + family: 'ArialMT', + size: ${chartTextSize} + } + }, + }, + layout: { + padding: { + left: -20, + right: 0, + top: ${topPadding}, + bottom: 0 + } + }, + responsive: true, + maintainAspectRatio: true, + legend: { + display: false, + }, + title: { + display: false, + }, + scales: { + xAxes: [ + { + gridLines: { + offsetGridLines: true, + display: false, + }, + ticks: { + display: false, + }, + }, + ], + yAxes: [ + { + ticks: { + display: false, + beginAtZero: true, + }, + gridLines: { + offsetGridLines: true, + display: false, + }, + }, + ], + }, + }, + }`; + return chartStr; + } + + chartColors () { + 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']]; + let chartColors = colorArr[Math.floor(Math.random() * colorArr.length)]; + //chartColors = ['#DB36A4', '#F7FF00']; // 固定京豆图表填充颜色 + return chartColors; + } + + colorfulBar () { + 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'], ]; + let chartColors = colorArr[Math.floor(Math.random() * colorArr.length)]; + //chartColors = ['#C1B14A','#6FBC75','#39B6B3','#86A1CD','#D083AB','#DF786B']; // 固定京豆图表填充颜色 + return chartColors; + } + + isSmall (a = false) { + if (a) return config.widgetFamily == 'small' ? true : false; + else return config.widgetFamily == 'small' ? '_small' : ''; + } + + // #####################小组件################### + renderSmall = async (w) => { + const bodyStack = w.addStack(); + bodyStack.layoutVertically(); + if (this.basicSetting.smallShowType === '个人信息') { + await this.setUserShow(bodyStack); + } else { + await this.setHeaderShow(bodyStack); + bodyStack.addSpacer(); + switch (this.chartSetting.smallShowType) { + case '折线图表' : + await this.setChartShow(bodyStack, 1); + break; + case '柱状图表' : + await this.setChartShow(bodyStack, 2); + break; + case '曲线面积图': + await this.setChartShow(bodyStack, 3); + break; + default: + await this.setBeanShow(bodyStack, 22 * this.basicSetting.scale, 40 * this.basicSetting.scale); + } + bodyStack.addSpacer(5 * this.basicSetting.scale); + if (this.funcSetting.showBaitiao === '打开' && this.baitiao.number > 0) { + await this.setBaitiaoShow(bodyStack, true); + } else if (this.basicSetting.walletShowType === '红包') { + await this.setRedPackageShow(bodyStack, true); + } else { + await this.setCoinShow(bodyStack, true); + } + } + return w; + } + + // #####################中组件################### + renderMedium = async (w) => { + const bodyStack = w.addStack(); + await this.setUserShow(bodyStack); + bodyStack.addSpacer(this.basicSetting.division * this.basicSetting.scale); + const mainStack = bodyStack.addStack(); + mainStack.layoutVertically(); + await this.setHeaderShow(mainStack, this.JDImg); + mainStack.addSpacer(); + if (this.funcSetting.showPackage === '打开' && this.package.number > 0) { + await this.setPackageShow(mainStack); + mainStack.addSpacer(); + } else { + switch (this.chartSetting.showType) { + case '折线图表': + await this.setChartShow(mainStack, 1); + mainStack.addSpacer(5 * this.basicSetting.scale); + break; + case '柱状图表': + await this.setChartShow(mainStack, 2); + mainStack.addSpacer(5 * this.basicSetting.scale); + break; + case '曲线面积图': + await this.setChartShow(mainStack, 3); + mainStack.addSpacer(5 * this.basicSetting.scale); + break; + default: + await this.setBeanShow(mainStack, 30 * this.basicSetting.scale, 50 * this.basicSetting.scale); + mainStack.addSpacer(); + } + } + if (this.funcSetting.showBaitiao === '打开' && this.baitiao.number > 0) { + await this.setBaitiaoShow(mainStack); + } else if (this.basicSetting.walletShowType === '红包') { + await this.setRedPackageShow(mainStack); + } else { + await this.setCoinShow(mainStack); + } + return w; + } + + // #####################大组件################### + renderLarge = async (w) => { + const bodyStack = w.addStack(); + bodyStack.size = new Size(0, 150); + bodyStack.addSpacer(); + await this.setUserShow(bodyStack); + bodyStack.addSpacer(); + w.addSpacer(20); + const text = w.addText('\u6211\u600e\u4e48\u8fd9\u4e48\u597d\u770b'); + w.addSpacer(20); + text.font = Font.thinSystemFont(30); + text.centerAlignText(); + const emoji = w.addText('🤣🥰🤪'); + emoji.centerAlignText(); + w.addSpacer(); + return w; + } + + // #####################用户信息################### + async setUserShow(stack) { + const userStack = stack.addStack(); + userStack.size = new Size(this.basicSetting.userStack * this.basicSetting.scale, 0); + userStack.layoutVertically(); + // 头像 + const userImgStack = userStack.addStack(); + userImgStack.addSpacer(); + const imgStack = userImgStack.addStack(); + if (this.isPlus) { + imgStack.size = new Size(this.basicSetting.userImage * this.basicSetting.scale, this.basicSetting.userImage * this.basicSetting.scale * 1.039); + imgStack.backgroundImage = await this.getImageByUrl(this.plusBG, 'plusBGImage.png'); + } + const subStack = imgStack.addStack(); + subStack.url = 'openapp.jdmobile://'; + subStack.size = new Size(this.basicSetting.userImage * this.basicSetting.scale, this.basicSetting.userImage * this.basicSetting.scale); + subStack.cornerRadius = this.basicSetting.userImage / 2 * this.basicSetting.scale; + subStack.backgroundImage = await this.getImageByUrl(this.basicSetting.customizeAvatar || this.userImage, `userImage_${this.userName}.png`); + if (this.isPlus) { + const userImg = subStack.addImage(await this.getImageByUrl(this.plusFG, 'plusFGImage.png')); + } + userImgStack.addSpacer(); + userStack.addSpacer(); + // 物流提示 + const tipStack = userStack.addStack(); + tipStack.addSpacer(); + const signStack = tipStack.addStack(); + signStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) + signStack.backgroundColor = new Color('0dD6A0'); + signStack.cornerRadius = 14 * this.basicSetting.scale / 2; + signStack.centerAlignContent(); + let signIcon = SFSymbol.named('checkmark'); + const signItem = signStack.addImage(signIcon.image); + signItem.imageSize = new Size(8 * this.basicSetting.scale, 8 * this.basicSetting.scale); + signItem.tintColor = new Color('FFFFFF'); + if (this.package.number > 0) { + tipStack.addSpacer(3 * this.basicSetting.scale); + const packageStack = tipStack.addStack(); + packageStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) + packageStack.backgroundColor = new Color('FC8600'); + packageStack.cornerRadius = 14 * this.basicSetting.scale / 2; + packageStack.centerAlignContent(); + packageStack.setPadding(1 * this.basicSetting.scale, 2 * this.basicSetting.scale, 1 * this.basicSetting.scale, 2 * this.basicSetting.scale); + let packageNum = packageStack.addText(this.package.number.toString()); + packageNum.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + packageNum.textColor = new Color('FFFFFF'); + packageNum.minimumScaleFactor = 0.1; + } + if (this.funcSetting.showFruit === '打开') { + tipStack.addSpacer(3 * this.basicSetting.scale); + const fruitStack = tipStack.addStack(); + fruitStack.size = new Size(14 * this.basicSetting.scale, 14 * this.basicSetting.scale) + fruitStack.backgroundColor = new Color('118AB2'); + fruitStack.cornerRadius = 14 * this.basicSetting.scale / 2; + fruitStack.centerAlignContent(); + fruitStack.setPadding(1 * this.basicSetting.scale, 2 * this.basicSetting.scale, 1 * this.basicSetting.scale, 2 * this.basicSetting.scale); + let fruitText = fruitStack.addText(this.fruitState); + fruitText.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + fruitText.textColor = new Color('FFFFFF'); + fruitText.minimumScaleFactor = 0.1; + } + tipStack.addSpacer(); + userStack.addSpacer(); + // 用户名 + const nameStack = userStack.addStack(); + nameStack.centerAlignContent(); + if (this.isPlus) { + const nameImg = nameStack.addImage(await this.getImageByUrl(this.plusIcon, 'plusIcon.png')); + nameImg.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); + } else { + const nameIcon = nameStack.addImage(await this.getImageByUrl(this.nameImg, 'nameImg.png')); + nameIcon.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); + } + nameStack.addSpacer(5 * this.basicSetting.scale); + const name = nameStack.addText(this.basicSetting.customizeName || this.nickName); + name.lineLimit = 1; + name.font = Font.regularSystemFont(14 * this.basicSetting.scale); + userStack.addSpacer(5 * this.basicSetting.scale); + // 京享值 + const valueStack = userStack.addStack(); + valueStack.centerAlignContent(); + const lableIcon = valueStack.addImage(await this.getImageByUrl(this.tagImg, 'tagImg.png')); + lableIcon.imageSize = new Size(15 * this.basicSetting.scale, 15 * this.basicSetting.scale); + valueStack.addSpacer(5 * this.basicSetting.scale); + const value = valueStack.addText(this.jValue.toString()); + value.font = Font.mediumSystemFont(14 * this.basicSetting.scale); + + valueStack.addSpacer(5 * this.basicSetting.scale); + const jStack = valueStack.addStack(); + jStack.backgroundColor = new Color('fa2d19'); // “京享”二字背景颜色 + jStack.cornerRadius = 5; + jStack.setPadding(1 * this.basicSetting.scale, 4 * this.basicSetting.scale, 1 * this.basicSetting.scale, 4 * this.basicSetting.scale); + const jLable = jStack.addText('京享'); + jLable.font = Font.systemFont(8 * this.basicSetting.scale); + jLable.textColor = new Color('FFFFFF') // “京享”二字字体颜色 + ;[name, value].map(t => t.textColor = this.widgetColor); + } + + // #####################顶部内容################### + async setHeaderShow(stack, image) { + const topStack = stack.addStack(); + topStack.centerAlignContent(); + if (image) { + const JDLogo = topStack.addImage(await this.getImageByUrl(this.logo, 'logoImage.png')); + JDLogo.imageSize = new Size(this.basicSetting.logo * this.basicSetting.scale, this.basicSetting.logo * this.basicSetting.scale); + topStack.addSpacer(10 * this.basicSetting.scale); + const JD = topStack.addImage(await this.getImageByUrl(image, 'jingdongImage.png')); + JD.imageSize = new Size(194 * 0.2 * this.basicSetting.scale, 78 * 0.2 * this.basicSetting.scale); + } else { + const imgStack = topStack.addStack(); + if (this.isPlus) { + imgStack.size = new Size(30 * this.basicSetting.scale, 30 * this.basicSetting.scale * 1.039); + imgStack.backgroundImage = await this.getImageByUrl(this.plusBG, 'plusBGImage.png'); + } + const subStack = imgStack.addStack(); + subStack.url = 'openapp.jdmobile://'; + subStack.size = new Size(30 * this.basicSetting.scale, 30 * this.basicSetting.scale); + subStack.cornerRadius = 30 / 2 * this.basicSetting.scale; + subStack.backgroundImage = await this.getImageByUrl(this.basicSetting.customizeAvatar || this.userImage, `userImage_${this.userName}.png`); + if (this.isPlus) { + const userImg = subStack.addImage(await this.getImageByUrl(this.plusFG, 'plusFGImage.png')); + } + } + topStack.addSpacer(); + const jdBean = topStack.addText(this.beanCount.toString()); + jdBean.font = Font.mediumSystemFont(20 * this.basicSetting.scale); + jdBean.textColor = new Color('fa2d19'); // 右上角京豆数颜色 + 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'; + const desStack = topStack.addStack(); + desStack.layoutVertically(); + desStack.addSpacer(5.5 * this.basicSetting.scale); + const desText = desStack.addText(' 京豆'); + desText.font = Font.mediumSystemFont(10 * this.basicSetting.scale); + desText.textColor = new Color('fa2d19', 0.7); + } + + // #####################京豆收支################### + async setBeanShow(stack, textSize, imageSize) { + const beanStack = stack.addStack(); + // 今日收支 + const yestodayStack = beanStack.addStack(); + yestodayStack.layoutVertically(); + try { + this.bean.ydayIncome = this.rangeTimer[this.yestoday][0] - this.rangeTimer[this.yestoday][1]; + this.bean.ydayExpense = this.rangeTimer[this.yestoday][1]; + this.bean.todayIncome = this.rangeTimer[this.today][0] - this.rangeTimer[this.today][1];; + this.bean.todayExpense = this.rangeTimer[this.today][1]; + } catch (e) { + 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'); + } + this.rowBeanCell( + yestodayStack, + this.bean.ydayExpense.toString(), + this.bean.ydayIncome.toString(), + textSize, + '昨日', + ); + beanStack.addSpacer(); + // 京豆图片 + const ddStack = beanStack.addStack(); + ddStack.layoutVertically(); + const ddImg = ddStack.addImage(await this.getImageByUrl(this.beanImg, 'beanImage.png')); + ddImg.imageSize = new Size(imageSize, imageSize); + beanStack.addSpacer(); + // 昨日收支 + const todayStack = beanStack.addStack(); + todayStack.layoutVertically(); + this.rowBeanCell( + todayStack, + this.bean.todayExpense.toString(), + this.bean.todayIncome.toString(), + textSize, + '今日', + ); + } + + // #####################京豆图表################### + async setChartShow(stack, type) { + let labels = [], data = []; + Object.keys(this.rangeTimer).forEach((day) => { + const value = this.rangeTimer[day]; + const arrDay = day.split('-'); + labels.push(arrDay[2]); + if (this.chartSetting.countBean === '收入-支出') + data.push(value[0]); + else data.push(value[0] - value[1]); + }); + let cacheKey = `chart${type}Image${this.isSmall()}_${this.userName}.png`; + let textSize = this.chartSetting.textSize; + let linePadding = this.chartSetting.linePadding; + let barPadding = this.chartSetting.barPadding; + if (config.widgetFamily === 'small') { + data.splice(0, 2); + labels.splice(0, 2); + textSize = this.chartSetting.textSize + 7; + linePadding = this.chartSetting.linePadding + 10; + barPadding = this.chartSetting.barPadding + 5; + } + let chartStr; + switch (type) { + case 2: + chartStr = this.barChart(labels, data, textSize, barPadding, 'bar'); + break; + case 3: + chartStr = this.barChart(labels, data, textSize, barPadding, 'line'); + break; + default: + chartStr = this.lineChart(labels, data, textSize, linePadding); + } + const url = `https://quickchart.io/chart?w=${400}&h=${this.chartSetting.height}&f=png&c=${encodeURIComponent(chartStr)}`; + const chart = await this.getImageByUrl(url, cacheKey, this.cacheChart); + + const chartStack = stack.addStack(); + const chartImage = chartStack.addImage(chart); + const beanDateStack = stack.addStack(); + let showDays = data.length; + for (let i = 0; i < showDays; i++) { + beanDateStack.addSpacer(); + let subStack = beanDateStack.addStack(); + let beanDay = beanDateStack.addText(`${labels[i]}${this.chartSetting.dayText}`); + beanDay.textColor = this.widgetColor; + beanDay.font = new Font('ArialMT', this.chartSetting.daySize * this.basicSetting.scale); + beanDay.textOpacity = 0.8; + beanDateStack.addSpacer(); + } + } + + // #####################物流信息################### + setPackageShow(stack) { + const packageStack = stack.addStack(); + const detailStack = packageStack.addStack(); + detailStack.layoutVertically(); + const titleStack = detailStack.addStack(); + titleStack.centerAlignContent(); + const title = titleStack.addText(this.package.title); + title.lineLimit = 1; + title.font = Font.mediumSystemFont(12 * this.basicSetting.scale); + detailStack.addSpacer(2 * this.basicSetting.scale); + const desc = detailStack.addText(this.package.desc); + desc.lineLimit = 3; + desc.font = Font.regularSystemFont(12 * this.basicSetting.scale); + detailStack.addSpacer(2 * this.basicSetting.scale); + const statusStack = detailStack.addStack(); + const time = statusStack.addText(this.package.time); + statusStack.addSpacer(); + const status = statusStack.addText(this.package.status); + ;[title, desc, time, status].map(t => t.textColor = this.widgetColor); + ;[time, status].map(t => t.font = Font.regularSystemFont(9 * this.basicSetting.scale)); + ;[time, status].map(t => t.textOpacity = 0.7); + } + + // #####################金贴&钢镚################## + async setCoinShow(stack, vertical = false) { + await this.getExtraData(); + const extraDataStack = stack.addStack(); + const jtImage = await this.getImageByUrl(this.jingtieImg, 'jtImage.png'); + const gbImage = await this.getImageByUrl(this.gangbengImg, 'gbImage.png'); + const dataStack = extraDataStack.addStack(); + if (vertical) dataStack.layoutVertically(); + this.rowCell(dataStack, jtImage, this.extra.jingtie.toString(), '金贴'); + if (vertical) extraDataStack.addSpacer(5 * this.basicSetting.scale); + if (!vertical) dataStack.addSpacer(20 * this.basicSetting.scale); + this.rowCell(dataStack, gbImage, this.extra.gangbeng.toString(), '钢镚'); + } + + // #####################京东红包################## + async setRedPackageShow(stack, small = false) { + await this.getRedPackageData(); + const walletImage = await this.getImageByUrl(this.walletImg, 'walletImage.png'); + small ? this.rowSmallWalletCell(stack, walletImage, this.redPackage) : this.rowWalletCell(stack, walletImage, this.redPackage); + } + + // #####################京东白条################## + async setBaitiaoShow(stack, small = false) { + const baitiaoImage = await this.getImageByUrl(this.baitiaoImg, 'baitiaoImage.png'); + small ? this.rowSmallWalletCell(stack, baitiaoImage, this.baitiao) : this.rowWalletCell(stack, baitiaoImage, this.baitiao); + } + + rowCell(stack, image, value, title) { + const rowStack = stack.addStack(); + rowStack.centerAlignContent(); + const rowImage = rowStack.addImage(image); + rowImage.imageSize = new Size(13 * this.basicSetting.scale, 13 * this.basicSetting.scale); + rowStack.addSpacer(); + const rowValue = rowStack.addText(value); + rowValue.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + rowStack.addSpacer(); + const rowTitle = rowStack.addText(title); + rowTitle.font = Font.regularSystemFont(13 * this.basicSetting.scale); + ;[rowValue, rowTitle].map(t => t.textColor = this.widgetColor); + } + + rowBeanCell(stack, min, add, textSize, label) { + const rowOne = stack.addStack(); + const labelText = rowOne.addText(label); + labelText.font = Font.regularSystemFont(10 * this.basicSetting.scale); + labelText.textOpacity = 0.7; + const rowTwo = stack.addStack(); + const rowNumber = rowTwo.addText(add); + rowNumber.font = Font.lightSystemFont(textSize); + if (min < 0) { + const rowThree = stack.addStack(); + const minText = rowThree.addText(min); + minText.font = Font.mediumSystemFont(10 * this.basicSetting.scale); + minText.textColor = new Color('fa2d19'); // 支出京豆颜色 + } + ;[labelText, rowNumber].map(t => t.textColor = this.widgetColor); + } + + rowWalletCell(stack, image, data) { + const stackOne = stack.addStack(); + stackOne.centerAlignContent(); + const stackImage = stackOne.addImage(image); + stackImage.imageSize = new Size(127 * 0.17 * this.basicSetting.scale, 75 * 0.17 * this.basicSetting.scale); + stackOne.addSpacer(5 * this.basicSetting.scale); + const title = stackOne.addText(data.title); + title.font = Font.regularSystemFont(13 * this.basicSetting.scale); + stackOne.addSpacer(); + const number = stackOne.addText(`${data.number}`); + number.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + stackOne.addSpacer(); + const desc = stackOne.addText(data.desc); + desc.font = Font.regularSystemFont(10 * this.basicSetting.scale); + desc.textOpacity = 0.7; + ;[title, number, desc].map(t => t.textColor = this.widgetColor); + } + + rowSmallWalletCell(stack, image, data) { + const stackOne = stack.addStack(); + stackOne.centerAlignContent(); + const stackImage = stackOne.addImage(image); + stackImage.imageSize = new Size(127 * 0.17 * this.basicSetting.scale, 75 * 0.17 * this.basicSetting.scale); + stackOne.addSpacer(); + const number = stackOne.addText(`${data.number}`); + number.font = Font.mediumSystemFont(15 * this.basicSetting.scale); + stack.addSpacer(5 * this.basicSetting.scale); + const stackTwo = stack.addStack(); + stackTwo.centerAlignContent(); + const title = stackTwo.addText(data.title); + title.font = Font.regularSystemFont(13 * this.basicSetting.scale); + stackTwo.addSpacer(); + const desc = stackTwo.addText(data.desc); + desc.font = Font.regularSystemFont(10 * this.basicSetting.scale); + desc.textOpacity = 0.7; + ;[number, title, desc].map(t => t.textColor = this.widgetColor); + } + + init = async () => { + try { + let beanCacheKey = `beanData${this.isSmall()}_${this.userName}`; + let beanCacheData = !this.loadStringCache(beanCacheKey) ? {} : JSON.parse(this.loadStringCache(beanCacheKey)); + let beanCache = beanCacheData.data ? beanCacheData.data.assetInfo.beanNum : 0; + await this.TotalBean(); + await this.wxData(); + console.log(`京豆数据:${beanCache}`); + console.log(`京豆数据:${this.beanCount}`); + + if (!this.cookie) return; + if (Keychain.contains(this.CACHE_KEY)) { + this.rangeTimer = JSON.parse(Keychain.get(this.CACHE_KEY)); + if (this.rangeTimer.hasOwnProperty(this.today) && beanCache !== 0 && beanCache == this.beanCount) { + this.cacheChart = this.funcSetting.alwaysRefreshChart ==='打开' ? false : true; + console.log('京豆数据:无变化,使用缓存数据'); + return; + } + + this.rangeTimer[this.today] = [0, 0]; + const timerKeys = Object.keys(this.rangeTimer); + if (timerKeys.length > this.maxDays) { + for (let i = 0; i < timerKeys.length - this.maxDays; i++) { + delete this.rangeTimer[timerKeys[i]]; + } + Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); + } + + this.timerKeys = [this.today]; + } else { + this.rangeTimer = this.getDay(5); + this.timerKeys = Object.keys(this.rangeTimer); + } + await this.getAmountData(); + } catch (e) { + console.log(e); + } + }; + + getAmountData = async () => { + let i = 0, + page = 1; + do { + const response = await this.getJingBeanBalanceDetail(page); + const result = response.code === '0'; + console.log(`第${page}页:${result ? '请求成功' : '请求失败'}`); + if (response.code === '3') { + i = 1; + this.notify(this.name, response.message) + console.log(response); + } + if (response && result) { + page++; + let detailList = response.jingDetailList; + if (detailList && detailList.length > 0) { + for (let item of detailList) { + const dates = item.date.split(' '); + if (this.timerKeys.indexOf(dates[0]) > -1) { + const amount = Number(item.amount); + this.rangeTimer[dates[0]][0] += amount; + if (amount < 0) + this.rangeTimer[dates[0]][1] += amount; + } else { + i = 1; + Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); + break; + } + } + } + } + } while (i === 0); + } + + getDay(dayNumber) { + let data = {}; + let i = dayNumber; + do { + const today = new Date(); + const year = today.getFullYear(); + const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; + today.setTime(targetday_milliseconds); + let month = today.getMonth() + 1; + month = month >= 10 ? month : `0${month}`; + let day = today.getDate(); + day = day >= 10 ? day : `0${day}`; + data[`${year}-${month}-${day}`] = [0, 0]; + i--; + } while (i >= 0); + return data; + } + + TotalBean = async () => { + const dataName = '京豆数据'; + let userCache = `beanData${this.isSmall()}`; + const url = 'https://me-api.jd.com/user_new/info/GetJDUserInfoUnion'; + const options = { + headers: { + cookie: this.cookie, + }, + }; + const response = await this.httpRequest(dataName, url, true, options, userCache); + try { + if (response.retcode === '0' && response['data']) { + this.beanCount = response.data.assetInfo.beanNum; + this.userImage = response.data.userInfo.baseInfo.headImageUrl.replace(/big/, 'mid') || 'https://img11.360buyimg.com/jdphoto/s120x120_jfs/t21160/90/706848746/2813/d1060df5/5b163ef9N4a3d7aa6.png'; + this.nickName = response.data.userInfo.baseInfo.nickname; + this.isPlus = response.data.userInfo.isPlusVip === '1' ? true : false; + } else { + this.notify(this.name, response.msg); + console.log('京豆数据:获取失败,' + response.msg) + } + } catch (e) { + console.log(e); + console.log('京豆数据:获取失败,') + } + } + + wxData = async () => { + const dataName = '微信数据'; + let userCache = `wxData${this.isSmall()}`; + const url = 'https://wxapp.m.jd.com/kwxhome/myJd/home.json?&useGuideModule=0&bizId=&brandId=&fromType=wxapp'; + const options = { + headers: { + cookie: this.cookie, + }, + }; + const response = await this.httpRequest(dataName, url, true, options, userCache); + try { + if (response['user']) { + this.jValue = response.user.uclass.replace(/[^0-9]/ig, ''); + } else { + this.notify(this.name, response.msg); + console.log('微信数据:获取失败,' + response.msg) + } + } catch (e) { + console.log(e); + } + } + + getJingBeanBalanceDetail = async (page) => { + try { + const options = { + url: `https://bean.m.jd.com/beanDetail/detail.json`, + body: `page=${page}`, + headers: { + 'X-Requested-With': `XMLHttpRequest`, + Connection: `keep-alive`, + 'Accept-Encoding': `gzip, deflate, br`, + 'Content-Type': `application/x-www-form-urlencoded; charset=UTF-8`, + Origin: `https://bean.m.jd.com`, + '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`, + Cookie: this.cookie, + Host: `bean.m.jd.com`, + Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, + 'Accept-Language': `zh-cn`, + Accept: `application/json, text/javascript, */*; q=0.01`, + }, + }; + let params = {...options, method: 'POST'}; + let request = new Request(params.url); + Object.keys(params).forEach((key) => { + request[key] = params[key]; + }); + return await request.loadJSON(); + } catch (e) { + console.log(e); + } + } + + getExtraData = async () => { + const JTDataName = '金贴数据'; + const JTUrl = 'https://ms.jr.jd.com/gw/generic/uc/h5/m/mySubsidyBalance'; + const GBDataName = '钢镚数据'; + const GBUrl = 'https://coin.jd.com/m/gb/getBaseInfo.html'; + const options = { + headers: { + cookie: this.cookie, + Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', + }, + }; + try { + const JTData = await this.httpRequest(JTDataName, JTUrl, true, options, 'jintieData'); + const GBData = await this.httpRequest(GBDataName, GBUrl, true, options, 'gangbengData'); + if (JTData.resultCode === 0) { + this.extra.jingtie = JTData.resultData.data['balance']; + } else { + this.notify(this.name, JTdata.resultMsg); + console.log('金贴数据:获取失败,' + JTdata.resultMsg); + }; + if (GBData.gbBalance) this.extra.gangbeng = GBData.gbBalance; + } catch(e) { + console.log(e); + } + } + + getPackageData = async () => { + const dataName = '包裹数据'; + const url ='https://wq.jd.com/bases/wuliudetail/notify?sceneval=2&sceneval=2&g_login_type=1&callback'; + const options = { + headers: { + cookie: this.cookie, + Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'packageData'); + if (data.errCode == 0 && data['dealLogList']) { + console.log('包裹数据:获取成功'); + console.log(`包裹数据:您有${data['dealLogList'].length}个包裹`); + if (data['dealLogList'].length > 0) { + let item = data.dealLogList[Math.floor(Math.random() * data['dealLogList'].length)] + this.package.number = data.dealLogList.length; + this.package.title = item['name']; + this.package.desc = item['wlStateDesc']; + this.package.time = item['createTime']; + this.package.status = item['stateName']; + } + } else { + console.log('包裹数据:获取失败'); + } + } catch (e) { + console.log(e); + } + } + + getRedPackageData = async () => { + const dataName = '红包数据'; + const url = + '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'; + const options = { + headers: { + cookie: this.cookie, + Referer: 'https://home.m.jd.com/myJd/newhome.action?sceneval=2&ufc=&', + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'redPackageData'); + if (data.errcode === 0) { + this.redPackage.number = data.data.balance ? data.data.balance : 0; + if (data.data.expiredBalance && data.data.expiredBalance !== '') this.redPackage.desc = `即将过期${data.data.expiredBalance}`; + } else { + this.notify(this.name, data.msg); + console.log('红包数据:获取失败,' + data.msg); + } + } catch (e) { + console.log(e); + } + } + + getJValue = async () => { + const dataName = '京享数据'; + const url = 'https://vip.m.jd.com/scoreDetail/current'; + const options = { + headers: { + cookie: this.cookie, + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'JValue'); + if (data.code === 0) { + this.jValue = data.model.scoreDescription.userScore.score; + } else { + console.log('京享数据:获取失败'); + }; + } catch (e) { + console.log(e); + } + } + + getBaitiaoData = async () => { + const dataName = '白条数据'; + const url = 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew'; + const options = { + body: 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}', + headers: { + cookie: this.cookie, + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'baitiaoData', 'POST', false); + if (data.resultCode !== 0) { + return this.notify(this.name, data['resultMsg']); + } + this.baitiao.title = data['resultData']['data']['bill']['title']; + this.baitiao.number = data['resultData']['data']['bill']['amount'].replace(/,/g, ''); + this.baitiao.desc = data['resultData']['data']['bill']['buttonName'].replace(/最近还款日/, ''); + } catch (e) { + console.log(e); + } + } + + getFruitData = async () => { + const dataName = '东东农场'; + const url = 'https://api.m.jd.com/client.action?functionId=initForFarm'; + const options = { + body: 'body=version:4&appid=wh5&clientVersion=9.1.0', + headers: { + 'User-Agent': 'jdapp;iPhone;9.2.2;14.2;%E4%BA%AC%E4%B8%9C/9.2.2 CFNetwork/1206 Darwin/20.1.0', + 'Content-Type': 'application/x-www-form-urlencoded', + 'cookie': this.cookie, + }, + }; + try { + const data = await this.httpRequest(dataName, url, true, options, 'FruitData', 'POST', false); + if (data.msg && data.msg == 'not login') { + this.fruitState = "X"; + } + else if (data.farmUserPro.treeState == 2 || data.farmUserPro.treeState == 3) { + this.fruitState = "100"; + } + else if (data.farmUserPro.treeState == 0) { + this.fruitState = "X"; + } + else { + this.fruitState = Math.floor((data.farmUserPro.treeEnergy / data.farmUserPro.treeTotalEnergy) * 100).toString(); + } + } catch (e) { + console.log(e); + } + } + + getImageByUrl = async(url, cacheKey, useCache = true, logable = true) => { + if (this.CACHES.indexOf(cacheKey) < 0) { + this.CACHES.push(cacheKey); + this.settings.CACHES = this.CACHES; + this.saveSettings(false); + } + if (useCache) { + const cacheImg = this.loadImgCache(cacheKey); + if (cacheImg != undefined && cacheImg != null) { + if (logable) console.log(`使用缓存:${cacheKey}`); + return this.loadImgCache(cacheKey); + } + } + + try { + if (logable) console.log(`在线请求:${cacheKey}`); + const req = new Request(url); + const imgData = await req.load(); + const img = Image.fromData(imgData); + this.saveImgCache(cacheKey, img); + return img; + } catch (e) { + console.error(`图片加载失败:${e}`); + let cacheImg = this.loadImgCache(cacheKey); + if (cacheImg != undefined) { + console.log(`使用缓存图片:${cacheKey}`); + return cacheImg; + } + console.log(`使用预设图片`); + let ctx = new DrawContext(); + ctx.size = new Size(80, 80); + ctx.setFillColor(Color.darkGray()); + ctx.fillRect(new Rect(0, 0, 80, 80)); + return await ctx.getImage(); + } + } + + saveImgCache(cacheKey, img) { + if (!this.fm.fileExists(this.cachePath)) { + this.fm.createDirectory(this.cachePath, true); + }; + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + this.fm.writeImage(cacheFile, img); + } + + loadImgCache(cacheKey) { + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + const fileExists = this.fm.fileExists(cacheFile); + let img = undefined; + if (fileExists) { + img = Image.fromFile(cacheFile); + } + return img; + } + + httpRequest = async(dataName, url, json = true, options, key, method = 'GET', logable = this.funcSetting.logable === '打开') => { + let cacheKey = `${key}_${this.userName}`; + if (this.CACHES.indexOf(cacheKey) < 0) { + this.CACHES.push(cacheKey); + this.settings.CACHES = this.CACHES; + this.saveSettings(false); + } + const localCache = this.loadStringCache(cacheKey); + const lastCacheTime = this.getCacheModificationDate(cacheKey); + const timeInterval = Math.floor((this.getCurrentTimeStamp() - lastCacheTime) / 60); + console.log( + `${dataName}:缓存${timeInterval}分钟前,有效期${this.basicSetting.interval}分钟,${localCache.length}`); + if (timeInterval < this.basicSetting.interval && localCache != null && localCache.length > 0) { + console.log(`${dataName}:读取缓存`); + return json ? JSON.parse(localCache) : localCache; + } + let data = null; + try { + console.log(`${dataName}:在线请求`); + let req = new Request(url); + req.method = method; + Object.keys(options).forEach((key) => { + req[key] = options[key]; + }); + data = await (json ? req.loadJSON() : req.loadString()); + } catch (e) { + console.error(`${dataName}:请求失败:${e}`); + } + if (!data && localCache != null && localCache.length > 0) { + console.log(`${dataName}:获取失败,读取缓存`); + return json ? JSON.parse(localCache) : localCache; + } + this.saveStringCache(cacheKey, json ? JSON.stringify(data) : data); + if (logable) { + console.log(`${dataName}:在线请求响应数据:${JSON.stringify(data)}`); + } + return data; + } + + loadStringCache(cacheKey) { + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + const fileExists = this.fm.fileExists(cacheFile); + let cacheString = ''; + if (fileExists) { + cacheString = this.fm.readString(cacheFile); + } + return cacheString; + } + + saveStringCache(cacheKey, content) { + if (!this.fm.fileExists(this.cachePath)) { + this.fm.createDirectory(this.cachePath, true); + }; + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + this.fm.writeString(cacheFile, content); + } + + getCacheModificationDate(cacheKey) { + const cacheFile = this.fm.joinPath(this.cachePath, cacheKey); + const fileExists = this.fm.fileExists(cacheFile); + if (fileExists) { + return this.fm.modificationDate(cacheFile).getTime() / 1000; + } else { + return 0; + } + } + + getCurrentTimeStamp() { + return new Date().getTime() / 1000; + } + + timeFormat(time) { + let date; + if (time) { + date = new Date(time); + } else { + date = new Date(); + } + return ((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : '0' + (date.getMonth() + 1)) + '月' + (date.getDate() >= 10 ? date.getDate() : '0' + date.getDate()) + '日'; + } + + async updateCheck(version){ + let data; + try { + let updateCheck = new Request('https://raw.githubusercontent.com/anker1209/Scriptable/main/upcoming.json'); + data = await updateCheck.loadJSON(); + if (data.version != version) { + let updata = new Alert(); + updata.title = `有新版 ${data.version} 可用`; + updata.addAction('去Github更新'); + updata.addAction('网页版商店更新'); + updata.addCancelAction('稍后'); + updata.message = '\n更新说明:\n' + data.notes + '\n\n点击相应按钮更新脚本'; + let id = await updata.present(); + if (id == 0) { + Safari.openInApp('https://raw.githubusercontent.com/anker1209/Scriptable/main/scripts/JD-in-one-v2.js'); + } else if (id == 1) { + Safari.openInApp('http://scriptablejs.gitee.io/store/#/menu/myInfo'); + } else { + return; + } + } else { + let updata = new Alert(); + updata.title = '暂无更新'; + updata.addCancelAction('好的'); + updata.message = `\n当前版本 ${version} 为最新版本`; + await updata.present(); + } + } catch(e) { + console.log(e); + } + } + + async faqTable() { + const table = new UITable(); + table.showSeparators = false; + let data; + try { + let faq = new Request('https://raw.githubusercontent.com/anker1209/Scriptable/main/faq.json'); + data = await faq.loadJSON(); + let info = new UITableRow(); + info.height = parseFloat(data.height); + let desc = info.addText(data.update, data.desc); + desc.subtitleColor = Color.blue(); + desc.titleFont = Font.mediumSystemFont(14); + desc.subtitleFont = Font.systemFont(14); + table.addRow(info); + for (let i = 0; i < data.data.length; i++) { + let header = new UITableRow(); + header.backgroundColor = Color.dynamic(new Color('F5F5F5'), new Color('000000'));; + let heading = header.addText(data.data[i].name) + heading.titleFont = Font.mediumSystemFont(17); + heading.centerAligned(); + table.addRow(header); + data.data[i].item.forEach((faq) => { + let row = new UITableRow(); + row.height = parseFloat(faq['height']); + let rowtext = row.addText(faq['question'], faq['answer']); + rowtext.titleFont = Font.mediumSystemFont(16); + rowtext.titleColor = Color.blue(); + rowtext.subtitleFont = Font.systemFont(14); + rowtext.subtitleColor = Color.dynamic(new Color('000000', 0.7), new Color('ffffff', 0.7)); + table.addRow(row); + }); + } + } catch (e) { + console.log(e); + } + await table.present(); + } + + async settingCategory(table, item, outfit, category) { + let header = new UITableRow(); + let heading = header.addText(outfit) + heading.titleFont = Font.mediumSystemFont(17); + heading.centerAligned(); + table.addRow(header); + item.forEach((data) => { + Object.keys(data.option).forEach((key) => { + let row = new UITableRow(); + let rowIcon = row.addImageAtURL(data['icon']); + rowIcon.widthWeight = 100; + let rowtext = row.addText(data['title']); + rowtext.widthWeight = 400; + let rowNumber = row.addText(`${this.settings[category][key]} >`); + rowNumber.widthWeight = 500; + rowNumber.rightAligned(); + rowNumber.titleColor = Color.gray(); + rowNumber.titleFont = Font.systemFont(16); + rowtext.titleFont = Font.systemFont(16); + row.dismissOnSelect = false; + row.onSelect = async () => { + if (data.type == 'text') { + await this.alertInput(data['title'], data['desc'], category, data['option']); + } else if (data.type == 'menu') { + await this.showAlert(data['title'], data['desc'], data['menu'], category, key,); + } + await this.tableContent(table); + } + table.addRow(row); + }); + }); + table.reload(); + } + + async tableContent(table) { + const basic = [ + {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'}, + {type: 'text', title: '京东标志大小', desc: '京东logo(形象狗)大小\n\n缺省值:30', option: {logo: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/logo.png'}, + {type: 'text', title: '用户头像大小', desc: '⚠️注意:若要修改头像,请在京东app上传后将缓存清除再运行脚本。\n\n缺省值:69', option: {userImage: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/userImage.png'}, + {type: 'text', title: '左侧栏宽度', desc: '左侧用户信息栏整体宽度\n\n缺省值:103', option: {userStack: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/userStack.png'}, + {type: 'text', title: '左右栏间距', desc: '左侧用户信息栏与右侧京豆数据间距\n\n缺省值:25', option: {division: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/division.png'}, + {type: 'text', title: '缓存时间', desc: '数据请求间隔时间\n请设置合适时间,避免频繁访问接口数据以及加载缓慢。单位:分钟\n\n缺省值:10', option: {interval: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/interval.png'}, + {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'}, + {type: 'text', title: '自定义昵称', desc: '自定义用户信息栏的昵称名称,\n留空将显示京东账号昵称。\n\n注意:单脚本多账户若使用自定义昵称,所有账户将同时显示此昵称,如需单独自定义昵称,请复制脚本单独设置。', option: {customizeName: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/customizeName.png'}, + {type: 'text', title: '自定义头像', desc: '自定义用户信息栏的头像,\n留空将显示京东APP头像。\n\n注意:单脚本多账户若使用自定义头像,所有账户将同时显示此头像,如需单独自定义头像,请复制脚本单独设置。', option: {customizeAvatar: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/customizeAvatar.png'}, + {type: 'menu', title: '小组件显示内容', desc: '\n缺省值:京豆、钱包数据', option: {smallShowType: ''}, menu: ['京豆、钱包数据', '个人信息'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/smallShowType.png'}, + {type: 'menu', title: '钱包显示类型', desc: '若要显示钱包内容,白条需关闭或者白条打开的情况下无待还白条。\n\n缺省值:红包', option: {walletShowType: ''}, menu: ['红包', '钢镚和金贴'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/walletShowType.png'}, + ]; + const chart = [ + {type: 'text', title: '图表高度', desc: '京豆数据未与日期对齐的,\n请调低此数值\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:130', option: {height: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/height.png'}, + {type: 'text', title: '日期文字大小', desc: '京豆图表底部日期文字大小\n\n缺省值:9', option: {daySize: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/daySize.png'}, + {type: 'text', title: '日期文字后缀', desc: '京豆图表底部日期文字后缀', option: {dayText: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/dayText.png'}, + {type: 'text', title: '京豆数文字大小', desc: '京豆图表数据文字大小\n\n⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:18', option: {textSize: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textSize.png'}, + {type: 'text', title: '京豆数白天颜色', desc: '⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:999999', option: {textDayColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textDayColor.png'}, + {type: 'text', title: '京豆数晚上颜色', desc: '⚠️如需即时查看调整效果,\n[功能设置]-->刷新图表 需打开。\n\n缺省值:999999', option: {textNightColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/textNightColor.png'}, + {type: 'text', title: '折线图线条颜色', desc: '支持渐变色,每个颜色之间以英文逗号分隔,颜色值必须带“#”。\n\n缺省值:#FA6859', option: {lineColor: ''}, icon: 'https://gitee.com/anker1209/image/raw/master/jd/lineColor.png'}, + {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'}, + {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'}, + {type: 'menu', title: '小组件图表类型', desc: '\n缺省值:双日视图', option: {smallShowType: ''}, menu: ['双日视图', '折线图表', '柱状图表', '曲线面积图'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/smallShowType2.png'}, + {type: 'menu', title: '中组件图表类型', desc: '\n缺省值:双日视图', option: {showType: ''}, menu: ['双日视图', '折线图表', '柱状图表', '曲线面积图'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showType.png'}, + {type: 'menu', title: '每日京豆数计算', desc: '\n缺省值:收入-支出', option: {countBean: ''}, menu: ['收入-支出', '收入'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/countBean.png'}, + {type: 'menu', title: '多彩柱状图', desc: '设置为打开时仅对柱状图表生效\n\n缺省值:关闭', option: {colorful: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/colorful.png'}, + ]; + const func = [ + {type: 'menu', title: '白条信息', desc: '关闭或者打开后无待还白条的情况下,\n会显示基础设置里选择的钱包内容。\n\n缺省值:打开', option: {showBaitiao: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showBaitiao.png'}, + {type: 'menu', title: '包裹信息', desc: '只有中组件显示一条物流信息,\n若无物流信息会显示图表设置里选择的图表类型。\n\n缺省值:关闭', option: {showPackage: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showPackage.png'}, + {type: 'menu', title: '农场进度', desc: '显示东东农场种植进度。\n\n缺省值:打开', option: {showFruit: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/showFruit.png'}, + {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'}, + {type: 'menu', title: '刷新图表', desc: '打开,每次刷新组件会随机刷新图表颜色(仅柱状图表和曲线面积图);关闭,则只有在京豆数据有变化的情况下刷新图表颜色及数据。建议在排版调整没有问题后,设置为关闭。设置为打开会加长数据载入时间。\n\n⚠️注意:图表设置选项里修改图表高度、颜色、文字大小、顶边距需打开此选项以查看即时反馈。\n\n缺省值:打开', option: {alwaysRefreshChart: ''}, menu: ['打开', '关闭'], icon: 'https://gitee.com/anker1209/image/raw/master/jd/alwaysRefreshChart.png'}, + ]; + table.removeAllRows(); + let topRow = new UITableRow(); + let leftText = topRow.addButton('教程'); + leftText.widthWeight = 0.25; + leftText.onTap = async () => { + await Safari.open('https://github.com/anker1209/Scriptable#jd_in_one'); + } + let faqText = topRow.addButton('常见问题'); + faqText.widthWeight = 0.25; + faqText.leftAligned(); + faqText.onTap = async () => { + await this.faqTable(); + } + let versionText = topRow.addButton('版本检测'); + versionText.widthWeight = 0.25; + versionText.rightAligned(); + versionText.onTap = async () => { + await this.updateCheck(this.version); + } + let rightText = topRow.addButton('电报群'); + rightText.widthWeight = 0.25; + rightText.rightAligned(); + rightText.onTap = async () => { + await Safari.open('https://t.me/Scriptable_JS'); + } + table.addRow(topRow); + + let header = new UITableRow(); + let heading = header.addText('重置设置') + heading.titleFont = Font.mediumSystemFont(17); + heading.centerAligned(); + table.addRow(header); + let row1 = new UITableRow(); + let rowtext1 = row1.addText('重置缓存','若需要修改头像或数据显示错误,尝试此操作'); + rowtext1.titleFont = Font.systemFont(16); + rowtext1.subtitleFont = Font.systemFont(12); + rowtext1.subtitleColor = new Color('999999'); + row1.dismissOnSelect = false; + row1.onSelect = async () => { + const options = ['取消', '重置']; + const message = '所有在线请求的数据缓存将会被清空'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + this.fm.remove(this.cachePath); + delete this.settings['CACHES']; + this.saveSettings(); + } + table.addRow(row1); + let row2 = new UITableRow(); + let rowtext2 = row2.addText('重置京豆数据','若京豆数据缺失或显示有误,尝试此操作'); + rowtext2.titleFont = Font.systemFont(16); + rowtext2.subtitleFont = Font.systemFont(12); + rowtext2.subtitleColor = new Color('999999'); + row2.dismissOnSelect = false; + row2.onSelect = async () => { + const options = ['取消', '重置']; + const message = '若缺少京豆数据或显示为0(双日视图或图表的京豆数)采用此操作。京豆数据重置后,将会重新抓取近6天的京豆明细。请勿频繁使用,会产生大量数据'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + Keychain.remove(this.settings.CACHE_KEY); + delete this.settings.CACHE_KEY; + this.saveSettings(); + } + table.addRow(row2); + let row3 = new UITableRow(); + let rowtext3 = row3.addText('重置设置参数','设置参数绑定脚本文件名,请勿随意更改脚本文件名'); + rowtext3.titleFont = Font.systemFont(16); + rowtext3.subtitleFont = Font.systemFont(12); + rowtext3.subtitleColor = new Color('999999'); + row3.dismissOnSelect = false; + row3.onSelect = async () => { + const options = ['取消', '重置']; + const message = '本菜单里的所有设置参数将会重置为默认值,重置后请重新打开设置菜单'; + const index = await this.generateAlert(message, options); + if (index === 0) return; + delete this.settings['basicSetting']; + delete this.settings['chartSetting']; + delete this.settings['funcSetting']; + this.saveSettings(); + } + table.addRow(row3); + await this.settingCategory(table, basic, '基础设置', 'basicSetting'); + await this.settingCategory(table, chart, '图表设置', 'chartSetting'); + await this.settingCategory(table, func, '功能设置', 'funcSetting'); + } + + async editSettings() { + const table = new UITable(); + table.showSeparators = true; + await this.tableContent(table); + await table.present(true); + } + + alertInput = async (title, desc, category, opt = {}) => { + const a = new Alert(); + a.title = title; + a.message = !desc ? '' : desc; + let key = Object.keys(opt)[0]; + a.addTextField(key, `${this.settings[category][key]}`); + a.addAction('确定'); + a.addCancelAction('取消'); + const id = await a.presentAlert(); + if (id === -1) return; + this.settings[category][key] = a.textFieldValue(0); + this.saveSettings(); + }; + + async showAlert(title, message, options, category, key) { + let alert = new Alert(); + alert.title = title; + alert.message = message; + alert.addCancelAction('取消') + for (const option of options) { + alert.addAction(option); + }; + let id = await alert.presentAlert(); + if (id === -1) return; + this.settings[category][key] = options[id]; + this.saveSettings(); + } + + run = (filename, args) => { + if(!this.settings.basicSetting) this.settings.basicSetting = this.basicSetting; + Object.keys(this.basicSetting).forEach((key) => { + if(!this.settings.basicSetting.hasOwnProperty(key)) + this.settings['basicSetting'][key] = this.basicSetting[key]; + }); + if(!this.settings.chartSetting) this.settings.chartSetting = this.chartSetting; + Object.keys(this.chartSetting).forEach((key) => { + if(!this.settings.chartSetting.hasOwnProperty(key)) + this.settings['chartSetting'][key] = this.chartSetting[key]; + }); + if(!this.settings.funcSetting) this.settings.funcSetting = this.funcSetting; + Object.keys(this.funcSetting).forEach((key) => { + if(!this.settings.funcSetting.hasOwnProperty(key)) + this.settings['funcSetting'][key] = this.funcSetting[key]; + }); + if(!this.settings.CACHES) this.settings.CACHES = []; + this.CACHES = this.settings.CACHES; + if (this.settings['basicSetting']['directory'] === 'iCloud') this.fm = FileManager.iCloud(); + this.cachePath = this.fm.joinPath(this.fm.documentsDirectory(), this.CACHE_FOLDER); + + if (config.runsInApp) { + this.registerAction('参数配置', this.editSettings, 'https://gitee.com/anker1209/image/raw/master/jd/setting.png'); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', + ]); + if (index === 0) { + await this.jdWebView(); + } else { + await this.setAlertInput('账号设置', '京东账号cookie\n\n⚠️\n用户名和cookie必须输入!\n多账号注意用户名不要重复!', { + username: '用户名,必须输入!多账号勿重复!', + cookie: 'Cookie', + }); + } + }, 'https://gitee.com/anker1209/image/raw/master/jd/account.png'); + this.registerAction('代理缓存', this.actionSettings, 'https://gitee.com/anker1209/image/raw/master/jd/boxjs.png'); + this.registerAction('基础设置', this.setWidgetConfig, 'https://gitee.com/anker1209/image/raw/master/jd/preferences.png'); + } + Object.keys(this.settings['basicSetting']).forEach((key) => { + if (key == 'customizeName' || key == 'customizeAvatar' || key == 'smallShowType' || key == 'walletShowType' || key == 'directory') { + this.basicSetting[key] = this.settings['basicSetting'][key]; + } else if (!isNaN(this.settings['basicSetting'][key])) { + this.basicSetting[key] = parseFloat(this.settings['basicSetting'][key]); + } + }); + Object.keys(this.settings['chartSetting']).forEach((key) => { + if (key == 'textDayColor' || key == 'textNightColor' || key =='showType' || key == 'smallShowType' || key == 'countBean' || key == 'colorful' || key == 'lineColor' || key == 'dayText') { + this.chartSetting[key] = this.settings['chartSetting'][key]; + } else if (!isNaN(this.settings['chartSetting'][key])) { + this.chartSetting[key] = parseFloat(this.settings['chartSetting'][key]); + } + }); + Object.keys(this.settings['funcSetting']).forEach((key) => { + this.funcSetting[key] = this.settings['funcSetting'][key]; + }); + + + }; + + jdWebView = async () => { + const webView = new WebView(); + const url = + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; + await webView.loadURL(url); + await webView.present(true); + const req = new Request( + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', + ); + req.method = 'POST'; + req.body = + 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; + await req.loadJSON(); + const cookies = req.response.cookies; + const account = {username: '', cookie: ''}; + const cookie = []; + cookies.forEach((item) => { + const value = `${item.name}=${item.value}`; + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { + account.username = item.value; + cookie.push(value); + } + }); + account.cookie = cookie.join('; '); + console.log(account); + + if (account.cookie) { + this.settings = {...this.settings, ...account}; + this.saveSettings(false); + console.log(`${this.name}: cookie获取成功,请关闭窗口!`); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); + } + }; + + _loadJDCk = async () => { + try { + const CookiesData = await this.getCache('CookiesJD'); + if (CookiesData) { + this.CookiesData = this.transforJSON(CookiesData); + } + const CookieJD = await this.getCache('CookieJD'); + if (CookieJD) { + const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; + const ck1 = { + cookie: CookieJD, + userName, + }; + this.CookiesData.push(ck1); + } + const Cookie2JD = await this.getCache('CookieJD2'); + if (Cookie2JD) { + const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; + const ck2 = { + cookie: Cookie2JD, + userName, + }; + this.CookiesData.push(ck2); + } + return true; + } catch (e) { + console.log(e); + this.CookiesData = []; + return false; + } + }; + + async actionSettings() { + try { + const table = new UITable(); + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; + // 如果是节点,则先远程获取 + this.settings.cookieData = this.CookiesData; + this.saveSettings(false); + this.CookiesData.map((t, index) => { + const r = new UITableRow(); + r.addText(`parameter:${index} ${t.userName}`); + r.onSelect = (n) => { + this.settings.username = t.userName; + this.settings.cookie = t.cookie; + this.saveSettings(); + }; + table.addRow(r); + }); + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; + if (this.settings.cookie) { + body += ',或者使用当前选中Ck:' + this.settings.username; + } + this.notify(this.name, body); + table.present(false); + } catch (e) { + this.notify( + this.name, + '', + 'BoxJS 数据读取失败,请点击通知查看教程', + 'https://chavyleung.gitbook.io/boxjs/awesome/videos', + ); + } + } + + + async getCookie() { + this.JDindex = + typeof args.widgetParameter === 'string' + ? parseInt(args.widgetParameter) + : false; + let _md5 = this.md5(module.filename + this.en); + if (this.funcSetting.logable === '打开') console.log('当前配置内容:' + JSON.stringify(this.settings)); + await this._loadJDCk(); + this.JDindex = + typeof args.widgetParameter === "string" + ? parseInt(args.widgetParameter) + : false; + try { + if (this.JDindex !== false && this.JDindex + 1 > 0) { + this.cookie = this.CookiesData[this.JDindex]['cookie']; + this.userName =this.CookiesData[this.JDindex]["userName"]; + } else { + const cacheCookie = this.CookiesData.find(item=>item.userName === this.settings.username) || {}; + this.userName = cacheCookie.userName; + this.cookie = cacheCookie.cookie; + } + if (!this.cookie) throw "京东 CK 获取失败"; + this.userName = decodeURI(this.userName); + this.CACHE_KEY = `cache_${_md5}_` + this.userName; + this.settings.CACHE_KEY = this.CACHE_KEY; + this.saveSettings(false); + return true; + } catch (e) { + this.notify("错误提示", e); + return false; + } + } + + async render() { + await this.getCookie(); + if (!this.cookie || !this.userName) { + this.notify(this.name, 'cookie或用户名未设置'); + return; + }; + await this.init(); + await this.getPackageData(); + if (this.funcSetting.showBaitiao === '打开') await this.getBaitiaoData(); + if (this.funcSetting.showFruit === '打开') await this.getFruitData(); + if (this.funcSetting.logable === '打开') console.log(this.rangeTimer); + const widget = new ListWidget(); + const padding = 14 * this.basicSetting.scale; + widget.setPadding(padding, padding, padding, padding); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +await Runing(Widget, '', false); \ No newline at end of file diff --git a/Scripts/JDDou.js b/Scripts/JDDou.js index cd5aba6..af40af9 100644 --- a/Scripts/JDDou.js +++ b/Scripts/JDDou.js @@ -3,375 +3,413 @@ // icon-color: cyan; icon-glyph: yen-sign; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "京东豆"; - this.en = "JDDou"; - this.JDRun(module.filename, args); - } + constructor(arg) { + super(arg); + this.name = '京东豆'; + this.en = 'JDDou'; + this.JDRun(module.filename, args); + } - prefix = "boxjs.net"; - beanCount = 0; - incomeBean = 0; - expenseBean = 0; - timerKeys = []; + beanCount = 0; + incomeBean = 0; + expenseBean = 0; + timerKeys = []; - JDCookie = { - cookie: "", - userName: "", - }; - CookiesData = []; + JDCookie = { + cookie: '', + userName: '', + }; + CookiesData = []; - init = async () => { - try { - await this.TotalBean(); - this.timerKeys = this.getDay(1); - await this.getAmountData(); - } catch (e) { - console.log(e); - } - }; + init = async () => { + try { + await this.TotalBean(); + this.timerKeys = this.getDay(1); + await this.getAmountData(); + } catch (e) { + console.log(e); + } + }; - getAmountData = async () => { - let i = 0, - page = 1; - do { - const response = await this.getJingBeanBalanceDetail(page); - console.log( - `第${page}页:${response.code === "0" ? "请求成功" : "请求失败"}`, - ); - if (response && response.code === "0") { - page++; - let detailList = response.jingDetailList; - if (detailList && detailList.length > 0) { - for (let item of detailList) { - const dates = item.date.split(" "); - if (this.timerKeys.indexOf(dates[0]) > -1) { - if (this.timerKeys[0] === dates[0]) { - const amount = Number(item.amount); - if (amount > 0) this.incomeBean += amount; - if (amount < 0) this.expenseBean += amount; - } - } else { - i = 1; - break; - } - } - } - } - } while (i === 0); - }; + getAmountData = async () => { + let i = 0, + page = 1; + do { + const response = await this.getJingBeanBalanceDetail(page); + console.log( + `第${page}页:${response.code === '0' ? '请求成功' : '请求失败'}`, + ); + if (response.code === '3') { + i = 1; + console.log(response); + } + if (response && response.code === '0') { + page++; + let detailList = response.jingDetailList; + if (detailList && detailList.length > 0) { + for (let item of detailList) { + const dates = item.date.split(' '); + if (this.timerKeys.indexOf(dates[0]) > -1) { + if (this.timerKeys[0] === dates[0]) { + const amount = Number(item.amount); + if (amount > 0) this.incomeBean += amount; + if (amount < 0) this.expenseBean += amount; + } + } else { + i = 1; + break; + } + } + } + } + } while (i === 0); + }; - getDay(dayNumber) { - let data = []; - let i = dayNumber; - do { - const today = new Date(); - const year = today.getFullYear(); - const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; - today.setTime(targetday_milliseconds); //注意,这行是关键代码 - let month = today.getMonth() + 1; - month = month >= 10 ? month : `0${month}`; - let day = today.getDate(); - day = day >= 10 ? day : `0${day}`; - data.push(`${year}-${month}-${day}`); - i--; - } while (i >= 0); - return data; - } + getDay(dayNumber) { + let data = []; + let i = dayNumber; + do { + const today = new Date(); + const year = today.getFullYear(); + const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; + today.setTime(targetday_milliseconds); //注意,这行是关键代码 + let month = today.getMonth() + 1; + month = month >= 10 ? month : `0${month}`; + let day = today.getDate(); + day = day >= 10 ? day : `0${day}`; + data.push(`${year}-${month}-${day}`); + i--; + } while (i >= 0); + return data; + } - TotalBean = async () => { - const options = { - headers: { - Accept: "application/json,text/plain, */*", - "Content-Type": "application/x-www-form-urlencoded", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-cn", - Connection: "keep-alive", - Cookie: this.JDCookie.cookie, - Referer: "https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2", - "User-Agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1", - }, - }; - const url = "https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2"; - const request = new Request(url, { method: "POST" }); - request.body = options.body; - request.headers = options.headers; + TotalBean = async () => { + const options = { + headers: { + Accept: 'application/json,text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'zh-cn', + Connection: 'keep-alive', + Cookie: this.JDCookie.cookie, + Referer: 'https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2', + 'User-Agent': + 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', + }, + }; + const url = 'https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2'; + const request = new Request(url, { method: 'POST' }); + request.body = options.body; + request.headers = options.headers; - const response = await request.loadJSON(); - if (response.retcode === 0) { - this.beanCount = response.base.jdNum; - } else { - console.log("京东服务器返回空数据"); - } - return response; - }; + const response = await request.loadJSON(); + if (response.retcode === 0) { + this.beanCount = response.base.jdNum; + } else { + console.log('京东服务器返回空数据'); + } + return response; + }; - getJingBeanBalanceDetail = async (page) => { - try { - const options = { - url: `https://bean.m.jd.com/beanDetail/detail.json`, - body: `page=${page}`, - headers: { - "X-Requested-With": `XMLHttpRequest`, - Connection: `keep-alive`, - "Accept-Encoding": `gzip, deflate, br`, - "Content-Type": `application/x-www-form-urlencoded; charset=UTF-8`, - Origin: `https://bean.m.jd.com`, - "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`, - Cookie: this.JDCookie.cookie, - Host: `bean.m.jd.com`, - Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, - "Accept-Language": `zh-cn`, - Accept: `application/json, text/javascript, */*; q=0.01`, - }, - }; - return await this.$request.post(options.url, options); - } catch (e) { - console.log(e); - } - }; + getJingBeanBalanceDetail = async (page) => { + try { + const options = { + url: `https://bean.m.jd.com/beanDetail/detail.json`, + body: `page=${page}`, + headers: { + 'X-Requested-With': `XMLHttpRequest`, + Connection: `keep-alive`, + 'Accept-Encoding': `gzip, deflate, br`, + 'Content-Type': `application/x-www-form-urlencoded; charset=UTF-8`, + Origin: `https://bean.m.jd.com`, + '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`, + Cookie: this.JDCookie.cookie, + Host: `bean.m.jd.com`, + Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, + 'Accept-Language': `zh-cn`, + Accept: `application/json, text/javascript, */*; q=0.01`, + }, + }; + return await this.$request.post(options.url, options); + } catch (e) { + console.log(e); + } + }; - transforJSON = (str) => { - if (typeof str == "string") { - try { - return JSON.parse(str); - } catch (e) { - console.log(e); - return []; - } - } - console.log("It is not a string!"); - }; + transforJSON = (str) => { + if (typeof str == 'string') { + try { + return JSON.parse(str); + } catch (e) { + console.log(e); + return []; + } + } + console.log('It is not a string!'); + }; - setContainer = async (container, { icon, text, desc }) => { - container.layoutVertically(); - container.centerAlignContent(); + setContainer = async (container, { icon, text, desc }) => { + container.layoutVertically(); + container.centerAlignContent(); - const viewer = container.addStack(); - viewer.size = new Size(90, 25); - const jdD_icon = await this.$request.get(icon, "IMG"); - const imageElemView = viewer.addImage(jdD_icon); - imageElemView.centerAlignImage(); - imageElemView.imageSize = new Size(25, 25); - container.addSpacer(10); + const viewer = container.addStack(); + viewer.size = new Size(90, 25); + const jdD_icon = await this.$request.get(icon, 'IMG'); + const imageElemView = viewer.addImage(jdD_icon); + imageElemView.centerAlignImage(); + imageElemView.imageSize = new Size(25, 25); + container.addSpacer(10); - const textview = container.addStack(); - textview.size = new Size(90, 30); - const titleTextItem = textview.addText(text); - titleTextItem.font = Font.boldSystemFont(22); - titleTextItem.textColor = new Color("#ffef03"); - titleTextItem.centerAlignText(); + const textview = container.addStack(); + textview.size = new Size(90, 30); + const titleTextItem = textview.addText(text); + titleTextItem.font = Font.boldSystemFont(22); + titleTextItem.textColor = new Color('#ffef03'); + titleTextItem.centerAlignText(); - const descView = container.addStack(); - descView.size = new Size(90, 30); - const descTextItem = descView.addText(desc); - descTextItem.textColor = this.widgetColor; - descTextItem.font = Font.lightSystemFont(16); - descTextItem.centerAlignText(); + const descView = container.addStack(); + descView.size = new Size(90, 30); + const descTextItem = descView.addText(desc); + descTextItem.textColor = this.widgetColor; + descTextItem.font = Font.lightSystemFont(16); + descTextItem.centerAlignText(); - return container; - }; + return container; + }; - setWidget = async (widget) => { - const body = widget.addStack(); - body.centerAlignContent(); - body.url = "https://bean.m.jd.com/"; - const letfContainer = body.addStack(); - await this.setContainer(letfContainer, { - icon: - "https://raw.githubusercontent.com/dompling/Scriptable/master/JDDou/jdd.png", - text: `${this.beanCount}`, - desc: "当前京豆", - }); - body.addSpacer(); - const centerContainer = body.addStack(); - await this.setContainer(centerContainer, { - icon: - "https://raw.githubusercontent.com/dompling/Scriptable/master/JDDou/jdd.png", - text: `+${this.incomeBean}`, - desc: "昨日收入", - }); - body.addSpacer(); - const rightContainer = body.addStack(); - await this.setContainer(rightContainer, { - icon: - "https://raw.githubusercontent.com/dompling/Scriptable/master/JDDou/jdd.png", - text: `${this.expenseBean}`, - desc: "昨日支出", - }); - return widget; - }; + setWidget = async (widget) => { + const body = widget.addStack(); + body.centerAlignContent(); + body.url = 'https://bean.m.jd.com/'; + const letfContainer = body.addStack(); + await this.setContainer(letfContainer, { + icon: 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png', + text: `${this.beanCount}`, + desc: '当前京豆', + }); + body.addSpacer(); + const centerContainer = body.addStack(); + await this.setContainer(centerContainer, { + icon: 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png', + text: `+${this.incomeBean}`, + desc: '昨日收入', + }); + body.addSpacer(); + const rightContainer = body.addStack(); + await this.setContainer(rightContainer, { + icon: 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDDou/jdd.png', + text: `${this.expenseBean}`, + desc: '昨日支出', + }); + return widget; + }; - renderSmall = async (w) => { - return await this.renderLarge(w); - }; + renderSmall = async (w) => { + return await this.renderLarge(w); + }; - renderLarge = async (w) => { - const text = w.addText("暂不支持"); - text.font = Font.boldSystemFont(20); - text.textColor = this.widgetColor; - return w; - }; + renderLarge = async (w) => { + const text = w.addText('暂不支持'); + text.font = Font.boldSystemFont(20); + text.textColor = this.widgetColor; + return w; + }; - renderMedium = async (w) => { - return await this.setWidget(w); - }; + renderMedium = async (w) => { + return await this.setWidget(w); + }; - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderJDHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name); - } - widget.addSpacer(20); - if (this.widgetFamily === "medium") { - widget.url = - "https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean"; - await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - await this.renderLarge(widget); - } else { - await this.renderSmall(widget); - } - return widget; - } + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + const header = widget.addStack(); + if (this.widgetFamily !== 'small') { + await this.renderJDHeader(header); + } else { + await this.renderHeader(header, this.logo, this.name); + } + widget.addSpacer(20); + if (this.widgetFamily === 'medium') { + widget.url = + 'https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean'; + await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + await this.renderLarge(widget); + } else { + await this.renderSmall(widget); + } + return widget; + } - JDRun = (filename, args) => { - if (config.runsInApp) { - this.registerAction("设置背景图", this.setWidgetBackground); - this.registerAction("输入京东 CK", this.inputJDck); - this.registerAction("选择京东 CK", this.actionSettings); - } - let _md5 = this.md5(filename + this.en); - this.CACHE_KEY = `cache_${_md5}`; - this.JDindex = parseInt(args.widgetParameter) || undefined; - this.logo = "https://raw.githubusercontent.com/Orz-3/task/master/jd.png"; - try { - this.JDCookie = this.settings[this.en] || { - cookie: "", - userName: "", - }; - if (this.JDindex !== undefined) { - this.JDCookie = this.settings.JDAccount[this.JDindex]; - } - if (!this.JDCookie.cookie) { - throw "京东 CK 获取失败"; - } - return true; - } catch (e) { - this.notify("错误提示", e); - return false; - } - }; + renderJDHeader = async (header) => { + header.centerAlignContent(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + header.addSpacer(); + const headerMore = header.addStack(); + headerMore.url = 'https://home.m.jd.com/myJd/home.action'; + headerMore.setPadding(1, 10, 1, 10); + headerMore.cornerRadius = 10; + headerMore.backgroundColor = new Color('#fff', 0.5); + const textItem = headerMore.addText(this.JDCookie.userName); + textItem.font = Font.boldSystemFont(12); + textItem.textColor = this.widgetColor; + textItem.lineLimit = 1; + textItem.rightAlignText(); + return header; + }; - renderJDHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(this.JDCookie.userName); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; + jdWebView = async () => { + const webView = new WebView(); + const url = + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; + await webView.loadURL(url); + await webView.present(false); + const req = new Request( + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', + ); + req.method = 'POST'; + req.body = + 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; + await req.loadJSON(); + const cookies = req.response.cookies; + const account = { username: '', cookie: '' }; + const cookie = []; + cookies.forEach((item) => { + const value = `${item.name}=${item.value}`; + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { + account.username = item.value; + cookie.push(value); + } + }); + account.cookie = cookie.join('; '); + console.log(account); - // 加载京东 Ck 节点列表 - _loadJDCk = async () => { - try { - const CookiesData = await this.getCache("CookiesJD"); - if (CookiesData) { - this.CookiesData = this.transforJSON(CookiesData); - } - const CookieJD = await this.getCache("CookieJD"); - if (CookieJD) { - const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; - const ck1 = { - cookie: CookieJD, - userName, - }; - this.CookiesData.push(ck1); - } - const Cookie2JD = await this.getCache("Cookie2JD"); - if (Cookie2JD) { - const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; - const ck2 = { - cookie: Cookie2JD, - userName, - }; - this.CookiesData.push(ck2); - } - return true; - } catch (e) { - console.log(e); - this.CookiesData = []; - return false; - } - }; + if (account.cookie) { + this.settings = { ...this.settings, ...account }; + this.saveSettings(false); + console.log(`${this.name}: cookie获取成功,请关闭窗口!`); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); + } + }; - async inputJDck() { - const a = new Alert(); - a.title = "京东账号 Ck"; - a.message = "手动输入京东 Ck"; - a.addTextField("昵称", this.JDCookie.userName); - a.addTextField("Cookie", this.JDCookie.cookie); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.JDCookie.userName = a.textFieldValue(0); - this.JDCookie.cookie = a.textFieldValue(1); - // 保存到本地 - this.settings[this.en] = this.JDCookie; - this.saveSettings(); - } + JDRun = (filename, args) => { + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', + ]); + if (index === 0) { + await this.jdWebView(); + } else { + await this.setAlertInput('账号设置', '京东账号 Ck', { + username: '昵称', + cookie: 'Cookie', + }); + } + }); + this.registerAction('代理缓存', this.actionSettings); + } + let _md5 = this.md5(filename + this.en); + this.CACHE_KEY = `cache_${_md5}`; + this.JDindex = + typeof args.widgetParameter === 'string' + ? parseInt(args.widgetParameter) + : false; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png'; + try { + const cookieData = this.settings.cookieData; + if (this.JDindex !== false && cookieData[this.JDindex]) { + this.JDCookie = cookieData[this.JDindex]; + } else { + this.JDCookie.userName = this.settings.username; + this.JDCookie.cookie = this.settings.cookie; + } + if (!this.JDCookie.cookie) throw '京东 CK 获取失败'; + this.JDCookie.userName = decodeURI(this.JDCookie.userName); + return true; + } catch (e) { + this.notify('错误提示', e); + return false; + } + }; - async actionSettings() { - try { - const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; - // 如果是节点,则先远程获取 - this.CookiesData.map((t) => { - const r = new UITableRow(); - r.addText(t.userName); - r.onSelect = (n) => { - this.settings[this.en] = t; - this.saveSettings(); - }; - table.addRow(r); - }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; - if (this.settings[this.en]) { - body += ",或者使用当前选中Ck:" + this.settings[this.en].userName; - } - this.notify(this.name, body); - table.present(false); - } catch (e) { - this.notify(this.name, e); - } - } + // 加载京东 Ck 节点列表 + _loadJDCk = async () => { + try { + const CookiesData = await this.getCache('CookiesJD'); + this.CookiesData = []; + if (CookiesData) { + this.CookiesData = this.transforJSON(CookiesData); + } + const CookieJD = await this.getCache('CookieJD'); + if (CookieJD) { + const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; + const ck1 = { + cookie: CookieJD, + userName, + }; + this.CookiesData.push(ck1); + } + const Cookie2JD = await this.getCache('CookieJD2'); + if (Cookie2JD) { + const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; + const ck2 = { + cookie: Cookie2JD, + userName, + }; + this.CookiesData.push(ck2); + } + return true; + } catch (e) { + console.log(e); + this.CookiesData = []; + return false; + } + }; + + async actionSettings() { + try { + const table = new UITable(); + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; + // 如果是节点,则先远程获取 + this.settings.cookieData = this.CookiesData; + this.saveSettings(false); + this.CookiesData.map((t, index) => { + const r = new UITableRow(); + r.addText(`parameter:${index} ${t.userName}`); + r.onSelect = (n) => { + this.settings.username = t.userName; + this.settings.cookie = t.cookie; + this.saveSettings(); + }; + table.addRow(r); + }); + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; + if (this.settings.cookie) { + body += ',或者使用当前选中Ck:' + this.settings.username; + } + this.notify(this.name, body); + table.present(false); + } catch (e) { + console.log(e); + } + } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/JDDouBarChart.js b/Scripts/JDDouBarChart.js deleted file mode 100644 index 41d59a6..0000000 --- a/Scripts/JDDouBarChart.js +++ /dev/null @@ -1,443 +0,0 @@ -// Variables used by Scriptable. -// These must be at the very top of the file. Do not edit. -// icon-color: cyan; icon-glyph: chart-line; - -// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); - -// @组件代码开始 -class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "京东豆收支"; - this.en = "JDDouBarChart"; - this.JDRun(module.filename, args); - } - - drawContext = new DrawContext(); - - forceCache = false; // 重置缓存 - rangeDay = 5; // 天数范围配置 - widgetFamily = "medium"; - rangeTimer = {}; - timerKeys = []; - isRender = false; - prefix = "boxjs.net"; - JDCookie = { - cookie: "", - userName: "", - }; - CookiesData = []; - beanCount = 0; - widgetHeight = 267; - widgetWidth = 720; - lineWeight = 2; // 线的宽度 - vertLineWeight = 50; // 竖线的宽度 - graphLow = 214; - graphHeight = 125; - spaceBetweenDays = 115; // 间距 - - accentColor1 = new Color("#33cc33", 1); - accentColor2 = new Color("#75f9c9", 1); - accentColor3 = Color.red(); - - drawTextR(text, rect, color, font) { - this.drawContext.setFont(font); - this.drawContext.setTextColor(color); - this.drawContext.drawTextInRect(new String(text).toString(), rect); - } - - drawLine(point1, point2, width, color) { - const path = new Path(); - path.move(point1); - path.addLine(point2); - this.drawContext.addPath(path); - this.drawContext.setStrokeColor(color); - this.drawContext.setLineWidth(width); - this.drawContext.strokePath(); - } - - init = async () => { - try { - // await this.TotalBean(); - this.rangeTimer = this.getDay(this.rangeDay); - if (Keychain.contains(this.CACHE_KEY) && !this.forceCache) { - const data = JSON.parse(Keychain.get(this.CACHE_KEY)); - Object.keys(this.rangeTimer).forEach((key) => { - this.rangeTimer[key] = data[key]; - }); - const date = new Date(); - const year = date.getFullYear(); - let month = date.getMonth() + 1; - month = month >= 10 ? month : `0${month}`; - let day = date.getDate(); - day = day >= 10 ? day : `0${day}`; - const today = `${year}-${month}-${day}`; - this.rangeTimer[today] = 0; - this.timerKeys = [today]; - } else { - this.timerKeys = Object.keys(this.rangeTimer); - } - await this.getAmountData(); - } catch (e) { - console.log(e); - } - }; - - getAmountData = async () => { - let i = 0, - page = 1; - do { - const response = await this.getJingBeanBalanceDetail(page); - const result = response.code === "0"; - console.log(`第${page}页:${result ? "请求成功" : "请求失败"}`); - if (response && result) { - page++; - let detailList = response.jingDetailList; - if (detailList && detailList.length > 0) { - for (let item of detailList) { - const dates = item.date.split(" "); - if (this.timerKeys.indexOf(dates[0]) > -1) { - const amount = Number(item.amount); - this.rangeTimer[dates[0]] += amount; - } else { - i = 1; - this.isRender = true; - Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); - break; - } - } - } - } - } while (i === 0); - }; - - TotalBean = async () => { - const options = { - headers: { - Accept: "application/json,text/plain, */*", - "Content-Type": "application/x-www-form-urlencoded", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-cn", - Connection: "keep-alive", - Cookie: this.JDCookie.cookie, - Referer: "https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2", - "User-Agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1", - }, - }; - const url = "https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2"; - const request = new Request(url, { method: "POST" }); - request.body = options.body; - request.headers = options.headers; - - const response = await request.loadJSON(); - if (response.retcode === 0) { - this.beanCount = response.base.jdNum; - } else { - console.log("京东服务器返回空数据"); - } - return response; - }; - - getDay(dayNumber) { - let data = {}; - let i = dayNumber; - do { - const today = new Date(); - const year = today.getFullYear(); - const targetday_milliseconds = today.getTime() - 1000 * 60 * 60 * 24 * i; - today.setTime(targetday_milliseconds); //注意,这行是关键代码 - let month = today.getMonth() + 1; - month = month >= 10 ? month : `0${month}`; - let day = today.getDate(); - day = day >= 10 ? day : `0${day}`; - data[`${year}-${month}-${day}`] = 0; - i--; - } while (i >= 0); - return data; - } - - getJingBeanBalanceDetail = async (page) => { - try { - const options = { - url: `https://bean.m.jd.com/beanDetail/detail.json`, - body: `page=${page}`, - headers: { - "X-Requested-With": `XMLHttpRequest`, - Connection: `keep-alive`, - "Accept-Encoding": `gzip, deflate, br`, - "Content-Type": `application/x-www-form-urlencoded; charset=UTF-8`, - Origin: `https://bean.m.jd.com`, - "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`, - Cookie: this.JDCookie.cookie, - Host: `bean.m.jd.com`, - Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, - "Accept-Language": `zh-cn`, - Accept: `application/json, text/javascript, */*; q=0.01`, - }, - }; - - return await this.$request.post(options.url, options); - } catch (e) { - console.log(e); - } - }; - - - drawImage = async () => { - this.drawContext.size = new Size(this.widgetWidth, this.widgetHeight); - this.drawContext.opaque = false; - - let min, max, diff; - const rangeKeys = Object.keys(this.rangeTimer); - for (let i = 0; i < rangeKeys.length; i++) { - const key = rangeKeys[i]; - let aux = this.rangeTimer[key]; - min = aux < min || min == undefined ? aux : min; - max = aux > max || max == undefined ? aux : max; - } - - diff = max - min; - - const maxHeight = this.graphLow - this.graphHeight; - - const startY = this.graphHeight - maxHeight + 20; - // Vertical Line - const axisX = new Point(2, 0); - const axisY = new Point(0, this.graphLow - startY); - this.drawLine(axisX, axisY, 4, this.accentColor1); - - const axisW = new Point(this.widgetWidth, this.graphLow - startY); - this.drawLine(axisY, axisW, 4, this.accentColor1); - - - const highestIndex = rangeKeys.length - 1; - for (let i = 0, j = highestIndex; i < rangeKeys.length; i++, j--) { - const rangeKey = rangeKeys[i]; - const date = rangeKey.split("-"); - const day = `${date[1]}.${date[2]}`; - const cases = this.rangeTimer[rangeKey]; - - const delta = (cases - min) / diff; - // if (i < highestIndex) { - // const nextCases = this.rangeTimer[rangeKeys[i + 1]]; - // if (nextCases) { - // const nextDelta = (nextCases - min) / diff; - // const point1 = new Point( - // this.spaceBetweenDays * i + 50, - // this.graphLow - this.graphHeight * delta, - // ); - // const point2 = new Point( - // this.spaceBetweenDays * (i + 1) + 50, - // this.graphLow - this.graphHeight * nextDelta, - // ); - // this.drawLine(point1, point2, this.lineWeight, this.accentColor1); - // } - // } - // Vertical Line - const point1 = new Point( - this.spaceBetweenDays * i + 50, - (this.graphLow - this.graphHeight * delta) - startY, - ); - // const cRect = new Rect(this.spaceBetweenDays * i + 50 - 5, this.graphLow - 2.5 - this.graphHeight * delta, 5, 5); - // this.drawContext.setStrokeColor(this.widgetColor); - // this.drawContext.strokeEllipse(cRect); - const point2Y = cases > 0 ? this.graphLow - startY : this.graphLow - 20; - const point2 = new Point(this.spaceBetweenDays * i + 50, point2Y); - this.drawLine(point1, point2, this.vertLineWeight, cases > 0 ? this.accentColor2 : this.accentColor3); - - const calculation = (this.graphLow - 40 - this.graphHeight * delta); - const casesRectY = cases > 0 ? calculation - 50 : this.graphLow; - const casesRect = new Rect( - this.spaceBetweenDays * i + 30, - casesRectY, - 60, - 23, - ); - - const dayRect = new Rect( - this.spaceBetweenDays * i + 27, - cases > 0 ? point2Y + 10 : point2Y - 80, - 60, - 23, - ); - - const color = cases > 0 ? this.widgetColor : Color.red(); - this.drawTextR(cases, casesRect, color, Font.systemFont(22)); - this.drawTextR(day, dayRect, color, Font.systemFont(22)); - } - }; - - renderSmall = async (w) => { - return await this.renderLarge(w); - }; - - renderLarge = async (w) => { - w.addSpacer(); - const text = w.addText("暂不支持"); - text.font = Font.boldSystemFont(20); - text.textColor = this.widgetColor; - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - const header = widget.addStack(); - if (this.widgetFamily !== "small") { - await this.renderJDHeader(header); - } else { - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - } - widget.addSpacer(20); - if (this.widgetFamily === "medium") { - await this.drawImage(); - const chart = widget.addStack(); - chart.addSpacer(); - const graphLine = chart.addStack(); - graphLine.url = - "https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean"; - graphLine.size = new Size(this.widgetWidth / 2.4, this.widgetHeight / 2.2); - graphLine.addImage(this.drawContext.getImage()); - chart.addSpacer(); - } else if (this.widgetFamily === "large") { - widget.addSpacer(); - await this.renderLarge(widget); - } else { - widget.addSpacer(); - await this.renderSmall(widget); - } - return widget; - } - - renderJDHeader = async (header) => { - header.centerAlignContent(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - header.addSpacer(); - const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; - headerMore.setPadding(1, 10, 1, 10); - headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); - const textItem = headerMore.addText(`${this.JDCookie.userName}`); - textItem.font = Font.boldSystemFont(12); - textItem.textColor = this.widgetColor; - textItem.lineLimit = 1; - textItem.rightAlignText(); - return header; - }; - - - JDRun = (filename, args) => { - if (config.runsInApp) { - this.registerAction("设置背景图", this.setWidgetBackground); - this.registerAction("输入京东 CK", this.inputJDck); - this.registerAction("选择京东 CK", this.actionSettings); - } - let _md5 = this.md5(filename + this.en); - this.CACHE_KEY = `cache_${_md5}`; - this.JDindex = parseInt(args.widgetParameter) || undefined; - this.logo = "https://raw.githubusercontent.com/Orz-3/task/master/jd.png"; - try { - this.JDCookie = this.settings[this.en] || { - cookie: "", - userName: "", - }; - if (this.JDindex !== undefined) { - this.JDCookie = this.settings.JDAccount[this.JDindex]; - } - if (!this.JDCookie.cookie) { - throw "京东 CK 获取失败"; - } - return true; - } catch (e) { - this.notify("错误提示", e); - return false; - } - }; - - // 加载京东 Ck 节点列表 - _loadJDCk = async () => { - try { - const CookiesData = await this.getCache("CookiesJD"); - if (CookiesData) { - this.CookiesData = this.transforJSON(CookiesData); - } - const CookieJD = await this.getCache("CookieJD"); - if (CookieJD) { - const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; - const ck1 = { - cookie: CookieJD, - userName, - }; - this.CookiesData.push(ck1); - } - const Cookie2JD = await this.getCache("Cookie2JD"); - if (Cookie2JD) { - const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; - const ck2 = { - cookie: Cookie2JD, - userName, - }; - this.CookiesData.push(ck2); - } - return true; - } catch (e) { - console.log(e); - this.CookiesData = []; - return false; - } - }; - - async inputJDck() { - const a = new Alert(); - a.title = "京东账号 Ck"; - a.message = "手动输入京东 Ck"; - a.addTextField("昵称", this.JDCookie.userName); - a.addTextField("Cookie", this.JDCookie.cookie); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.JDCookie.userName = a.textFieldValue(0); - this.JDCookie.cookie = a.textFieldValue(1); - // 保存到本地 - this.settings[this.en] = this.JDCookie; - this.saveSettings(); - } - - async actionSettings() { - try { - const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; - // 如果是节点,则先远程获取 - this.CookiesData.map((t) => { - const r = new UITableRow(); - r.addText(t.userName); - r.onSelect = (n) => { - this.settings[this.en] = t; - this.saveSettings(); - }; - table.addRow(r); - }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; - if (this.settings[this.en]) { - body += ",或者使用当前选中Ck:" + this.settings[this.en].userName; - } - this.notify(this.name, body); - table.present(false); - } catch (e) { - this.notify(this.name, e); - } - } -} - -// @组件代码结束 -// await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/JDDouK.js b/Scripts/JDDouK.js index c33ef7d..44b70bb 100644 --- a/Scripts/JDDouK.js +++ b/Scripts/JDDouK.js @@ -3,77 +3,143 @@ // icon-color: cyan; icon-glyph: chart-line; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = "京东豆收支"; - this.en = "JDDouK"; + this.name = '京东豆收支'; + this.en = 'JDDouK'; this.rangeDay = 5; // 天数范围配置 + this.maxDate = parseInt(this.settings.maxDate) || 12; // 显示最大天数 + this.forceCache = false; // 重置缓存 this.JDRun(module.filename, args); } drawContext = new DrawContext(); - - forceCache = false; // 重置缓存 - - widgetFamily = "medium"; + widgetFamily = 'medium'; rangeTimer = {}; timerKeys = []; isRender = false; - prefix = "boxjs.net"; JDCookie = { - cookie: "", - userName: "", + cookie: '', + userName: '', }; CookiesData = []; beanCount = 0; - widgetHeight = 248; - widgetWidth = 650; - lineWeight = 2; // 线的宽度 - vertLineWeight = 0.5; // 竖线的宽度 - graphLow = 200; - graphHeight = 120; - spaceBetweenDays = 45; // 间距 - - accentColor1 = new Color("#33cc33", 1); - accentColor2 = Color.lightGray(); - - drawTextR(text, rect, color, font) { - this.drawContext.setFont(font); - this.drawContext.setTextColor(color); - this.drawContext.drawTextInRect(new String(text).toString(), rect); - } - drawLine(point1, point2, width, color) { - const path = new Path(); - path.move(point1); - path.addLine(point2); - this.drawContext.addPath(path); - this.drawContext.setStrokeColor(color); - this.drawContext.setLineWidth(width); - this.drawContext.strokePath(); - } + chartConfig = (labels = [], datas = []) => { + const color = `#${this.widgetColor.hex}`; + let template; + let path = this.FILE_MGR.documentsDirectory(); + const name = `/${this.en}Template`; + path = path + name + '.js'; + if (this.FILE_MGR.fileExists(path)) { + template = require('.' + name); + } else { + template = ` +{ + 'type': 'bar', + 'data': { + 'labels': __LABELS__, + 'datasets': [ + { + type: 'line', + backgroundColor: '#fff', + borderColor: getGradientFillHelper('vertical', ['#c8e3fa', '#e62490']), + 'borderWidth': 2, + pointRadius: 5, + 'fill': false, + 'data': __DATAS__, + }, + ], + }, + 'options': { + plugins: { + datalabels: { + display: true, + align: 'top', + color: __COLOR__, + font: { + size: '16' + } + }, + }, + layout: { + padding: { + left: 0, + right: 0, + top: 30, + bottom: 5 + } + }, + responsive: true, + maintainAspectRatio: true, + 'legend': { + 'display': false, + }, + 'title': { + 'display': false, + }, + scales: { + xAxes: [ // X 轴线 + { + gridLines: { + display: false, + color: __COLOR__, + }, + ticks: { + display: true, + fontColor: __COLOR__, + fontSize: '16', + }, + }, + ], + yAxes: [ + { + ticks: { + display: false, + beginAtZero: true, + fontColor: __COLOR__, + }, + gridLines: { + borderDash: [7, 5], + display: false, + color: __COLOR__, + }, + }, + ], + }, + }, + }`; + const content = `// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: ellipsis-v; +const template = \`${template}\`; +module.exports = template;`; + this.FILE_MGR.writeString(path, content); + } + template = template.replaceAll('__COLOR__', `'${color}'`); + template = template.replace('__LABELS__', `${JSON.stringify(labels)}`); + template = template.replace('__DATAS__', `${JSON.stringify(datas)}`); + return template; + }; init = async () => { try { - await this.TotalBean(); + if (!this.JDCookie.cookie) return; if (Keychain.contains(this.CACHE_KEY) && !this.forceCache) { this.rangeTimer = JSON.parse(Keychain.get(this.CACHE_KEY)); const timerKeys = Object.keys(this.rangeTimer); - if (timerKeys.length >= 12) { - delete this.rangeTimer[timerKeys[0]]; + if (timerKeys.length >= this.maxDate) { + for (let i = 0; i < timerKeys.length - this.maxDate; i++) { + delete this.rangeTimer[timerKeys[i]]; + } Keychain.set(this.CACHE_KEY, JSON.stringify(this.rangeTimer)); } - this.spaceBetweenDays = - (this.widgetWidth - 15 - this.spaceBetweenDays * timerKeys.length) / - timerKeys.length + - this.spaceBetweenDays; - const date = new Date(); const year = date.getFullYear(); let month = date.getMonth() + 1; @@ -98,14 +164,18 @@ class Widget extends DmYY { page = 1; do { const response = await this.getJingBeanBalanceDetail(page); - const result = response.code === "0"; - console.log(`第${page}页:${result ? "请求成功" : "请求失败"}`); + const result = response.code === '0'; + console.log(`第${page}页:${result ? '请求成功' : '请求失败'}`); + if (response.code === '3') { + i = 1; + console.log(response); + } if (response && result) { page++; let detailList = response.jingDetailList; if (detailList && detailList.length > 0) { for (let item of detailList) { - const dates = item.date.split(" "); + const dates = item.date.split(' '); if (this.timerKeys.indexOf(dates[0]) > -1) { const amount = Number(item.amount); this.rangeTimer[dates[0]] += amount; @@ -121,34 +191,6 @@ class Widget extends DmYY { } while (i === 0); }; - TotalBean = async () => { - const options = { - headers: { - Accept: "application/json,text/plain, */*", - "Content-Type": "application/x-www-form-urlencoded", - "Accept-Encoding": "gzip, deflate, br", - "Accept-Language": "zh-cn", - Connection: "keep-alive", - Cookie: this.JDCookie.cookie, - Referer: "https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2", - "User-Agent": - "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1", - }, - }; - const url = "https://wq.jd.com/user/info/QueryJDUserInfo?sceneval=2"; - const request = new Request(url, { method: "POST" }); - request.body = options.body; - request.headers = options.headers; - - const response = await request.loadJSON(); - if (response.retcode === 0) { - this.beanCount = response.base.jdNum; - } else { - console.log("京东服务器返回空数据"); - } - return response; - }; - getDay(dayNumber) { let data = {}; let i = dayNumber; @@ -173,91 +215,38 @@ class Widget extends DmYY { url: `https://bean.m.jd.com/beanDetail/detail.json`, body: `page=${page}`, headers: { - "X-Requested-With": `XMLHttpRequest`, - Connection: `keep-alive`, - "Accept-Encoding": `gzip, deflate, br`, - "Content-Type": `application/x-www-form-urlencoded; charset=UTF-8`, - Origin: `https://bean.m.jd.com`, - "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`, + Accept: 'application/json,text/plain, */*', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept-Encoding': 'gzip, deflate, br', + 'Accept-Language': 'zh-cn', + Connection: 'keep-alive', Cookie: this.JDCookie.cookie, - Host: `bean.m.jd.com`, - Referer: `https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean`, - "Accept-Language": `zh-cn`, - Accept: `application/json, text/javascript, */*; q=0.01`, + Referer: 'https://wqs.jd.com/my/jingdou/my.shtml?sceneval=2', + 'User-Agent': + 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1', }, }; - return await this.$request.post(options.url, options); } catch (e) { console.log(e); } }; - drawImage = async () => { - this.drawContext.size = new Size(this.widgetWidth, this.widgetHeight); - this.drawContext.opaque = false; - - let min, max, diff; - const rangeKeys = Object.keys(this.rangeTimer); - for (let i = 0; i < rangeKeys.length; i++) { - const key = rangeKeys[i]; - let aux = this.rangeTimer[key]; - min = aux < min || min == undefined ? aux : min; - max = aux > max || max == undefined ? aux : max; - } - diff = max - min; - const highestIndex = rangeKeys.length - 1; - - for (let i = 0, j = highestIndex; i < rangeKeys.length; i++, j--) { - const rangeKey = rangeKeys[i]; - const date = rangeKey.split("-"); - const day = `${date[1]}.${date[2]}`; - const rangeItem = this.rangeTimer[rangeKey]; - - const cases = rangeItem; - const delta = (cases - min) / diff; - - if (i < highestIndex) { - const nextRange = this.rangeTimer[rangeKeys[i + 1]]; - const nextCases = nextRange; - const nextDelta = (nextCases - min) / diff; - const point1 = new Point( - this.spaceBetweenDays * i + 50, - this.graphLow - this.graphHeight * delta - ); - const point2 = new Point( - this.spaceBetweenDays * (i + 1) + 50, - this.graphLow - this.graphHeight * nextDelta - ); - this.drawLine(point1, point2, this.lineWeight, this.accentColor1); - } - - // Vertical Line - const point1 = new Point( - this.spaceBetweenDays * i + 50, - this.graphLow - this.graphHeight * delta - ); - const point2 = new Point(this.spaceBetweenDays * i + 50, this.graphLow); - this.drawLine(point1, point2, this.vertLineWeight, this.accentColor2); - - const casesRect = new Rect( - this.spaceBetweenDays * i + 20, - this.graphLow - 40 - this.graphHeight * delta, - 60, - 23 - ); - - const dayRect = new Rect( - this.spaceBetweenDays * i + 27, - this.graphLow + 10, - 60, - 23 - ); - - const color = cases > 0 ? this.widgetColor : Color.red(); - this.drawTextR(cases, casesRect, color, Font.systemFont(22)); - this.drawTextR(day, dayRect, color, Font.systemFont(22)); - } + createChart = async () => { + let labels = [], + data = []; + Object.keys(this.rangeTimer).forEach((month) => { + const value = this.rangeTimer[month]; + const arrMonth = month.split('-'); + labels.push(`${arrMonth[1]}.${arrMonth[2]}`); + data.push(value); + }); + const chartStr = this.chartConfig(labels, data); + console.log(chartStr); + const url = `https://quickchart.io/chart?w=580&h=190&f=png&c=${encodeURIComponent( + chartStr, + )}`; + return await this.$request.get(url, 'IMG'); }; renderSmall = async (w) => { @@ -265,7 +254,7 @@ class Widget extends DmYY { }; renderLarge = async (w) => { - const text = w.addText("暂不支持"); + const text = w.addText('暂不支持'); text.font = Font.boldSystemFont(20); text.textColor = this.widgetColor; }; @@ -277,27 +266,19 @@ class Widget extends DmYY { async render() { await this.init(); const widget = new ListWidget(); - if (await this.getWidgetBackgroundImage(widget)) { - this.widgetColor = Color.white(); - } - widget.addStack(20); + await this.getWidgetBackgroundImage(widget); const header = widget.addStack(); - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { await this.renderJDHeader(header); } else { await this.renderHeader(header, this.logo, this.name, this.widgetColor); } - if (this.widgetFamily === "medium") { - await this.drawImage(); - const chart = widget.addStack(); - chart.addSpacer(); - const kGraph = chart.addStack(); - kGraph.url = - "https://bean.m.jd.com/beanDetail/index.action?resourceValue=bean"; - kGraph.size = new Size(this.widgetWidth / 2.2, this.widgetHeight / 2); - kGraph.addImage(this.drawContext.getImage()); - chart.addSpacer(); - } else if (this.widgetFamily === "large") { + widget.addSpacer(10); + const stackChart = widget.addStack(); + if (this.widgetFamily === 'medium') { + const chart = await this.createChart(); + stackChart.addImage(chart); + } else if (this.widgetFamily === 'large') { await this.renderLarge(widget); } else { await this.renderSmall(widget); @@ -306,30 +287,108 @@ class Widget extends DmYY { return widget; } + opacity(str, opacity = 0.8) { + let sColor = str.toLowerCase(); + let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; + if (sColor && reg.test(sColor)) { + if (sColor.length === 4) { + let sColorNew = '#'; + for (let i = 1; i < 4; i += 1) { + sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1)); + } + sColor = sColorNew; + } + let sColorChange = []; + for (let i = 1; i < 7; i += 2) { + sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2))); + } + return `rgba(${sColorChange.join(',')},${opacity})`; + } + return sColor; + } + + jdWebView = async () => { + const webView = new WebView(); + const url = + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; + await webView.loadURL(url); + await webView.present(false); + const req = new Request( + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', + ); + req.method = 'POST'; + req.body = + 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; + await req.loadJSON(); + const cookies = req.response.cookies; + const account = { username: '', cookie: '' }; + const cookie = []; + cookies.forEach((item) => { + const value = `${item.name}=${item.value}`; + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { + account.username = item.value; + cookie.push(value); + } + }); + account.cookie = cookie.join('; '); + console.log(account); + + if (account.cookie) { + this.settings = { ...this.settings, ...account }; + this.saveSettings(false); + console.log(`${this.name}: cookie获取成功,请关闭窗口!`); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); + } + }; + JDRun = (filename, args) => { if (config.runsInApp) { - this.registerAction("设置背景图", this.setWidgetBackground); - this.registerAction("输入京东 CK", this.inputJDck); - this.registerAction("选择京东 CK", this.actionSettings); + this.registerAction('显示天数', async () => { + await this.setAlertInput('设置显示天数周期范围', false, { + maxDate: '天数', + }); + }); + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', + ]); + if (index === 0) { + await this.jdWebView(); + } else { + await this.setAlertInput('账号设置', '京东账号 Ck', { + username: '昵称', + cookie: 'Cookie', + }); + } + }); + this.registerAction('代理缓存', this.actionSettings); } let _md5 = this.md5(filename + this.en); - this.CACHE_KEY = `cache_${_md5}`; - this.JDindex = parseInt(args.widgetParameter) || undefined; - this.logo = "https://raw.githubusercontent.com/Orz-3/task/master/jd.png"; + + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png'; + this.JDindex = + typeof args.widgetParameter === 'string' + ? parseInt(args.widgetParameter) + : false; try { - this.JDCookie = this.settings[this.en] || { - cookie: "", - userName: "", - }; - if (this.JDindex !== undefined) { - this.JDCookie = this.settings.JDAccount[this.JDindex]; - } - if (!this.JDCookie.cookie) { - throw "京东 CK 获取失败"; + const cookieData = this.settings.cookieData; + if (this.JDindex !== false && cookieData[this.JDindex]) { + this.JDCookie = cookieData[this.JDindex]; + } else { + this.JDCookie.userName = this.settings.username; + this.JDCookie.cookie = this.settings.cookie; } + if (!this.JDCookie.cookie) throw '京东 CK 获取失败'; + this.JDCookie.userName = decodeURI(this.JDCookie.userName); + this.CACHE_KEY = `cache_${_md5}_` + this.JDCookie.userName; + return true; } catch (e) { - this.notify("错误提示", e); + this.notify('错误提示', e); return false; } }; @@ -339,10 +398,10 @@ class Widget extends DmYY { await this.renderHeader(header, this.logo, this.name, this.widgetColor); header.addSpacer(); const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; + headerMore.url = 'https://home.m.jd.com/myJd/home.action'; headerMore.setPadding(1, 10, 1, 10); headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); + headerMore.backgroundColor = new Color('#fff', 0.5); const textItem = headerMore.addText(this.JDCookie.userName); textItem.font = Font.boldSystemFont(12); textItem.textColor = this.widgetColor; @@ -354,11 +413,10 @@ class Widget extends DmYY { // 加载京东 Ck 节点列表 _loadJDCk = async () => { try { - const CookiesData = await this.getCache("CookiesJD"); - if (CookiesData) { - this.CookiesData = this.transforJSON(CookiesData); - } - const CookieJD = await this.getCache("CookieJD"); + const CookiesData = await this.getCache('CookiesJD'); + this.CookiesData = []; + if (CookiesData) this.CookiesData = this.transforJSON(CookiesData); + const CookieJD = await this.getCache('CookieJD'); if (CookieJD) { const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; const ck1 = { @@ -367,7 +425,7 @@ class Widget extends DmYY { }; this.CookiesData.push(ck1); } - const Cookie2JD = await this.getCache("Cookie2JD"); + const Cookie2JD = await this.getCache('CookieJD2'); if (Cookie2JD) { const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; const ck2 = { @@ -384,49 +442,35 @@ class Widget extends DmYY { } }; - async inputJDck() { - const a = new Alert(); - a.title = "京东账号 Ck"; - a.message = "手动输入京东 Ck"; - a.addTextField("昵称", this.JDCookie.userName); - a.addTextField("Cookie", this.JDCookie.cookie); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.JDCookie.userName = a.textFieldValue(0); - this.JDCookie.cookie = a.textFieldValue(1); - // 保存到本地 - this.settings[this.en] = this.JDCookie; - this.saveSettings(); - } - async actionSettings() { try { const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; // 如果是节点,则先远程获取 - this.CookiesData.map((t) => { + this.settings.cookieData = this.CookiesData; + this.saveSettings(false); + this.CookiesData.map((t, index) => { const r = new UITableRow(); - r.addText(t.userName); + r.addText(`parameter:${index} ${t.userName}`); r.onSelect = (n) => { - this.settings[this.en] = t; + this.settings.username = t.userName; + this.settings.cookie = t.cookie; this.saveSettings(); }; table.addRow(r); }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; - if (this.settings[this.en]) { - body += ",或者使用当前选中Ck:" + this.settings[this.en].userName; + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; + if (this.settings.cookie) { + body += ',或者使用当前选中Ck:' + this.settings.username; } this.notify(this.name, body); table.present(false); } catch (e) { - this.notify(this.name, e); + console.log(e); } } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/JDWuLiu.js b/Scripts/JDWuLiu.js index 5573b4c..a5429eb 100644 --- a/Scripts/JDWuLiu.js +++ b/Scripts/JDWuLiu.js @@ -3,35 +3,33 @@ // icon-color: teal; icon-glyph: truck; // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { constructor(arg) { super(arg); - this.name = "京东物流"; - this.en = "JDWuLiu"; + this.name = '京东物流'; + this.en = 'JDWuLiu'; this.JDRun(module.filename, args); } - prefix = "boxjs.net"; JDCookie = { - cookie: "", - userName: "", + cookie: '', + userName: '', }; CookiesData = []; orderList = []; - logistics = []; opts = { headers: { Accept: `*/*`, Connection: `keep-alive`, Host: `wq.jd.com`, - "Accept-Language": "zh-cn", - "Accept-Encoding": "gzip, deflate, br", - "User-Agent": `Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`, + 'Accept-Language': 'zh-cn', + 'Accept-Encoding': 'gzip, deflate, br', + 'User-Agent': `Mozilla/5.0 (iPhone; CPU iPhone OS 14_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1`, }, }; @@ -46,8 +44,8 @@ class Widget extends DmYY { getOrderList = async () => { const url = - "https://wq.jd.com/bases/orderlist/list?order_type=2&start_page=1&page_size=10"; - const request = new Request(""); + 'https://wq.jd.com/bases/orderlist/list?order_type=2&start_page=1&page_size=10'; + const request = new Request(''); request.url = url; request.headers = { ...this.opts.headers, @@ -58,7 +56,7 @@ class Widget extends DmYY { try { data = response.orderList.filter((item) => { return ( - item.stateInfo.stateCode === "15" || item.stateInfo.stateCode === "9" + item.stateInfo.stateCode === '15' || item.stateInfo.stateCode === '9' ); }); } catch (e) { @@ -81,18 +79,22 @@ class Widget extends DmYY { }; setListCell = async (cell, data) => { - const { productList = [], orderDetailLink = "", progressInfo = {} } = data; + const { + productList = [], + orderDetailLink = '', + progressInfo = {}, + stateInfo, + } = data; const product = productList[0]; let body = cell.addStack(); body.url = orderDetailLink; - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { const imageView = body.addStack(); imageView.size = new Size(75, 75); imageView.cornerRadius = 5; imageView.url = product.skuLink; - const image = await this.$request.get(product.image, "IMG"); - imageView.backgroundImage = image; - body.addSpacer(); + imageView.backgroundImage = await this.$request.get(product.image, 'IMG'); + body.addSpacer(10); } const textView = body.addStack(); @@ -106,28 +108,36 @@ class Widget extends DmYY { textView.addSpacer(); - const timerText = textView.addText(progressInfo.tip); + const stackDesc = textView.addStack(); + const timerText = stackDesc.addText(progressInfo.tip); timerText.font = Font.lightSystemFont(12); timerText.textColor = this.widgetColor; + // timerText.lineLimit = 1; + stackDesc.addSpacer(); + const statusText = stackDesc.addText(stateInfo.stateName); + statusText.font = Font.lightSystemFont(12); + timerText.textColor = this.widgetColor; timerText.lineLimit = 1; - textView.addSpacer(); + + textView.addSpacer(10); + cell.addSpacer(10); return cell; }; setWidget = async (body) => { body.url = - "https://wqs.jd.com/order/orderlist_merge.shtml?sceneval=2&orderType=waitReceipt"; + 'https://wqs.jd.com/order/orderlist_merge.shtml?sceneval=2&orderType=waitReceipt'; const container = body.addStack(); container.layoutVertically(); if (!this.orderList.length) { - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { const bg = await this.$request.get( - "https://raw.githubusercontent.com/dompling/Scriptable/master/JDWuLiu/cart.png", - "IMG" + 'https://gitee.com/scriptableJS/Scriptable/raw/master/JDWuLiu/cart.png', + 'IMG', ); const cartView = container.addStack(); - if (this.widgetFamily === "large") { + if (this.widgetFamily === 'large') { cartView.size = new Size(285, 150); } else { cartView.size = new Size(285, 50); @@ -136,8 +146,8 @@ class Widget extends DmYY { cartView.addImage(bg); } let textItem = container.addStack(); - if (this.widgetFamily !== "small") textItem.size = new Size(300, 20); - textItem.addText("空空如也"); + if (this.widgetFamily !== 'small') textItem.size = new Size(300, 20); + textItem.addText('空空如也'); textItem.textColor = this.widgetColor; textItem.font = Font.boldSystemFont(15); textItem.lineLimit = 1; @@ -145,7 +155,7 @@ class Widget extends DmYY { return body; } for (let index = 0; index < this.orderList.length; index++) { - if (this.widgetFamily !== "large" && index === 1) { + if (this.widgetFamily !== 'large' && index === 1) { return body; } if (index === 3) { @@ -181,58 +191,30 @@ class Widget extends DmYY { const widget = new ListWidget(); await this.getWidgetBackgroundImage(widget); const header = widget.addStack(); - if (this.widgetFamily !== "small") { + if (this.widgetFamily !== 'small') { await this.renderJDHeader(header); } else { await this.renderHeader(header, this.logo, this.name, this.widgetColor); } widget.addSpacer(20); - if (this.widgetFamily === "medium") { + if (this.widgetFamily === 'medium') { return await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { + } else if (this.widgetFamily === 'large') { return await this.renderLarge(widget); } else { return await this.renderSmall(widget); } } - JDRun = (filename, args) => { - if (config.runsInApp) { - this.registerAction("设置背景图", this.setWidgetBackground); - this.registerAction("输入京东 CK", this.inputJDck); - this.registerAction("选择京东 CK", this.actionSettings); - } - let _md5 = this.md5(filename + this.en); - this.CACHE_KEY = `cache_${_md5}`; - this.JDindex = parseInt(args.widgetParameter) || undefined; - this.logo = "https://raw.githubusercontent.com/Orz-3/task/master/jd.png"; - try { - this.JDCookie = this.settings[this.en] || { - cookie: "", - userName: "", - }; - if (this.JDindex !== undefined) { - this.JDCookie = this.settings.JDAccount[this.JDindex]; - } - if (!this.JDCookie.cookie) { - throw "京东 CK 获取失败"; - } - return true; - } catch (e) { - this.notify("错误提示", e); - return false; - } - }; - renderJDHeader = async (header) => { header.centerAlignContent(); await this.renderHeader(header, this.logo, this.name, this.widgetColor); header.addSpacer(); const headerMore = header.addStack(); - headerMore.url = "https://home.m.jd.com/myJd/home.action"; + headerMore.url = 'https://home.m.jd.com/myJd/home.action'; headerMore.setPadding(1, 10, 1, 10); headerMore.cornerRadius = 10; - headerMore.backgroundColor = new Color("#fff", 0.5); + headerMore.backgroundColor = new Color('#fff', 0.5); const textItem = headerMore.addText(this.JDCookie.userName); textItem.font = Font.boldSystemFont(12); textItem.textColor = this.widgetColor; @@ -241,14 +223,94 @@ class Widget extends DmYY { return header; }; + jdWebView = async () => { + const webView = new WebView(); + const url = + 'https://mcr.jd.com/credit_home/pages/index.html?btPageType=BT&channelName=024'; + await webView.loadURL(url); + await webView.present(false); + const req = new Request( + 'https://ms.jr.jd.com/gw/generic/bt/h5/m/firstScreenNew', + ); + req.method = 'POST'; + req.body = + 'reqData={"clientType":"ios","clientVersion":"13.2.3","deviceId":"","environment":"3"}'; + await req.loadJSON(); + const cookies = req.response.cookies; + const account = { username: '', cookie: '' }; + const cookie = []; + cookies.forEach((item) => { + const value = `${item.name}=${item.value}`; + if (item.name === 'pt_key') cookie.push(value); + if (item.name === 'pt_pin') { + account.username = item.value; + cookie.push(value); + } + }); + account.cookie = cookie.join('; '); + console.log(account); + + if (account.cookie) { + this.settings = { ...this.settings, ...account }; + this.saveSettings(false); + console.log(`${this.name}: cookie获取成功,请关闭窗口!`); + this.notify(this.name, 'cookie获取成功,请关闭窗口!'); + } + }; + + JDRun = (filename, args) => { + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('账号设置', async () => { + const index = await this.generateAlert('设置账号信息', [ + '网站登录', + '手动输入', + ]); + if (index === 0) { + await this.jdWebView(); + } else { + await this.setAlertInput('账号设置', '京东账号 Ck', { + username: '昵称', + cookie: 'Cookie', + }); + } + }); + this.registerAction('代理缓存', this.actionSettings); + } + let _md5 = this.md5(filename + this.en); + this.CACHE_KEY = `cache_${_md5}`; + this.JDindex = + typeof args.widgetParameter === 'string' + ? parseInt(args.widgetParameter) + : false; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png'; + try { + const cookieData = this.settings.cookieData; + if (this.JDindex !== false && cookieData[this.JDindex]) { + this.JDCookie = cookieData[this.JDindex]; + } else { + this.JDCookie.userName = this.settings.username; + this.JDCookie.cookie = this.settings.cookie; + } + if (!this.JDCookie.cookie) throw '京东 CK 获取失败'; + this.JDCookie.userName = decodeURI(this.JDCookie.userName); + return true; + } catch (e) { + this.notify('错误提示', e); + return false; + } + }; + // 加载京东 Ck 节点列表 _loadJDCk = async () => { try { - const CookiesData = await this.getCache("CookiesJD"); + const CookiesData = await this.getCache('CookiesJD'); + this.CookiesData = []; if (CookiesData) { this.CookiesData = this.transforJSON(CookiesData); } - const CookieJD = await this.getCache("CookieJD"); + const CookieJD = await this.getCache('CookieJD'); if (CookieJD) { const userName = CookieJD.match(/pt_pin=(.+?);/)[1]; const ck1 = { @@ -257,7 +319,7 @@ class Widget extends DmYY { }; this.CookiesData.push(ck1); } - const Cookie2JD = await this.getCache("Cookie2JD"); + const Cookie2JD = await this.getCache('CookieJD2'); if (Cookie2JD) { const userName = Cookie2JD.match(/pt_pin=(.+?);/)[1]; const ck2 = { @@ -274,49 +336,35 @@ class Widget extends DmYY { } }; - async inputJDck() { - const a = new Alert(); - a.title = "京东账号 Ck"; - a.message = "手动输入京东 Ck"; - a.addTextField("昵称", this.JDCookie.userName); - a.addTextField("Cookie", this.JDCookie.cookie); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - this.JDCookie.userName = a.textFieldValue(0); - this.JDCookie.cookie = a.textFieldValue(1); - // 保存到本地 - this.settings[this.en] = this.JDCookie; - this.saveSettings(); - } - async actionSettings() { try { const table = new UITable(); - if (!(await this._loadJDCk())) throw "BoxJS 数据读取失败"; + if (!(await this._loadJDCk())) throw 'BoxJS 数据读取失败'; // 如果是节点,则先远程获取 - this.CookiesData.map((t) => { + this.settings.cookieData = this.CookiesData; + this.saveSettings(false); + this.CookiesData.map((t, index) => { const r = new UITableRow(); - r.addText(t.userName); + r.addText(`parameter:${index} ${t.userName}`); r.onSelect = (n) => { - this.settings[this.en] = t; + this.settings.username = t.userName; + this.settings.cookie = t.cookie; this.saveSettings(); }; table.addRow(r); }); - let body = "京东 Ck 缓存成功,根据下标选择相应的 Ck"; - if (this.settings[this.en]) { - body += ",或者使用当前选中Ck:" + this.settings[this.en].userName; + let body = '京东 Ck 缓存成功,根据下标选择相应的 Ck'; + if (this.settings.cookie) { + body += ',或者使用当前选中Ck:' + this.settings.username; } this.notify(this.name, body); table.present(false); } catch (e) { - this.notify(this.name, e); + console.log(e); } } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", true); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/Oild.js b/Scripts/Oild.js new file mode 100644 index 0000000..f2303cd --- /dev/null +++ b/Scripts/Oild.js @@ -0,0 +1,429 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: oil; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +const enumConfig = { + 89: '汽油', + 92: '汽油', + 95: '汽油', + 98: '汽油', + 0: '柴油', +}; + +const provinces = [ + '北京', + '天津', + '上海', + '重庆', + '河北', + '山西', + '辽宁', + '吉林', + '黑龙江', + '江苏', + '浙江', + '安徽', + '福建', + '江西', + '山东', + '河南', + '湖北', + '湖南', + '广东', + '海南', + '四川', + '贵州', + '云南', + '陕西', + '甘肃', + '青海', + '台湾', + '内蒙古', + '广西', + '西藏', + '宁夏', + '新疆', + '香港', + '澳门', +]; + +const squareColor = '#8165AC'; +const processColor = [`#7517F8`, `#E323FF`]; +const processBarColor = [`#4da1ff`, `#4dffdf`]; +const processBarBgColor = '#5A5A89'; + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = 'oilWidget'; + this.name = '油价'; + if (config.runsInApp) { + this.registerAction( + '油价设置', + () => { + return this.setAlertInput('油价设置', '设置类型,多个英文逗号分割', { + oilNumber: '92,95,89,0', + }); + }, + { name: 'oilcan', color: '#E64C57' } + ); + + this.registerAction({ + icon: { name: 'location.circle', color: '#8BE39D' }, + type: 'switch', + title: '自动定位', + val: 'location', + }); + + this.registerAction({ + icon: { name: 'mappin.and.ellipse.circle', color: '#5BBFF6' }, + type: 'select', + title: '油价省份', + val: 'province', + placeholder: '地区', + options: provinces, + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + dataSource = { + DIM_ID: '', + DIM_DATE: '', + CITYNAME: '', + V0: 0, + V95: 0, + V92: 0, + V89: 0, + ZDE0: 0, + ZDE92: 0, + ZDE95: 0, + ZDE89: 0, + QE0: 0, + QE92: 0, + QE95: 0, + QE89: 0, + }; + + init = async () => { + try { + this.oilNumber = `${this.settings.oilNumber || '92'}`.split(','); + const oilNumber = []; + this.oilNumber.forEach((item) => { + if (item && ['92', '95', '89', '0'].includes(item)) { + oilNumber.push(item); + } + }); + this.oilNumber = oilNumber; + if (!this.oilNumber.length) + return this.notify(this.name, '请设置油价型号:92,95,89,0'); + } catch (error) { + return console.log('请设置正确的油价'); + } + this.province = this.settings.province; + this.location = this.settings.location; + + if (this.settings.dataSource) { + this.dataSource = this.settings.dataSource[0]; + } else { + await this.cacheData(); + } + this.cacheData(); + }; + + cacheData = async () => { + try { + await this.getOilPrice(); + } catch (e) { + console.log(e); + } + }; + + getOilPrice = async () => { + if (this.location) { + const location = await Location.current(); + const locationText = await Location.reverseGeocode( + location.latitude, + location.longitude + ); + const { administrativeArea = '' } = locationText[0] || {}; + this.province = administrativeArea.replace('省', ''); + this.settings.province = this.province; + this.saveSettings(false); + } + + if (!this.province) return this.notify(this.name, '请设置油价省份!!'); + console.log(`当前省份:${this.province}`); + this.province = `(CITYNAME="${this.province}")`; + const time = Date.now(); + const url = `https://datacenter-web.eastmoney.com/api/data/v1/get?reportName=RPTA_WEB_YJ_JH&columns=ALL&filter=${encodeURIComponent( + this.province + )}&sortColumns=DIM_DATE&sortTypes=-1&pageNumber=1&pageSize=6&source=WEB&_=${time}`; + + const options = { url }; + const response = await this.$request.post(options); + console.log(response); + if (response.result) { + this.dataSource = response.result.data[0]; + this.settings.dataSource = response.result.data; + this.saveSettings(false); + } + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + renderSmall = async (w) => { + const headerStack = w.addStack(); + const dollarImage = SFSymbol.named(`yensign.circle`).image; + headerStack.centerAlignContent(); + const dollarWidgetImg = headerStack.addImage(dollarImage); + dollarWidgetImg.tintColor = new Color('#f5222d'); + dollarWidgetImg.imageSize = new Size(24, 24); + + headerStack.addSpacer(); + + w.addSpacer(); + const topStack = w.addStack(); + const topLStack = topStack.addStack(); + topLStack.layoutVertically(); + topLStack.addSpacer(); + topLStack.bottomAlignContent(); + const oilPrice = (this.dataSource[`V${this.oilNumber[0]}`] || '').toFixed( + 2 + ); + const timer = (this.dataSource.DIM_DATE.split(' ')[0] || '').split('-'); + const oilNumText = topLStack.addText(`${oilPrice}`); + oilNumText.textColor = this.widgetColor; + oilNumText.minimumScaleFactor = 0.6; + oilNumText.font = Font.boldSystemFont(38); + topLStack.addSpacer(); + + const oilStatus = this.dataSource[`ZDE${this.oilNumber[0]}`] > 0; + const oilZdeImage = SFSymbol.named( + oilStatus ? 'arrow.up' : 'arrow.up' + ).image; + topStack.addSpacer(); + const topRStack = topStack.addStack(); + topRStack.addSpacer(); + topRStack.layoutVertically(); + topRStack.bottomAlignContent(); + const zdeStack = topRStack.addStack(); + zdeStack.setPadding(2, 6, 0, 0); + const oilZdeWidgetImg = zdeStack.addImage(oilZdeImage); + oilZdeWidgetImg.tintColor = new Color(oilStatus ? '#f5222d' : '#a0d911'); + oilZdeWidgetImg.imageSize = new Size(16, 16); + + const timerText = topRStack.addText(`${timer[2]}/${timer[1]}`); + timerText.textColor = this.widgetColor; + timerText.font = Font.systemFont(12); + topRStack.addSpacer(); + + w.addSpacer(); + + const bottomStack = w.addStack(); + bottomStack.addSpacer(); + const rightText = bottomStack.addText( + `${this.oilNumber[0]}#${enumConfig[this.oilNumber[0]]}` + ); + oilNumText.textColor = this.widgetColor; + rightText.font = Font.boldSystemFont(18); + rightText.textOpacity = 0.3; + rightText.rightAlignText(); + bottomStack.addSpacer(5); + return w; + }; + + rowData = (w, oilNumber) => { + const oilPrice = (this.dataSource[`V${oilNumber}`] || '').toFixed(2); + const oilZde = (this.dataSource[`ZDE${oilNumber}`] || '').toFixed(2); + const oilType = enumConfig[oilNumber] || ''; + + const colStack = w.addStack(); + + const oilNumberStack = colStack.addStack(); + const colSize = new Size(40, 40); + oilNumberStack.size = colSize; + oilNumberStack.cornerRadius = 8; + oilNumberStack.borderWidth = 4; + oilNumberStack.borderColor = new Color(squareColor); + oilNumberStack.centerAlignContent(); + this.provideText(`${oilNumber}`, oilNumberStack, { + font: 'bold', + size: 26, + color: new Color(squareColor), + }); + + colStack.addSpacer(7); + + const oilInfoStack = colStack.addStack(); + oilInfoStack.size = new Size(65, colSize.height); + oilInfoStack.layoutVertically(); + oilInfoStack.addSpacer(); + this.provideText(`#${oilType}`, oilInfoStack, { + font: 'light', + size: 12, + color: this.widgetColor, + opacity: 0.5, + }); + + oilInfoStack.addSpacer(2); + + this.provideText(`${oilPrice}`, oilInfoStack, { + font: 'medium', + size: 18, + color: this.widgetColor, + }); + oilInfoStack.addSpacer(); + + const processStack = colStack.addStack(); + processStack.centerAlignContent(); + const processVerWidth = 10; + + let maxCount = 0; + const oilHistory = this.settings.dataSource.map((item) => { + const value = + (item[`ZDE${oilNumber}`] / item[`QE${oilNumber}`]).toFixed(2) * 100; + if (maxCount < value) maxCount = value; + return value; + }); + + maxCount = maxCount * 1.5; + + oilHistory.forEach((item) => { + const processItemStack = processStack.addStack(); + processItemStack.size = new Size(processVerWidth, colSize.height); + processItemStack.cornerRadius = processVerWidth / 2; + processItemStack.backgroundColor = new Color(processBarBgColor); + if (item > 0) processItemStack.addSpacer(); + processItemStack.layoutVertically(); + + const itemBarStack = processItemStack.addStack(); + itemBarStack.cornerRadius = processItemStack.cornerRadius; + itemBarStack.size = new Size( + processVerWidth, + colSize.height * (Math.abs(item) / maxCount) + ); + itemBarStack.backgroundGradient = this.gradient(processColor); + if (item < 0) processItemStack.addSpacer(); + processStack.addSpacer(); + }); + + colStack.addSpacer(); + + const oilZdeStack = colStack.addStack(); + + const oilZdeSize = new Size(80, 10); + + oilZdeStack.layoutVertically(); + oilZdeStack.size = new Size(oilZdeSize.width, colSize.height); + oilZdeStack.centerAlignContent(); + + const oilZdeValueStack = oilZdeStack.addStack(); + oilZdeValueStack.centerAlignContent(); + oilZdeValueStack.addSpacer(); + this.provideText(`${oilZde > 0 ? '+' : ''} ${oilZde}`, oilZdeValueStack, { + font: 'light', + size: 14, + color: this.widgetColor, + }); + + const oilZdeImage = SFSymbol.named( + oilZde > 0 ? 'arrow.up' : 'arrow.down' + ).image; + + oilZdeValueStack.addSpacer(10); + + const oilZdeWidgetImg = oilZdeValueStack.addImage(oilZdeImage); + oilZdeWidgetImg.tintColor = new Color(oilZde > 0 ? '#f5222d' : '#a0d911'); + oilZdeWidgetImg.imageSize = new Size(10, 10); + + oilZdeValueStack.addSpacer(); + + oilZdeStack.addSpacer(5); + + const oilZdeValue = Math.abs(parseFloat(oilZde)); + const processBarBgStack = oilZdeStack.addStack(); + + processBarBgStack.cornerRadius = 5; + processBarBgStack.size = oilZdeSize; + processBarBgStack.backgroundColor = new Color(processBarBgColor); + + if (oilZde < 0) processBarBgStack.addSpacer(); + + const processBarStack = processBarBgStack.addStack(); + + const linear = new LinearGradient(); + linear.colors = processBarColor.map((item) => new Color(item)); + linear.locations = [0, 0.5]; + linear.startPoint = new Point(0, 0); + linear.endPoint = new Point(1, 1); + + processBarStack.backgroundGradient = linear; + processBarStack.cornerRadius = oilZdeSize.height / 2; + processBarStack.size = new Size( + parseInt(oilZdeSize.width * oilZdeValue), + oilZdeSize.height + ); + + if (oilZde > 0) processBarBgStack.addSpacer(); + }; + + renderLarge = async (w) => { + return this.notSupport(w); + }; + + renderBorder = (stack) => { + stack.borderWidth = 1; + }; + + gradient = (color, config = { locations: [0, 0.5] }) => { + const linear = new LinearGradient(); + linear.colors = color.map((item) => new Color(item)); + linear.locations = config.locations; + return linear; + }; + + renderMedium = async (w) => { + w.addSpacer(); + this.oilNumber.forEach((oilNumber, index) => { + if (index > 2) return; + this.rowData(w, oilNumber); + w.addSpacer(); + }); + + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/PoisonCalendar.js b/Scripts/PoisonCalendar.js new file mode 100644 index 0000000..d53c770 --- /dev/null +++ b/Scripts/PoisonCalendar.js @@ -0,0 +1,231 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: yellow; icon-glyph: calendar-alt; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); +const { Calendar } = require('./Calendar'); +const $calendar = new Calendar(); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '毒汤日历'; + this.en = 'PoisonCalendar'; + this.Run(); + } + + cookie = ''; + date = new Date(); + format = new DateFormatter(); + + userInfo = {}; + dataSource = []; + baseUrl = 'http://www.dutangapp.cn'; + + init = async () => { + try { + await this.getUserInfo(); + await this.getDaysInfo(); + } catch (e) { + console.log(e); + } + }; + + getUserInfo = async () => { + try { + const url = `${this.baseUrl}/u/wx_login?code=&os=iOS&unid=${this.cookie}&version=3.5.2`; + const response = await this.$request.get(url); + if (response.code === 0) { + console.log('✅用户信息获取成功'); + const { data } = response; + this.userInfo = data; + } else { + console.log('❌用户信息获取失败'); + } + } catch (e) { + console.log('❌用户信息获取失败' + e); + } + }; + + getDaysInfo = async () => { + try { + const today = `${this.date.getFullYear()}-${ + this.date.getMonth() + 1 + }-${this.date.getDate()}`; + const url = `${this.baseUrl}/u/v2/days_info?days=${today}`; + console.log(url); + const response = await this.$request.get(url); + if (response.code === 0) { + console.log(`✅今日${this.name}获取成功`); + const { data } = response; + this.format.dateFormat = 'YYYY-MM-dd'; + const key = this.format.string(this.date); + this.dataSource = data[key].toxicList; + } else { + console.log(`❌今日${this.name}获取成功`); + } + } catch (e) { + console.log(`❌今日${this.name}获取成功` + e); + } + }; + + setAvatar = async (stack) => { + stack.size = new Size(50, 50); + stack.cornerRadius = 5; + const imgLogo = await this.$request.get(this.userInfo.avatar, 'IMG'); + const imgLogoItem = stack.addImage(imgLogo); + imgLogoItem.imageSize = new Size(50, 50); + return stack; + }; + + setTitleStack = (stack) => { + const textFormatNumber = this.textFormat.title; + textFormatNumber.color = this.backGroundColor; + const title = this.userInfo.nick; + textFormatNumber.size = + title.length > 20 || this.widgetFamily === 'small' ? 16 : 20; + const titleItem = this.provideText(title, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setPathStack = (stack) => { + const textFormatNumber = this.textFormat.defaultText; + textFormatNumber.color = new Color('#2481cc'); + textFormatNumber.size = 12; + this.format.dateFormat = 'HH:mm'; + let simpleText = '更新:' + this.format.string(this.date); + this.format.dateFormat = 'YYYY-MM-dd'; + const titleItem = this.provideText(simpleText, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setFooterCell = async (stack) => { + const dayInfos = this.getRandomArrayElements(this.dataSource || [], 1); + const info = dayInfos[0] || {}; + const title = this.textFormat.title; + title.color = this.backGroundColor; + title.size = 12; + const stackContent = stack.addStack(); + stackContent.layoutVertically(); + stackContent.centerAlignContent(); + stackContent.addSpacer(); + this.provideText(info.data, stackContent, title); + stackContent.addSpacer(); + }; + + setCalendar(stack) { + const today = this.format.string(this.date); + const todays = today.split('-'); + const response = $calendar.solar2lunar(todays[0], todays[1], todays[2]); + stack.layoutVertically(); + const stackCalendar = stack.addStack(); + stackCalendar.setPadding(5, 0, 5, 0); + stackCalendar.centerAlignContent(); + stackCalendar.cornerRadius = 4; + stackCalendar.backgroundColor = this.backGroundColor; + const title = this.textFormat.title; + title.color = this.widgetColor; + title.size = 24; + this.provideText(todays[2], stackCalendar, title); + const stackYear = stackCalendar.addStack(); + stackYear.layoutVertically(); + + const text = this.textFormat.defaultText; + text.color = this.widgetColor; + text.size = 8; + + const animal = $calendar.getAnimalZodiacToEmoji(response.Animal); + this.provideText(response.Animal + animal, stackYear, text); + stackYear.addSpacer(2); + this.provideText(response.IMonthCn, stackYear, text); + stackYear.addSpacer(2); + this.provideText(response.IDayCn, stackYear, text); + } + + renderSmall = async (w) => { + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.layoutVertically(); + const stackHeader = stackBody.addStack(); + stackHeader.setPadding(5, 10, 5, 10); + stackHeader.cornerRadius = 10; + stackHeader.backgroundColor = this.widgetColor; + + stackHeader.centerAlignContent(); + const stackLeft = stackHeader.addStack(); + await this.setAvatar(stackLeft); + stackHeader.addSpacer(20); + + const stackRight = stackHeader.addStack(); + stackRight.layoutVertically(); + this.setTitleStack(stackRight); + stackRight.addSpacer(5); + this.setPathStack(stackRight); + stackHeader.addSpacer(); + + const stackDay = stackHeader.addStack(); + this.setCalendar(stackDay); + + stackBody.addSpacer(); + + const stackFooter = stackBody.addStack(); + stackFooter.setPadding(10, 0, 10, 0); + stackFooter.cornerRadius = 10; + stackFooter.backgroundColor = this.widgetColor; + stackFooter.addSpacer(); + await this.setFooterCell(stackFooter); + stackFooter.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return w; + }; + + Run() { + if (config.runsInApp) { + const widgetInitConfig = { + cookie: '@DJT.unid', + }; + this.registerAction('账号设置', async () => { + await this.setAlertInput( + `${this.name}账号`, + '读取 BoxJS 缓存信息', + widgetInitConfig, + ); + }); + this.registerAction('代理缓存', async () => { + await this.setCacheBoxJSData(widgetInitConfig); + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + this.cookie = this.settings.cookie || this.cookie; + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", true); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/PoisonCalendarText.js b/Scripts/PoisonCalendarText.js new file mode 100644 index 0000000..ea64875 --- /dev/null +++ b/Scripts/PoisonCalendarText.js @@ -0,0 +1,196 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: yellow; icon-glyph: calendar-alt; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); +const { Calendar } = require('./Calendar'); +const $calendar = new Calendar(); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '毒汤日历'; + this.en = 'PoisonCalendar'; + this.Run(); + } + + cookie = ''; + date = new Date(); + format = new DateFormatter(); + + userInfo = {}; + dataSource = []; + baseUrl = 'http://www.dutangapp.cn'; + + init = async () => { + try { + await this.getUserInfo(); + await this.getDaysInfo(); + } catch (e) { + console.log(e); + } + }; + + getUserInfo = async () => { + try { + const url = `${this.baseUrl}/u/wx_login?code=&os=iOS&unid=${this.cookie}&version=3.5.2`; + const response = await this.$request.get(url); + if (response.code === 0) { + console.log('✅用户信息获取成功'); + const { data } = response; + this.userInfo = data; + } else { + console.log('❌用户信息获取失败'); + } + } catch (e) { + console.log('❌用户信息获取失败' + e); + } + }; + + getDaysInfo = async () => { + try { + // this.format.dateFormat = 'YYYY-MM-dd'; + const today = `${this.date.getFullYear()}-${ + this.date.getMonth() + 1 + }-${this.date.getDate()}`; + console.log(today); + const url = `${this.baseUrl}/u/v2/days_info?days=${today}`; + const response = await this.$request.get(url); + if (response.code === 0) { + console.log(`✅今日${this.name}获取成功`); + const { data } = response; + this.format.dateFormat = 'YYYY-MM-dd'; + const key = this.format.string(this.date); + this.dataSource = data[key].toxicList; + } else { + console.log(`❌今日${this.name}获取成功`); + } + } catch (e) { + console.log(`❌今日${this.name}获取成功` + e); + } + }; + + setAvatar = async (stack) => { + stack.size = new Size(50, 50); + stack.cornerRadius = 5; + const imgLogo = await this.$request.get(this.userInfo.avatar, 'IMG'); + const imgLogoItem = stack.addImage(imgLogo); + imgLogoItem.imageSize = new Size(50, 50); + return stack; + }; + + setTitleStack = (stack) => { + const textFormatNumber = this.textFormat.title; + textFormatNumber.color = this.backGroundColor; + const title = this.userInfo.nick; + textFormatNumber.size = + title.length > 20 || this.widgetFamily === 'small' ? 16 : 20; + const titleItem = this.provideText(title, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setPathStack = (stack) => { + const textFormatNumber = this.textFormat.defaultText; + textFormatNumber.color = new Color('#2481cc'); + textFormatNumber.size = 12; + this.format.dateFormat = 'HH:mm'; + let simpleText = '更新:' + this.format.string(this.date); + this.format.dateFormat = 'YYYY-MM-dd'; + const titleItem = this.provideText(simpleText, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setFooterCell = async (stack) => { + const dayInfos = this.getRandomArrayElements(this.dataSource || [], 1); + const info = dayInfos[0] || {}; + const title = this.textFormat.title; + title.color = this.backGroundColor; + title.size = 12; + const stackContent = stack.addStack(); + stackContent.layoutVertically(); + stackContent.centerAlignContent(); + stackContent.addSpacer(); + this.provideText(info.data, stackContent, title); + stackContent.addSpacer(); + }; + + setCalendar(stack) { + const today = this.format.string(this.date); + const todays = today.split('-'); + const response = $calendar.solar2lunar(todays[0], todays[1], todays[2]); + stack.layoutVertically(); + const stackCalendar = stack.addStack(); + stackCalendar.setPadding(5, 0, 5, 0); + stackCalendar.centerAlignContent(); + stackCalendar.cornerRadius = 4; + stackCalendar.backgroundColor = this.backGroundColor; + const title = this.textFormat.title; + title.color = this.widgetColor; + title.size = 24; + this.provideText(todays[2], stackCalendar, title); + const stackYear = stackCalendar.addStack(); + stackYear.layoutVertically(); + + const text = this.textFormat.defaultText; + text.color = this.widgetColor; + text.size = 8; + + const animal = $calendar.getAnimalZodiacToEmoji(response.Animal); + this.provideText(response.Animal + animal, stackYear, text); + stackYear.addSpacer(2); + this.provideText(response.IMonthCn, stackYear, text); + stackYear.addSpacer(2); + this.provideText(response.IDayCn, stackYear, text); + } + + renderSmall = async (w) => { + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + + const stackFooter = stackBody.addStack(); + stackFooter.setPadding(10, 0, 10, 0); + stackFooter.cornerRadius = 10; + stackFooter.backgroundColor = this.widgetColor; + stackFooter.addSpacer(); + await this.setFooterCell(stackFooter); + stackFooter.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return w; + }; + + Run() { + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", true); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/PriceWidgets.js b/Scripts/PriceWidgets.js new file mode 100644 index 0000000..fd60bf6 --- /dev/null +++ b/Scripts/PriceWidgets.js @@ -0,0 +1,332 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-blue; icon-glyph: dollar; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = ' btc'; + this.name = '比特币'; + + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'centsign.circle', color: '#feda31' }, + type: 'input', + title: '比特币种类', + desc: '设置关注种类', + placeholder: 'BTC,ETH,BNB', + val: 'btcType', + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + format = (str) => { + return parseInt(str) >= 10 ? str : `0${str}`; + }; + + endpoint = 'https://api.coingecko.com/api/v3'; + nomicsEndpoint = 'https://api.nomics.com/v1'; + + dataSource = []; + + init = async () => { + if (this.settings.dataSource && !config.runsInApp) { + this.dataSource = this.settings.dataSource; + } else { + await this.cacheData(this.settings.btcType); + } + this.cacheData(this.settings.btcType); + }; + + cacheData = async (params) => { + try { + const ids = await this.transforBtcType(params); + let response = await this.$request.get( + `${this.endpoint}/coins/markets?vs_currency=usd&ids=${ids}`, + 'STRING' + ); + this.dataSource = []; + response = JSON.parse(response); + if (!response.length) response = await this.getAllJson(); + if (ids) { + const idsData = ids.split(','); + idsData.forEach((id) => { + const it = response.find((item) => item.id === id); + if (it && this.dataSource.length < 6) { + this.dataSource.push({ + id: it.id, + name: it.name, + image: it.image, + symbol: it.symbol.toUpperCase(), + current_price: '' + it.current_price, + high_24h: it.high_24h, + low_24h: it.low_24h, + price_change_percentage_24h: it.price_change_percentage_24h, + last_updated: it.last_updated, + }); + } + }); + } else { + response.forEach((it, index) => { + if (index > 5) return; + this.dataSource.push({ + id: it.id, + name: it.name, + image: it.image, + symbol: it.symbol.toUpperCase(), + current_price: '' + it.current_price, + high_24h: it.high_24h, + low_24h: it.low_24h, + price_change_percentage_24h: it.price_change_percentage_24h, + last_updated: it.last_updated, + }); + }); + } + + this.settings.dataSource = this.dataSource; + this.saveSettings(false); + } catch (e) { + console.log(e); + return []; + } + }; + + transforBtcType = async (params) => { + let btcType; + if (params) btcType = params.split(','); + + const btcAll = await this.getAllJson(); + + if (!btcType) + return btcAll + .filter((item, index) => index < 6) + .map((item) => item.id) + .join(','); + + return btcType + .map((item) => { + const result = + btcAll.find((btc) => btc.symbol.toUpperCase() === item) || {}; + return result.id; + }) + .filter((item) => !!item) + .join(','); + }; + + getAllJson = async () => { + const cachePath = this.FILE_MGR.joinPath( + this.FILE_MGR.libraryDirectory(), + `${Script.name()}/datas` + ); + const filename = `${cachePath}/BTC.json`; + if (!this.FILE_MGR.fileExists(cachePath)) + this.FILE_MGR.createDirectory(cachePath, true); + + if (this.FILE_MGR.fileExists(filename)) { + const data = Data.fromFile(`${cachePath}/BTC.json`).toRawString(); + return JSON.parse(data); + } else { + const response = await this.$request.get( + `${this.endpoint}/coins/markets?vs_currency=usd&ids=` + ); + const data = Data.fromString(JSON.stringify(response)); + this.FILE_MGR.write(filename, data); + return response; + } + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + getSmallBg = async (url) => { + const webview = new WebView(); + let js = `const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = () => { + const { width, height } = img + canvas.width = width + canvas.height = height + ctx.globalAlpha = 0.3 + ctx.drawImage( + img, + -width / 2 + 50, + -height / 2 + 50, + width, + height + ) + const uri = canvas.toDataURL() + completion(uri); + }; + img.src = 'data:image/png;base64,${Data.fromPNG(url).toBase64String()}'`; + let image = await webview.evaluateJavaScript(js, true); + image = image.replace(/^data\:image\/\w+;base64,/, ''); + return Image.fromData(Data.fromBase64String(image)); + }; + + renderSmall = async (widget) => { + const market = this.dataSource[0] || {}; + + widget.url = `https://www.coingecko.com/en/coins/${market.id}`; + + const image = await this.renderImage(market.image); + const backgroundImg = await this.getSmallBg(image); + widget.backgroundColor = this.backGroundColor; + widget.backgroundImage = backgroundImg; + widget.setPadding(12, 12, 12, 12); + const coin = widget.addText(market.symbol.toUpperCase()); + coin.font = Font.heavySystemFont(24); + coin.textColor = this.widgetColor; + + coin.rightAlignText(); + const name = widget.addText(market.name); + name.font = Font.systemFont(10); + name.textColor = Color.gray(); + name.rightAlignText(); + widget.addSpacer(); + + const trend = widget.addText( + `${market.price_change_percentage_24h.toFixed(2)}%` + ); + trend.font = Font.semiboldSystemFont(16); + trend.textColor = + market.price_change_percentage_24h >= 0 ? Color.green() : Color.red(); + + trend.rightAlignText(); + const price = widget.addText(`$ ${market.current_price}`); + price.font = Font.boldSystemFont(28); + price.textColor = this.widgetColor; + price.rightAlignText(); + price.lineLimit = 1; + price.minimumScaleFactor = 0.1; + const history = widget.addText( + `H: ${market.high_24h}, L: ${market.low_24h}` + ); + history.font = Font.systemFont(10); + history.textColor = Color.gray(); + history.rightAlignText(); + history.lineLimit = 1; + history.minimumScaleFactor = 0.1; + return widget; + }; + + rowCell = async (rowStack, market) => { + rowStack.url = `https://www.coingecko.com/zh/coins/${market.id}`; + rowStack.layoutHorizontally(); + const image = await this.renderImage(market.image); + const iconImage = rowStack.addImage(image); + iconImage.imageSize = new Size(28, 28); + iconImage.cornerRadius = 14; + + rowStack.addSpacer(10); + + const centerStack = rowStack.addStack(); + centerStack.layoutVertically(); + + const topCenterStack = centerStack.addStack(); + topCenterStack.layoutHorizontally(); + + const titleText = topCenterStack.addText(market.symbol); + titleText.textColor = this.widgetColor; + titleText.font = this.provideFont('semibold', 16); + + topCenterStack.addSpacer(); + + const priceText = topCenterStack.addText(`$ ${market.current_price}`); + priceText.textColor = this.widgetColor; + priceText.font = this.provideFont('semibold', 15); + priceText.rightAlignText(); + + const bottomCenterStack = centerStack.addStack(); + bottomCenterStack.layoutHorizontally(); + + const subText = bottomCenterStack.addText(market.name); + subText.textColor = Color.gray(); + subText.font = this.provideFont('semibold', 10); + + bottomCenterStack.addSpacer(); + + const historyText = bottomCenterStack.addText( + `H: ${market.high_24h}, L: ${market.low_24h}` + ); + historyText.textColor = Color.gray(); + historyText.font = this.provideFont('semibold', 10); + historyText.rightAlignText(); + + rowStack.addSpacer(8); + + const rateStack = rowStack.addStack(); + rateStack.size = new Size(72, 28); + rateStack.centerAlignContent(); + rateStack.cornerRadius = 4; + rateStack.backgroundColor = + market.price_change_percentage_24h >= 0 ? Color.green() : Color.red(); + const rateText = rateStack.addText( + (market.price_change_percentage_24h >= 0 ? '+' : '') + + market.price_change_percentage_24h.toFixed(2) + + '%' + ); + rateText.textColor = new Color('#fff', 0.9); + rateText.font = this.provideFont('semibold', 14); + rateText.minimumScaleFactor = 0.01; + rateText.lineLimit = 1; + }; + + renderLarge = async (widget) => { + widget.setPadding(12, 12, 12, 12); + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + for (let index = 0; index < this.dataSource.length; index++) { + const item = this.dataSource[index]; + const rowCellStack = containerStack.addStack(); + await this.rowCell(rowCellStack, item); + if (index !== this.dataSource.length - 1) containerStack.addSpacer(); + } + return widget; + }; + + renderMedium = async (widget) => { + widget.setPadding(12, 12, 12, 12); + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + for (let index = 0; index < this.dataSource.length; index++) { + if (index > 2) return; + const item = this.dataSource[index]; + const rowCellStack = containerStack.addStack(); + await this.rowCell(rowCellStack, item); + if (index !== 2) containerStack.addSpacer(); + } + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + if (this.widgetFamily === 'small') await this.renderSmall(widget); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') await this.renderMedium(widget); + if (this.widgetFamily === 'large') await this.renderLarge(widget); + return widget; + } +} + +// @组件代码结束 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/Spotify.js b/Scripts/Spotify.js new file mode 100644 index 0000000..f1446b8 --- /dev/null +++ b/Scripts/Spotify.js @@ -0,0 +1,672 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-blue; icon-glyph: dollar; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); + +const logo = `https://open.spotifycdn.com/cdn/images/favicon.0f31d2ea.ico`; +const fontFamily = "AmericanTypewriter-Bold"; + +const api = { + me: `https://api.spotify.com/v1/me`, + accounts: `https://accounts.spotify.com/api/token`, + currentlyPlayingTrack: `https://api.spotify.com/v1/me/player/currently-playing`, + getTrack: `https://api.spotify.com/v1/tracks/11dFghVXANMlKmJXsNCbNl`, + palyState: `https://api.spotify.com/v1/me/player`, + playlists: `https://api.spotify.com/v1/me/playlists`, + following: `https://api.spotify.com/v1/me/following?type=artist`, +}; + +const scope = [ + "ugc-image-upload", + "playlist-read-collaborative", + "playlist-modify-private", + "playlist-modify-public", + "playlist-read-private", + "user-read-playback-position", + "user-read-recently-played", + "user-top-read", + "user-modify-playback-state", + "user-read-currently-playing", + "user-read-playback-state", + "user-read-private", + "user-read-email", + "user-library-modify", + "user-library-read", + "user-follow-modify", + "user-follow-read", + "streaming", + "app-remote-control", +]; + +const webUri = `https://accounts.spotify.com/zh-CN/login?continue=${encodeURIComponent( + "https://developer.spotify.com/documentation/web-api/reference/get-current-users-profile" +)}`; + +function generateRandomString(length) { + let text = ""; + let possible = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for (let i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + +function convertMillisecondsToHMS(milliseconds) { + // 计算总共有多少秒 + let totalSeconds = Math.floor(milliseconds / 1000); + + // 计算分钟数 + let minutes = Math.floor(totalSeconds / 60); + + // 剩余的秒数 + let seconds = totalSeconds - minutes * 60; + + if (minutes < 10) { + minutes = "0" + minutes; + } + if (seconds < 10) { + seconds = "0" + seconds; + } + + // 组合时分秒 + return minutes + ":" + seconds; +} + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = "Spotify"; + this.name = "声田音乐"; + this.SETTING_KEY = this.md5(this.en); + this._init(); + this.auth2 = args.queryParameters.code + ? args.queryParameters + : this.settings.auth2; + + this.settings.auth2 = this.auth2; + this.saveSettings(false); + + if (config.runsInApp) { + this.registerAction({ + icon: { + name: "airplayaudio.circle.fill", + color: "#65D46E", + }, + title: "登录", + val: "login", + name: "login", + dismissOnSelect: true, + onClick: () => { + return this.getWebToken(); + }, + }); + + this.registerAction({ + icon: { + name: "music.note", + color: "#65D46E", + }, + title: "客户端 ID", + val: "clientId", + name: "clientId", + type: "input", + dismissOnSelect: true, + }); + + this.registerAction("基础设置", this.setWidgetConfig); + } + } + + baseUri = ""; + accessToken = ""; + dataSource = { + currentlyPlayingTrack: {}, + playlists: {}, + me: {}, + following: {}, + }; + + init = async () => { + await this.getAccessToken(); + if (!this.settings.accessToken) return this.notify(this.name, "请登录!!"); + this.accessToken = `${this.settings.accessToken.token_type} ${this.settings.accessToken.access_token}`; + this.dataSource.playlists = await this.getApiRes(api.playlists); + this.dataSource.following = await this.getApiRes(api.following); + this.dataSource.me = await this.getApiRes(api.me); + this.dataSource.currentlyPlayingTrack = await this.getApiRes( + api.currentlyPlayingTrack + ); + }; + + getAccessToken = async () => { + const { accessToken } = this.settings; + if (accessToken) { + const diffTime = (new Date().getTime() - accessToken.time) / 1000; + if (diffTime > accessToken.expires_in) { + return this.refreshToken(); + } + } else if (this.auth2) { + if (!this.auth2.code) + return this.notify( + this.name, + "请登录!!!", + "https://developer.spotify.com" + ); + const { clientId, codeVerifier } = this.settings; + + const options = { + code: this.auth2.code, + client_id: clientId, + code_verifier: codeVerifier, + grant_type: "authorization_code", + redirect_uri: "scriptable:///run/Spotify?openEditor=true", + }; + + const body = Object.entries(options) + .map((item) => `${item[0]}=${item[1]}`) + .join("&"); + + const response = await this.$request.post(api.accounts, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: body, + }); + + console.log(response); + + if (!response.error) { + this.auth2 = null; + this.settings.auth2 = null; + this.settings.accessToken = { + ...this.settings.accessToken, + ...response, + time: new Date().getTime(), + }; + console.log( + `获取 Token 成功,有效期 ${response.expires_in / (60 * 10)} 分钟` + ); + this.saveSettings(); + } + } + }; + + refreshToken = async () => { + const { accessToken, clientId } = this.settings; + + const options = { + client_id: clientId, + grant_type: "refresh_token", + refresh_token: accessToken.refresh_token, + }; + + const body = Object.entries(options) + .map((item) => `${item[0]}=${item[1]}`) + .join("&"); + const response = await this.$request.post(api.accounts, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: this.accessToken, + }, + body: body, + }); + console.log("token 过期,更新"); + console.log(response); + if (!response.error) { + this.auth2 = null; + this.settings.auth2 = null; + this.settings.accessToken = { + ...this.settings.accessToken, + ...response, + time: new Date().getTime(), + }; + console.log( + `刷新 Token 成功,有效期 ${response.expires_in / (60 * 10)} 分钟` + ); + this.saveSettings(false); + } + }; + + getWebToken = async () => { + const clientId = this.settings.clientId; + if (!clientId) + this.notify( + this.name, + "请到 Spotify 官网申请开发者账号创建 APP", + "https://developer.spotify.com" + ); + + this.settings.codeVerifier = generateRandomString(128); + this.saveSettings(false); + + return WebView.loadURL( + `https://dompling.github.io/Spotify?clientId=${clientId}&codeVerifier=${ + this.settings.codeVerifier + }&scope=${encodeURIComponent(scope.join(" "))}` + ); + }; + + getApiRes = async (uri) => { + try { + const response = await this.$request.get(uri, { + headers: { Authorization: this.accessToken }, + }); + if (!response || response.error) + throw new Error(JSON.stringify(response)); + return response; + } catch (error) { + console.log(error); + return {}; + } + }; + + renderLarge = async (widget) => { + const { currentlyPlayingTrack, playlists, me, following } = this.dataSource; + if (currentlyPlayingTrack.is_playing) { + } else { + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + const logoStack = containerStack.addStack(); + + logoStack.centerAlignContent(); + logoStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = logoStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + logoImageWidget.centerAlignImage(); + + const meStack = containerStack.addStack(); + meStack.centerAlignContent(); + + const avatarImage = await this.$request.get( + me.images[1]?.url || me.images[0]?.url, + "IMG" + ); + + const avatarStack = meStack.addImage(avatarImage); + avatarStack.imageSize = new Size(64, 64); + avatarStack.cornerRadius = avatarStack.imageSize.height / 2; + + meStack.addSpacer(20); + + const infoStack = meStack.addStack(); + infoStack.layoutVertically(); + this.provideText(me.display_name, infoStack, { + size: 16, + font: fontFamily, + }); + infoStack.addSpacer(20); + + this.provideText(me.email, infoStack, { + size: 12, + font: fontFamily, + }); + + containerStack.addSpacer(20); + + const followStack = containerStack.addStack(); + followStack.centerAlignContent(); + this.provideText(`Following`, followStack, { + size: 16, + font: fontFamily, + }); + + followStack.addSpacer(); + + this.provideText(`${following.artists.total || 0}`, followStack, { + size: 16, + font: fontFamily, + }); + + containerStack.addSpacer(10); + + const followingItemStack = containerStack.addStack(); + followingItemStack.addSpacer(); + const itemSize = new Size(48, 48); + for (let index = 0; index < following.artists.items.length; index++) { + if (index === 4) break; + + const item = following.artists.items[index]; + const itemImage = await this.$request.get(item.images[0].url, "IMG"); + const itemImgStack = followingItemStack.addImage(itemImage); + itemImgStack.imageSize = itemSize; + itemImgStack.cornerRadius = itemSize.height / 2; + followingItemStack.addSpacer(); + } + + containerStack.addSpacer(); + + const playListStack = containerStack.addStack(); + + const listItems = playlists.items.reverse(); + const bottomItems = [ + listItems[0], + listItems[1], + listItems[2], + listItems[3], + ].filter((item) => !!item); + + playListStack.addSpacer(); + + for (const index in bottomItems) { + const item = bottomItems[index]; + const itemImage = await this.$request.get(item.images[0].url, "IMG"); + const itemImageStack = playListStack.addImage(itemImage); + itemImageStack.cornerRadius = 6; + itemImageStack.imageSize = new Size(68, 68); + playListStack.addSpacer(); + } + + containerStack.addSpacer(); + } + return widget; + }; + + renderMedium = async (widget) => { + const { currentlyPlayingTrack, playlists } = this.dataSource; + if (currentlyPlayingTrack.is_playing) { + const containerStack = widget.addStack(); + const leftStack = containerStack.addStack(); + leftStack.centerAlignContent(); + + const thumbImage = await this.$request.get( + currentlyPlayingTrack.item.album.images[0].url, + "IMG" + ); + leftStack.addSpacer(); + leftStack.size = new Size(140, 140); + const thumbImgStack = leftStack.addImage(thumbImage); + thumbImgStack.imageSize = leftStack.size; + thumbImgStack.cornerRadius = 12; + leftStack.addSpacer(); + + containerStack.addSpacer(20); + + const rightStack = containerStack.addStack(); + rightStack.layoutVertically(); + rightStack.addSpacer(); + + const logoStack = rightStack.addStack(); + + logoStack.centerAlignContent(); + logoStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = logoStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + logoImageWidget.centerAlignImage(); + + rightStack.addSpacer(); + + const nameStack = this.provideText( + currentlyPlayingTrack.item.name, + rightStack, + { size: 16, font: fontFamily } + ); + + nameStack.lineLimit = 1; + nameStack.minimumScaleFactor = 0.5; + + rightStack.addSpacer(); + + const athorStack = this.provideText( + currentlyPlayingTrack.item.artists[0].name, + rightStack, + { size: 12, font: fontFamily } + ); + + athorStack.lineLimit = 1; + athorStack.minimumScaleFactor = 0.5; + + rightStack.addSpacer(); + + const actionsStack = rightStack.addStack(); + + const preImage = SFSymbol.named("backward.end.fill").image; + const playImage = SFSymbol.named("pause.fill").image; + const nextImage = SFSymbol.named("forward.end.fill").image; + + const iconSize = new Size(20, 20); + const preStack = actionsStack.addImage(preImage); + preStack.tintColor = this.widgetColor; + preStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const playStack = actionsStack.addImage(playImage); + playStack.tintColor = this.widgetColor; + playStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const nextStack = actionsStack.addImage(nextImage); + nextStack.tintColor = this.widgetColor; + nextStack.imageSize = iconSize; + + rightStack.addSpacer(); + + containerStack.addSpacer(); + } else { + const listItems = playlists.items.reverse(); + const topItem = listItems[0]; + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + const topStack = containerStack.addStack(); + topStack.centerAlignContent(); + + const topImg = await this.$request.get(topItem.images[0].url, "IMG"); + const topImageStack = topStack.addImage(topImg); + topImageStack.cornerRadius = 6; + topImageStack.imageSize = new Size(50, 50); + + topStack.addSpacer(20); + + this.provideText(topItem.name, topStack, { font: fontFamily, size: 16 }); + + topStack.addSpacer(); + + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = topStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + + containerStack.addSpacer(); + + const bottomStack = containerStack.addStack(); + + const bottomItems = [ + listItems[1], + listItems[2], + listItems[3], + listItems[4], + ].filter((item) => !!item); + for (const index in bottomItems) { + const item = bottomItems[index]; + const itemImage = await this.$request.get(item.images[0].url, "IMG"); + const itemImageStack = bottomStack.addImage(itemImage); + itemImageStack.cornerRadius = 6; + itemImageStack.imageSize = new Size(65, 65); + if (index !== bottomItems.length - 1) bottomStack.addSpacer(); + } + } + + return widget; + }; + + renderSmall = async (widget) => { + widget.setPadding(0, 0, 0, 0); + const { currentlyPlayingTrack, playlists } = this.dataSource; + + if (currentlyPlayingTrack.is_playing) { + const iconSize = new Size(20, 20); + + const containerStack = widget.addStack(); + containerStack.setPadding(12, 12, 12, 12); + containerStack.layoutVertically(); + + const thumbImage = await this.$request.get( + currentlyPlayingTrack.item.album.images[0].url, + "IMG" + ); + const opacityImg = await this.shadowImage(thumbImage, "#000", 0.3); + containerStack.backgroundImage = opacityImg; + + const topStack = containerStack.addStack(); + topStack.centerAlignContent(); + + const music = SFSymbol.named("timer.circle.fill").image; + const musicStack = topStack.addImage(music); + musicStack.tintColor = Color.white(); + musicStack.imageSize = new Size(14, 14); + + topStack.addSpacer(10); + + this.provideText( + convertMillisecondsToHMS(currentlyPlayingTrack.item.duration_ms), + topStack, + { size: 12, font: fontFamily, color: Color.white() } + ); + + topStack.addSpacer(); + const logoStack = topStack.addStack(); + logoStack.centerAlignContent(); + + logoStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = logoStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + logoImageWidget.centerAlignImage(); + + containerStack.addSpacer(); + + const nameStack = this.provideText( + currentlyPlayingTrack.item.name, + containerStack, + { size: 16, font: fontFamily, color: Color.white() } + ); + + nameStack.lineLimit = 2; + containerStack.addSpacer(); + + const athorStack = this.provideText( + currentlyPlayingTrack.item.artists[0].name, + containerStack, + { size: 12, font: fontFamily, color: Color.white() } + ); + + athorStack.lineLimit = 1; + athorStack.minimumScaleFactor = 0.5; + + containerStack.addSpacer(); + + const actionsStack = containerStack.addStack(); + + const preImage = SFSymbol.named("backward.end.fill").image; + const playImage = SFSymbol.named("pause.fill").image; + const nextImage = SFSymbol.named("forward.end.fill").image; + + const preStack = actionsStack.addImage(preImage); + preStack.tintColor = Color.white(); + preStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const playStack = actionsStack.addImage(playImage); + playStack.tintColor = Color.white(); + playStack.imageSize = iconSize; + + actionsStack.addSpacer(); + + const nextStack = actionsStack.addImage(nextImage); + nextStack.tintColor = Color.white(); + nextStack.imageSize = iconSize; + } else { + const listItems = playlists.items.reverse(); + const topItem = listItems[0]; + + const containerStack = widget.addStack(); + containerStack.setPadding(12, 12, 12, 12); + containerStack.layoutVertically(); + + const thumbImage = await this.$request.get(topItem.images[0].url, "IMG"); + const opacityImg = await this.shadowImage(thumbImage, "#000", 0.3); + containerStack.backgroundImage = opacityImg; + + const topStack = containerStack.addStack(); + topStack.addSpacer(); + const logoImage = await this.$request.get(logo, "IMG"); + const logoImageWidget = topStack.addImage(logoImage); + logoImageWidget.imageSize = new Size(20, 20); + + containerStack.addSpacer(); + + this.provideText(topItem.name, containerStack, { + font: fontFamily, + size: 16, + color: Color.white(), + }); + + containerStack.addSpacer(); + + const descStack = this.provideText(topItem.description, containerStack, { + font: fontFamily, + size: 12, + color: Color.white(), + }); + + descStack.lineLimit = 2; + descStack.minimumScaleFactor = 0.5; + + containerStack.addSpacer(); + + const actionsStack = containerStack.addStack(); + actionsStack.addSpacer(); + actionsStack.centerAlignContent(); + + const heartImage = SFSymbol.named("heart.circle.fill").image; + const heartStack = actionsStack.addImage(heartImage); + heartStack.tintColor = Color.white(); + heartStack.imageSize = new Size(20, 20); + + actionsStack.addSpacer(5); + + this.provideText(`${topItem.tracks.total}`, actionsStack, { + size: 14, + font: fontFamily, + color: Color.white(), + }); + } + + return widget; + }; + + renderAccessoryInline = async (widget) => { + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.useDefaultPadding(); + + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "small") await this.renderSmall(widget); + if (this.widgetFamily === "medium") await this.renderMedium(widget); + if (this.widgetFamily === "large") await this.renderLarge(widget); + if (this.widgetFamily == "accessoryInline") + await this.renderAccessoryInline(widget); + + return widget; + } +} + +// @组件代码结束 +await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/Telegram.js b/Scripts/Telegram.js new file mode 100644 index 0000000..1d58acf --- /dev/null +++ b/Scripts/Telegram.js @@ -0,0 +1,240 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: pink; icon-glyph: paper-plane; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = 'Telegram'; + this.en = 'Telegram'; + this.inputValue = arg || 'Durov'; + let _md5 = this.md5(module.filename + this.en); + this.CACHE_KEY = `cache_${_md5}_` + this.inputValue; + this.Run(); + } + + CACHE_KEY; + useBoxJS = false; + dataSource = { + footer: {}, + }; + + init = async () => { + try { + await this.getData(); + } catch (e) { + console.log(e); + } + }; + + getData = async () => { + try { + let data = await this.$request.get( + 'https://t.me/s/' + this.inputValue, + 'STRING', + ); + data = data.match( + /tgme_channel_info_header">(.|\n)+tgme_channel_download_telegram"/, + )[0]; + this.dataSource.logo = data.match(/https.+jpg/)[0]; + this.dataSource.title = data.match( + /header_title">(.+)<\/span>/, + )[1]; + let entities = this.dataSource.title.match(/&#\d{2,3};/g); + if (entities) { + for (let k in entities) { + let rExp = new RegExp(entities[k], 'g'); + this.dataSource.title = this.dataSource.title.replace( + rExp, + this.entityToString(entities[k]), + ); + } + } + let counters = data.match(/counter_value">.+?<\/span>/g); + let type = data.match(/counter_type">.+?<\/span>/g); + counters.forEach((item, index) => { + const value = item.match(/counter_value">(.+?)<\/span>/)[1]; + const key = type[index].match(/counter_type">(.+?)<\/span>/)[1]; + this.dataSource.footer[key] = value; + }); + Keychain.set(this.CACHE_KEY, JSON.stringify(this.dataSource)); + } catch (e) { + if (Keychain.contains(this.CACHE_KEY)) { + this.dataSource = Keychain.get(this.CACHE_KEY); + } + } + }; + + entityToString(entity) { + let entities = entity.split(';'); + entities.pop(); + return entities.map((item) => + String.fromCharCode( + item[2] === 'x' + ? parseInt(item.slice(3), 16) + : parseInt(item.slice(2)), + ), + ).join(''); + } + + setAvatar = async (stack) => { + stack.size = new Size(60, 60); + stack.backgroundColor = this.widgetColor; + stack.cornerRadius = 10; + try { + const {logo} = this.dataSource; + const imgLogo = await this.$request.get(logo, 'IMG'); + const imgLogoItem = stack.addImage(imgLogo); + imgLogoItem.imageSize = new Size(60, 60); + } catch (e) { + console.log(e); + } + return stack; + }; + + setNumberStack = (stack, data) => { + stack.layoutVertically(); + stack.addSpacer(); + const textFormatNumber = this.textFormat.title; + textFormatNumber.color = this.widgetColor; + textFormatNumber.size = 18; + const stackTitle = stack.addStack(); + stackTitle.addSpacer(); + const valueItem = this.provideText( + data.value, + stackTitle, + textFormatNumber, + ); + valueItem.lineLimit = 1; + stackTitle.addSpacer(); + + stack.addSpacer(5); + + const textFormatDesc = this.textFormat.defaultText; + textFormatDesc.color = new Color('#aaaaaa'); + textFormatDesc.size = 10; + const stackDesc = stack.addStack(); + stackDesc.addSpacer(); + const descItem = this.provideText(data.key, stackDesc, textFormatDesc); + descItem.lineLimit = 1; + stackDesc.addSpacer(); + stack.addSpacer(); + + return stack; + }; + + setTitleStack = (stack) => { + const textFormatNumber = this.textFormat.title; + textFormatNumber.color = this.widgetColor; + const title = this.dataSource.title; + textFormatNumber.size = + title.length > 20 || this.widgetFamily === 'small' ? 16 : 20; + const titleItem = this.provideText(title, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setPathStack = (stack) => { + const textFormatNumber = this.textFormat.defaultText; + textFormatNumber.color = new Color('#2481cc'); + textFormatNumber.size = 12; + const titleItem = this.provideText( + `@${this.inputValue}`, + stack, + textFormatNumber, + ); + titleItem.lineLimit = 1; + }; + + renderSmall = async (w) => { + const stackBody = w.addStack(); + stackBody.url = `tg://resolve?domain=${this.inputValue}`; + stackBody.layoutVertically(); + stackBody.addSpacer(); + const stackHeader = stackBody.addStack(); + const stackLeft = stackHeader.addStack(); + await this.setAvatar(stackLeft); + stackHeader.addSpacer(5); + + const stackRight = stackHeader.addStack(); + Object.keys(this.dataSource.footer).forEach((key, index) => { + if (index === 0) { + const value = this.dataSource.footer[key]; + this.setNumberStack(stackRight, {key, value}); + } + }); + + stackBody.addSpacer(); + const stackFooter = stackBody.addStack(); + stackFooter.layoutVertically(); + this.setTitleStack(stackFooter); + stackFooter.addSpacer(5); + this.setPathStack(stackFooter); + stackBody.addSpacer(); + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.url = `https://t.me/${this.inputValue}`; + stackBody.layoutVertically(); + stackBody.addSpacer(); + const stackHeader = stackBody.addStack(); + const stackLeft = stackHeader.addStack(); + await this.setAvatar(stackLeft); + stackHeader.addSpacer(20); + + const stackRight = stackHeader.addStack(); + stackRight.layoutVertically(); + stackRight.addSpacer(5); + this.setTitleStack(stackRight); + stackRight.addSpacer(5); + this.setPathStack(stackRight); + + stackBody.addSpacer(); + + const stackFooter = stackBody.addStack(); + Object.keys(this.dataSource.footer).forEach((key) => { + const value = this.dataSource.footer[key]; + const stack = stackFooter.addStack(); + this.setNumberStack(stack, {key, value}); + }); + stackBody.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return await this.renderMedium(w); + }; + + Run() { + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", true); // 正式环境 +await Runing(Widget, args.widgetParameter); //远程开发环境 diff --git a/Scripts/VPNBoardPress.js b/Scripts/VPNBoardPress.js new file mode 100644 index 0000000..f038939 --- /dev/null +++ b/Scripts/VPNBoardPress.js @@ -0,0 +1,575 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: paper-plane; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const {DmYY, Runing} = require('./DmYY'); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = 'VPNBoardPress'; + this.en = 'VPNBoardPress'; + this.CACHE_KEY = this.md5(`dataSouce_${this.en}`); + this.Run(); + } + + logo = 'https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif'; + useBoxJS = false; + dataSource = { + restData: '0', + usedData: '0', + todayUsed: '0', + isCheckIn: false, + }; + + gradient = (color) => { + const linear = new LinearGradient(); + linear.colors = color.map(item => new Color(item)); + linear.locations = [0, 0.5]; + return linear; + }; + + color1 = ['#ef0a6a', '#b6359c']; + color2 = ['#ff54fa', '#fad126']; + color3 = ['#28cfb3', '#72d7cc']; + + chartConfig = (data, color, value) => { + console.log(data); + const fontColor = `#${this.widgetColor.hex}`; + const template1 = ` +{ + "type": "radialGauge", + "data": { + "datasets": [ + { + "data": [${parseFloat(data[0])}], + "borderWidth": 0, + "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( + color[0])}), + } + ] + }, + "options": { + centerPercentage: 86, + rotation: Math.PI / 2, + centerArea: { + displayText: false, + }, + options:{ + trackColor: '#f4f4f4', + } + } +} + `; + + const template2 = ` +{ + "type": "radialGauge", + "data": { + "datasets": [ + { + "data": [${parseFloat(data[1])}], + "borderWidth": 0, + "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( + color[1])}), + } + ] + }, + "options": { + layout: { + padding: { + left: 47, + right: 47, + top: 47, + bottom: 47 + } + }, + options:{ + trackColor: '#f4f4f4', + }, + centerPercentage: 80, + rotation: Math.PI / 2, + centerArea: { + displayText: false, + } + } +} + `; + const template3 = ` +{ + "type": "radialGauge", + "data": { + "datasets": [ + { + "data": [${parseFloat(data[2])}], + "borderWidth": 0, + "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( + color[2])}), + } + ] + }, + "options": { + layout: { + padding: { + left: 94, + right: 94, + top: 94, + bottom: 94 + } + }, + options:{ + trackColor: '#f4f4f4', + }, + centerPercentage: 70, + rotation: Math.PI / 2, + centerArea: { + displayText: false, + fontColor: '${fontColor}', + fontSize: 12, + text:(value)=>{ + return '${value}'; + } + } + } +} + `; + console.log(template1); + console.log(template2); + console.log(template3); + return {template1, template2, template3}; + }; + + account = { + title: '', + url: '', + email: '', + password: '', + }; + + init = async () => { + try { + if (this.account.url) { + await this.login(); + await this.checkin(); + await this.dataResults(); + } + } catch (e) { + console.log(e); + } + }; + + async login() { + const table = { + url: this.account.url, + body: `email=${encodeURIComponent( + this.account.email)}&passwd=${encodeURIComponent( + this.account.password)}&remember_me=on&rumber-me=week`, + }; + const request = new Request(table.url); + request.body = table.body; + request.method = 'POST'; + const data = await request.loadString(); + try { + if ( + JSON.parse(data).msg.match( + /邮箱不存在|邮箱或者密码错误|Mail or password is incorrect/, + ) + ) { + this.notify(this.name, '邮箱或者密码错误'); + console.log('登陆失败'); + this.cookie = request.response.cookies( + item => `${item.name}=${item.value}`).join('; '); + } else { + console.log('登陆成功'); + } + } catch (e) { + console.log(e); + } + } + + async checkin() { + const url = this.account.url; + let checkinPath = + url.indexOf('auth/login') !== -1 ? 'user/checkin' : 'user/_checkin.php'; + const checkinreqest = { + url: url.replace(/(auth|user)\/login(.php)*/g, '') + checkinPath, + headers: { + cookie: this.cookie, + }, + }; + const data = await this.$request.post(checkinreqest, 'STRING'); + if (data.match(/\"msg\"\:/)) { + console.log('签到成功'); + this.dataSource.isCheckIn = true; + } else { + console.log('签到失败'); + } + } + + async dataResults() { + let url = this.account.url; + const userPath = url.indexOf('auth/login') !== -1 + ? 'user' + : 'user/index.php'; + url = url.replace(/(auth|user)\/login(.php)*/g, '') + userPath; + const webView = new WebView(); + await webView.loadURL(url); + const js = ` +var response = {todayUsed: "0KB", usedData: "0KB", restData: "0KB"}; +if($('.progressbar').length){ + response.todayUsed = $('.progressbar .label-flex:eq(0) .card-tag').text(); + response.usedData = $('.progressbar .label-flex:eq(1) .card-tag').text(); + response.restData = $('.progressbar .label-flex:eq(2) .card-tag').text(); +} else if(document.body.innerHTML.includes('trafficDountChat')){ + response.todayUsed = $('.card.card-statistic-2:eq(1) .breadcrumb-item').text().split(' ')[1]; + response.restData = $('.card.card-statistic-2:eq(1) .card-body').text().trim(); + response.usedData = document.body.innerHTML.match(/trafficDountChat\\s*\\(([^\\)]+)/)[1].match(/\\d[^\\']+/g)[0]; +} +completion(response); + `; + const response = await webView.evaluateJavaScript(js, true); + this.dataSource = {...this.dataSource, ...response}; + } + + translateFlow(value) { + const unit = [ + {unit: 'T', value: 1024 * 1024}, + {unit: 'G', value: 1024}, + {unit: 'M', value: 1}, + {unit: 'K', value: 1 / 1024}, + ]; + const data = {unit: '', value: parseFloat(value)}; + unit.forEach(item => { + if (value.indexOf(item.unit) > -1) { + data.unit = item.unit; + data.value = Math.floor((parseFloat(value) * item.value) * 100) / 100; + } + }); + return data; + } + + createChart = async (size, chart) => { + const url = `https://quickchart.io/chart?w=${size.w}&h=${size.h}&f=png&c=${encodeURIComponent( + chart)}`; + return await this.$request.get(url, 'IMG'); + }; + + renderContent = async (w, size, viewSize) => { + const rest = this.translateFlow(this.dataSource.restData); + const use = this.translateFlow(this.dataSource.usedData); + const today = this.translateFlow(this.dataSource.todayUsed); + console.log(this.dataSource); + const total = rest.value + use.value; + const data1 = Math.floor(rest.value / total * 100); + const data2 = Math.floor(use.value / total * 100); + const data3 = Math.floor((today.value / total) * 100); + const {template1, template2, template3} = this.chartConfig([ + data1, data2, data3, + ], [this.color1, this.color2, this.color3], this.dataSource.todayUsed); + + const stackContent = w.addStack(); + stackContent.centerAlignContent(); + const stackSize = viewSize; + stackContent.size = stackSize; + stackContent.backgroundImage = await this.createChart(size, template1); + + const stackContent2 = stackContent.addStack(); + stackContent2.size = stackSize; + stackContent2.backgroundImage = await this.createChart(size, template2); + + const stackContent3 = stackContent2.addStack(); + stackContent3.size = stackSize; + stackContent3.backgroundImage = await this.createChart(size, template3); + + }; + + setLabelCell = async (stack, data) => { + stack.addSpacer(); + + const stackCell = stack.addStack(); + stackCell.centerAlignContent(); + const stackIcon = stackCell.addStack(); + stackIcon.size = new Size(16, 16); + stackIcon.cornerRadius = 8; + if (data.isImg) { + try { + const icon = await this.$request.get(data.icon, 'IMG'); + stackIcon.addImage(icon); + } catch (e) { + console.log(e); + } + } else { + stackIcon.backgroundGradient = data.icon; + } + + stackCell.addSpacer(5); + + const stackTitle = stackCell.addStack(); + const title = {...this.textFormat.title}; + title.color = this.widgetColor; + this.provideText(data.title, stackTitle, title); + + stackCell.addSpacer(5); + + const stackValue = stackCell.addStack(); + const value = {...this.textFormat.defaultText}; + value.color = this.widgetColor; + this.provideText(data.value, stackValue, title); + + stack.addSpacer(); + }; + + setFooterCell = (stack, data) => { + const title = {...this.textFormat.title}; + title.color = this.widgetColor; + title.size = 10; + + const stackCell = stack.addStack(); + stackCell.layoutVertically(); + + const stackIcon = stackCell.addStack(); + stackIcon.addSpacer(); + const stackViewIcon = stackIcon.addStack(); + stackViewIcon.size = new Size(10, 10); + stackViewIcon.cornerRadius = 50; + stackViewIcon.backgroundGradient = this.gradient(data.color); + stackIcon.addSpacer(); + + stackCell.layoutVertically(); + const stackText = stackCell.addStack(); + stackText.addSpacer(); + this.provideText(data.value, stackText, title); + stackText.addSpacer(); + + const desc = {...this.textFormat.defaultText}; + desc.color = this.widgetColor; + desc.size = 8; + + const stackTip = stackCell.addStack(); + stackTip.addSpacer(); + this.provideText(data.label, stackTip, desc); + stackTip.addSpacer(); + }; + + renderSmall = async (w) => { + const stackHeader = w.addStack(); + const stackLeft = stackHeader.addStack(); + stackHeader.centerAlignContent(); + stackLeft.centerAlignContent(); + try { + const imgIcon = await this.$request.get( + this.account.icon || this.logo, 'IMG'); + const stackImgItem = stackLeft.addImage(imgIcon); + stackImgItem.imageSize = new Size(12, 12); + stackImgItem.cornerRadius = 4; + stackLeft.addSpacer(5); + } catch (e) { + console.log(e); + } + const title = {...this.textFormat.title}; + title.color = this.widgetColor; + title.size = 12; + this.provideText(this.account.title, stackLeft, title); + stackHeader.addSpacer(); + + const stackRight = stackHeader.addStack(); + stackRight.centerAlignContent(); + const calendar = SFSymbol.named('calendar'); + const imgCalendar = stackRight.addImage(calendar.image); + imgCalendar.imageSize = new Size(12, 12); + imgCalendar.tintColor = new Color('#00b800'); + stackRight.addSpacer(5); + this.provideText( + this.dataSource.isCheckIn ? '已签' : '未签', stackRight, title); + + w.addSpacer(); + + const stackContent = w.addStack(); + stackContent.addSpacer(); + await this.renderContent(stackContent, {w: 360, h: 360}, new Size(80, 80)); + stackContent.addSpacer(); + + w.addSpacer(); + + const stackFooter = w.addStack(); + stackFooter.centerAlignContent(); + const stackFooterLeft = stackFooter.addStack(); + this.setFooterCell(stackFooterLeft, { + value: this.dataSource.restData, + label: '剩余', + color: this.color1, + }); + + stackFooter.addSpacer(); + + const stackFooterRight = stackFooter.addStack(); + this.setFooterCell(stackFooterRight, { + value: this.dataSource.usedData, + label: '累计', + color: this.color3, + }); + + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + const stackLeft = stackBody.addStack(); + await this.renderContent(stackLeft, {w: 360, h: 360}, new Size(140, 140)); + stackBody.addSpacer(10); + const stackRight = stackBody.addStack(); + stackRight.layoutVertically(); + await this.setLabelCell( + stackRight, { + icon: this.account.icon || this.logo, + title: this.account.title, + value: this.dataSource.isCheckIn ? '已签到' : '未签到', + isImg: true, + }); + await this.setLabelCell( + stackRight, + { + icon: this.gradient(this.color3), + title: '今日', + value: this.dataSource.todayUsed, + }, + ); + await this.setLabelCell( + stackRight, + { + icon: this.gradient(this.color2), + title: '累计', + value: this.dataSource.usedData, + }, + ); + await this.setLabelCell( + stackRight, + { + icon: this.gradient(this.color1), + title: '剩余', + value: this.dataSource.restData, + }, + ); + + return w; + }; + + renderLarge = async (w) => { + w.addText('暂不支持'); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } + + Run = () => { + try { + if (config.runsInApp) { + this.registerAction('默认账号', this.actionSettings, { + name: "text.badge.star", + color: "#a0d911", + }); + this.registerAction('新增账号', async () => { + const account = await this.setAlertInput( + '添加账号', '添加账号数据,添加完成之后请去设置默认账号', { + title: '机场名', + icon: '图标', + url: '登陆地址', + email: '邮箱账号', + password: '密码', + }, false); + if (!this.settings.dataSource) this.settings.dataSource = []; + if (!account) return; + if (account.title && account.url && account.email && + account.password) { + this.settings.dataSource.push(account); + } + this.settings.dataSource = this.settings.dataSource.filter( + item => item); + this.saveSettings(); + },{ + name: "text.badge.plus", + color: "#fadb14", + }); + this.registerAction('清除账号', this.deletedVpn, { + name: "text.badge.xmark", + color: "#f5222d", + }); + this.registerAction('基础设置', this.setWidgetConfig); + } + this.account = this.settings.account || this.account; + this.CACHE_KEY += '_' + this.account.title; + const index = typeof args.widgetParameter === 'string' ? parseInt( + args.widgetParameter) : false; + if (this.settings.dataSource && this.settings.dataSource[index] && + index !== + false) { + this.account = this.settings.dataSource[index]; + } + } catch (e) { + console.log(e); + } + }; + + async actionSettings() { + try { + const table = new UITable(); + const dataSource = this.settings.dataSource || []; + dataSource.map((t, index) => { + const r = new UITableRow(); + r.addText(`parameter:${index} 机场名:${t.title} 账号:${t.email}`); + r.onSelect = (n) => { + this.settings.account = t; + this.notify(t.title, `默认账号设置成功\n账号:${t.email}`); + this.saveSettings(false); + }; + table.addRow(r); + }); + table.present(false); + } catch (e) { + console.log(e); + } + } + + async deletedVpn() { + try { + const table = new UITable(); + const dataSource = this.settings.dataSource || []; + dataSource.map((t, index) => { + const r = new UITableRow(); + r.addText(`❌ 机场名:${t.title} 账号:${t.email}`); + r.onSelect = (n) => { + dataSource.splice(index, 1); + this.settings.dataSource = dataSource; + this.notify(t.title, `❌\n账号:${t.email}`); + this.saveSettings(false); + }; + table.addRow(r); + }); + table.present(false); + } catch (e) { + console.log(e); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/VPNSubscription.js b/Scripts/VPNSubscription.js new file mode 100644 index 0000000..fec9982 --- /dev/null +++ b/Scripts/VPNSubscription.js @@ -0,0 +1,532 @@ +// letiables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: paper-plane; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = "VPNSubscription"; + this.en = "VPNSubscription"; + this.CACHE_KEY = this.md5(`dataSouce_${this.en}`); + this.Run(); + } + + useBoxJS = false; + today = ""; + logo = + "https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif"; + + dataSource = { + restData: "0", + usedData: "0", + totalData: "0", + todayData: "0", + isCheckIn: false, + }; + + account = { + title: "", + url: "", + }; + + color1 = ["#ef0a6a", "#b6359c"]; + color2 = ["#ff54fa", "#fad126"]; + color3 = ["#28cfb3", "#72d7cc"]; + + chartConfig = (data, color, value) => { + console.log(data); + const fontColor = `#${this.widgetColor.hex}`; + const template1 = ` +{ + "type": "radialGauge", + "data": { + "datasets": [ + { + "data": [${parseFloat(data[0])}], + "borderWidth": 0, + "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( + color[0] + )}), + } + ] + }, + "options": { + centerPercentage: 86, + rotation: Math.PI / 2, + centerArea: { + displayText: false, + }, + options:{ + trackColor: '#f4f4f4', + } + } +} + `; + + const template2 = ` +{ + "type": "radialGauge", + "data": { + "datasets": [ + { + "data": [${parseFloat(data[1])}], + "borderWidth": 0, + "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( + color[1] + )}), + } + ] + }, + "options": { + layout: { + padding: { + left: 47, + right: 47, + top: 47, + bottom: 47 + } + }, + options:{ + trackColor: '#f4f4f4', + }, + centerPercentage: 80, + rotation: Math.PI / 2, + centerArea: { + displayText: false, + } + } +} + `; + const template3 = ` +{ + "type": "radialGauge", + "data": { + "datasets": [ + { + "data": [${parseFloat(data[2])}], + "borderWidth": 0, + "backgroundColor": getGradientFillHelper('vertical', ${JSON.stringify( + color[2] + )}), + } + ] + }, + "options": { + layout: { + padding: { + left: 94, + right: 94, + top: 94, + bottom: 94 + } + }, + options:{ + trackColor: '#f4f4f4', + }, + centerPercentage: 70, + rotation: Math.PI / 2, + centerArea: { + displayText: true, + fontColor: '${fontColor}', + fontSize: 20, + text:(value)=>{ + return '${value}'; + } + } + } +} + `; + console.log(template1); + console.log(template2); + console.log(template3); + return { template1, template2, template3 }; + }; + + init = async () => { + try { + const data = await this.getdata(this.account.url); + const total = data[2]; + const today = data[0]; + const remain = data[2] - data[0] - data[1]; + const use = total - remain; + this.dataSource.restData = remain; + this.dataSource.totalData = total; + this.dataSource.usedData = use; + this.dataSource.todayData = today; + console.log(this.dataSource); + } catch (e) { + console.log(e); + } + }; + + async getdata(url) { + const req = new Request(url); + req.method = "GET"; + await req.load(); + let resp = req.response.headers["subscription-userinfo"]; + resp = [ + parseInt(resp.match(/upload=([0-9]+);?/)[1]).toFixed(2), + parseInt(resp.match(/download=([0-9]+);?/)[1]).toFixed(2), + parseInt(resp.match(/total=([0-9]+);?/)[1]).toFixed(2), + ]; + console.log(resp); + return resp; + } + + gradient = (color) => { + const linear = new LinearGradient(); + linear.colors = color.map((item) => new Color(item)); + linear.locations = [0, 0.5]; + return linear; + }; + + formatFileSize(fileSize) { + if (fileSize < 1024 * 1024) { + let temp = fileSize / 1024; + temp = temp.toFixed(2); + return temp + "KB"; + } else if (fileSize < 1024 * 1024 * 1024) { + let temp = fileSize / (1024 * 1024); + temp = temp.toFixed(2); + return temp + "MB"; + } else if (fileSize < 1024 * 1024 * 1024 * 1024) { + let temp = fileSize / (1024 * 1024 * 1024); + temp = temp.toFixed(2); + return temp + "GB"; + } else { + let temp = fileSize / (1024 * 1024 * 1024 * 1024); + temp = temp.toFixed(2); + return temp + "TB"; + } + } + + createChart = async (size, chart) => { + const url = `https://quickchart.io/chart?w=${size.w}&h=${ + size.h + }&f=png&c=${encodeURIComponent(chart)}`; + return await this.$request.get(url, "IMG"); + }; + + setContent = async (w, size, viewSize) => { + const rest = this.dataSource.restData; + const use = this.dataSource.usedData; + const today = this.dataSource.todayData; + const total = this.dataSource.totalData; + const data1 = Math.floor((rest / total) * 100); + const data2 = Math.floor((use / total) * 100); + const data3 = Math.floor((today / total) * 100); + const data = [data1 || 0, data2 || 0, data3 || 0]; + const { template1, template2, template3 } = this.chartConfig( + data, + [this.color1, this.color2, this.color3], + this.formatFileSize(this.dataSource.restData) + ); + + const stackContent = w.addStack(); + stackContent.centerAlignContent(); + const stackSize = viewSize; + stackContent.size = stackSize; + stackContent.backgroundImage = await this.createChart(size, template1); + + const stackContent2 = stackContent.addStack(); + stackContent2.size = stackSize; + stackContent2.backgroundImage = await this.createChart(size, template2); + + const stackContent3 = stackContent2.addStack(); + stackContent3.size = stackSize; + stackContent3.backgroundImage = await this.createChart(size, template3); + }; + + setLabelCell = async (stack, data) => { + stack.addSpacer(); + + const stackCell = stack.addStack(); + stackCell.centerAlignContent(); + const stackIcon = stackCell.addStack(); + stackIcon.size = new Size(16, 16); + stackIcon.cornerRadius = 8; + if (data.isImg) { + try { + const icon = await this.$request.get(data.icon, "IMG"); + stackIcon.addImage(icon); + } catch (e) { + console.log(e); + } + } else { + stackIcon.backgroundGradient = data.icon; + } + + stackCell.addSpacer(5); + + const stackTitle = stackCell.addStack(); + const title = { ...this.textFormat.title }; + title.color = this.widgetColor; + this.provideText(data.title, stackTitle, title); + + stackCell.addSpacer(5); + + const stackValue = stackCell.addStack(); + const value = { ...this.textFormat.defaultText }; + value.color = this.widgetColor; + this.provideText(data.value, stackValue, title); + + stack.addSpacer(); + }; + + setFooterCell = (stack, data) => { + const title = { ...this.textFormat.title }; + title.color = this.widgetColor; + title.size = 10; + + const stackCell = stack.addStack(); + stackCell.layoutVertically(); + + const stackIcon = stackCell.addStack(); + stackIcon.addSpacer(); + const stackViewIcon = stackIcon.addStack(); + stackViewIcon.size = new Size(10, 10); + stackViewIcon.cornerRadius = 5; + stackViewIcon.backgroundGradient = this.gradient(data.color); + stackIcon.addSpacer(); + + stackCell.layoutVertically(); + const stackText = stackCell.addStack(); + stackText.addSpacer(); + this.provideText(data.value, stackText, title); + stackText.addSpacer(); + + const desc = { ...this.textFormat.defaultText }; + desc.color = this.widgetColor; + desc.size = 8; + + const stackTip = stackCell.addStack(); + stackTip.addSpacer(); + this.provideText(data.label, stackTip, desc); + stackTip.addSpacer(); + }; + + renderSmall = async (w) => { + const stackHeader = w.addStack(); + const stackLeft = stackHeader.addStack(); + stackHeader.centerAlignContent(); + stackLeft.centerAlignContent(); + try { + const imgIcon = await this.$request.get( + this.account.icon || this.logo, + "IMG" + ); + const stackImgItem = stackLeft.addImage(imgIcon); + stackImgItem.imageSize = new Size(12, 12); + stackImgItem.cornerRadius = 4; + stackLeft.addSpacer(5); + } catch (e) { + console.log(e); + } + const title = { ...this.textFormat.title }; + title.color = this.widgetColor; + title.size = 12; + this.provideText(this.account.title, stackLeft, title); + stackHeader.addSpacer(); + + const stackRight = stackHeader.addStack(); + stackRight.centerAlignContent(); + const calendar = SFSymbol.named("waveform.path.badge.minus"); + const imgCalendar = stackRight.addImage(calendar.image); + imgCalendar.imageSize = new Size(12, 12); + imgCalendar.tintColor = new Color("#00b800"); + stackRight.addSpacer(5); + this.provideText( + this.formatFileSize(this.dataSource.todayData), + stackRight, + title + ); + w.addSpacer(); + + const stackContent = w.addStack(); + stackContent.addSpacer(); + await this.setContent(stackContent, { w: 360, h: 360 }, new Size(80, 80)); + stackContent.addSpacer(); + + w.addSpacer(); + + const stackFooter = w.addStack(); + stackFooter.centerAlignContent(); + const stackFooterLeft = stackFooter.addStack(); + this.setFooterCell(stackFooterLeft, { + value: this.formatFileSize(this.dataSource.restData), + label: "剩余", + color: this.color1, + }); + + stackFooter.addSpacer(); + + const stackFooterRight = stackFooter.addStack(); + this.setFooterCell(stackFooterRight, { + value: this.formatFileSize(this.dataSource.usedData), + label: "累计", + color: this.color2, + }); + + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + const stackLeft = stackBody.addStack(); + await this.setContent(stackLeft, { w: 360, h: 360 }, new Size(140, 140)); + stackBody.addSpacer(10); + const stackRight = stackBody.addStack(); + stackRight.layoutVertically(); + await this.setLabelCell(stackRight, { + icon: this.account.icon || this.logo, + title: this.account.title, + value: ``, + isImg: true, + }); + await this.setLabelCell(stackRight, { + icon: this.gradient(this.color3), + title: "上传", + value: this.formatFileSize(this.dataSource.todayData), + }); + await this.setLabelCell(stackRight, { + icon: this.gradient(this.color2), + title: "累计", + value: this.formatFileSize(this.dataSource.usedData), + }); + + await this.setLabelCell(stackRight, { + icon: this.gradient(this.color1), + title: "剩余", + value: this.formatFileSize(this.dataSource.restData), + }); + + return w; + }; + + renderLarge = async (w) => { + w.addText("暂不支持"); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "medium") { + return await this.renderMedium(widget); + } else if (this.widgetFamily === "large") { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } + + Run = () => { + try { + if (config.runsInApp) { + this.registerAction("默认订阅", this.actionSettings, { + name: "text.badge.star", + color: "#a0d911", + }); + this.registerAction("新增订阅", async () => { + const account = await this.setAlertInput( + "添加订阅", + "添加订阅数据,添加完成之后请去设置默认订阅", + { + title: "机场名", + icon: "图标", + url: "订阅地址", + }, + false + ); + if (!this.settings.dataSource) this.settings.dataSource = []; + if (!account) return; + if (account.title && account.url) { + this.settings.dataSource.push(account); + } + this.settings.dataSource = this.settings.dataSource.filter( + (item) => item + ); + this.saveSettings(); + },{ + name: "text.badge.plus", + color: "#fadb14", + }); + this.registerAction("清除订阅", this.deletedVpn, { + name: "text.badge.xmark", + color: "#f5222d", + }); + this.registerAction("基础设置", this.setWidgetConfig); + } + this.account = this.settings.account || this.account; + this.CACHE_KEY += "_" + this.account.title; + const index = + typeof args.widgetParameter === "string" + ? parseInt(args.widgetParameter) + : false; + if ( + this.settings.dataSource && + this.settings.dataSource[index] && + index !== false + ) { + this.account = this.settings.dataSource[index]; + } + } catch (e) { + console.log(e); + } + }; + + async actionSettings() { + try { + const table = new UITable(); + const dataSource = this.settings.dataSource || []; + dataSource.map((t, index) => { + const r = new UITableRow(); + r.addText(`parameter:${index} 机场名:${t.title} 订阅:${t.url}`); + r.onSelect = () => { + this.settings.account = t; + this.notify(t.title, `默认订阅设置成功\n订阅:${t.url}`); + this.saveSettings(false); + }; + table.addRow(r); + }); + table.present(false); + } catch (e) { + console.log(e); + } + } + + async deletedVpn() { + try { + const table = new UITable(); + const dataSource = this.settings.dataSource || []; + dataSource.map((t, index) => { + const r = new UITableRow(); + r.addText(`❌ 机场名:${t.title} 订阅:${t.url}`); + r.onSelect = () => { + dataSource.splice(index, 1); + this.settings.dataSource = dataSource; + this.notify(t.title, `❌\n订阅:${t.url}`); + this.saveSettings(false); + }; + table.addRow(r); + }); + table.present(false); + } catch (e) { + console.log(e); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/VpnBoard.js b/Scripts/VpnBoard.js new file mode 100644 index 0000000..d1b1afa --- /dev/null +++ b/Scripts/VpnBoard.js @@ -0,0 +1,546 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: paper-plane; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = "Vpn"; + this.en = "vpnBoard"; + this.CACHE_KEY = this.md5(`dataSouce_${this.en}`); + this.Run(); + } + + useBoxJS = false; + dataSource = { + restData: "0", + usedData: "0", + todayUsed: "0", + isCheckIn: false, + }; + + chartConfig = (labels = [], datas = [], text = []) => { + const color = `#${this.widgetColor.hex}`; + let template; + let path = this.FILE_MGR.documentsDirectory(); + path = path + "/VPNBoardTemplate.js"; + if (this.FILE_MGR.fileExists(path)) { + template = require("./VPNBoardTemplate"); + } else { + template = ` +{ + 'type': 'bar', + 'data': { + 'labels': __LABELS__, + 'tips': __TEXT__, + 'datasets': [ + { + type: 'line', + backgroundColor: '#fff', + borderColor: getGradientFillHelper('vertical', ['#c8e3fa', '#e62490']), + 'borderWidth': 2, + pointRadius: 5, + 'fill': false, + 'data': __DATAS__, + }, + ], + }, + 'options': { + plugins: { + datalabels: { + display: true, + align: 'top', + color: __COLOR__, + font: { + size: '16' + }, + formatter: function(value, context) { + return context.chart.data.tips[context.dataIndex]; + } + }, + }, + layout: { + padding: { + left: 0, + right: 0, + top: 30, + bottom: 10 + } + }, + responsive: true, + maintainAspectRatio: true, + 'legend': { + 'display': false, + }, + 'title': { + 'display': false, + }, + scales: { + xAxes: [ // X 轴线 + { + gridLines: { + display: false, + color: __COLOR__, + }, + ticks: { + display: true, + fontColor: __COLOR__, + fontSize: '16', + }, + }, + ], + yAxes: [ + { + ticks: { + display: false, + beginAtZero: true, + fontColor: __COLOR__, + }, + gridLines: { + borderDash: [7, 5], + display: false, + color: __COLOR__, + }, + }, + ], + }, + }, + }`; + const content = `// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: ellipsis-v; +const template = \`${template}\`; +module.exports = template;`; + this.FILE_MGR.writeString(path, content); + } + template = template.replaceAll("__COLOR__", `'${color}'`); + template = template.replace("__LABELS__", `${JSON.stringify(labels)}`); + template = template.replace("__TEXT__", `${JSON.stringify(text)}`); + template = template.replace("__DATAS__", `${JSON.stringify(datas)}`); + return template; + }; + + account = { + title: "", + url: "", + email: "", + password: "", + }; + + range = { + 11.1: { todayUsed: "30M" }, + 11.2: { todayUsed: "130M" }, + 11.3: { todayUsed: "300M" }, + 11.4: { todayUsed: "30M" }, + 11.5: { todayUsed: "2g" }, + 11.6: { todayUsed: "3G" }, + }; + max = 6; + + init = async () => { + try { + if (this.account.url) { + await this.login(); + await this.checkin(); + await this.dataResults(); + if (Keychain.contains(this.CACHE_KEY)) { + this.range = JSON.parse(Keychain.get(this.CACHE_KEY)); + } + const date = new Date(); + const format = new DateFormatter(); + format.dateFormat = "MM.dd"; + const dateDay = format.string(date); + this.range[dateDay] = this.dataSource; + const rangeKey = Object.keys(this.range); + if (rangeKey.length > this.max) { + for (let i = 0; i <= rangeKey.length - this.max; i++) { + delete this.range[rangeKey[i]]; + } + } + Keychain.set(this.CACHE_KEY, JSON.stringify(this.range)); + } + } catch (e) { + console.log(e); + } + }; + + async login() { + const table = { + url: this.account.url, + body: `email=${encodeURIComponent( + this.account.email + )}&passwd=${encodeURIComponent( + this.account.password + )}&remember_me=on&rumber-me=week`, + }; + const request = new Request(table.url); + request.body = table.body; + request.method = "POST"; + const data = await request.loadString(); + try { + if ( + JSON.parse(data).msg.match( + /邮箱不存在|邮箱或者密码错误|Mail or password is incorrect/ + ) + ) { + this.notify(this.name, "邮箱或者密码错误"); + console.log("登陆失败"); + this.cookie = request.response + .cookies((item) => `${item.name}=${item.value}`) + .join("; "); + } else { + console.log("登陆成功"); + } + } catch (e) { + console.log(e); + } + } + + async checkin() { + const url = this.account.url; + let checkinPath = + url.indexOf("auth/login") !== -1 ? "user/checkin" : "user/_checkin.php"; + const checkinreqest = { + url: url.replace(/(auth|user)\/login(.php)*/g, "") + checkinPath, + headers: { + cookie: this.cookie, + }, + }; + const data = await this.$request.post(checkinreqest, "STRING"); + if (data.match(/\"msg\"\:/)) { + console.log("签到成功"); + this.dataSource.isCheckIn = true; + } else { + console.log("签到失败"); + } + } + + async dataResults() { + let url = this.account.url; + const userPath = + url.indexOf("auth/login") !== -1 ? "user" : "user/index.php"; + url = url.replace(/(auth|user)\/login(.php)*/g, "") + userPath; + const webView = new WebView(); + await webView.loadURL(url); + const js = ` +var response = {todayUsed: "0KB", usedData: "0KB", restData: "0KB"}; +if($('.progressbar').length){ + response.todayUsed = $('.progressbar .label-flex:eq(0) .card-tag').text(); + response.usedData = $('.progressbar .label-flex:eq(1) .card-tag').text(); + response.restData = $('.progressbar .label-flex:eq(2) .card-tag').text(); +} else if(document.body.innerHTML.includes('trafficDountChat')){ + response.todayUsed = $('.card.card-statistic-2:eq(1) .breadcrumb-item').text().split(' ')[1]; + response.restData = $('.card.card-statistic-2:eq(1) .card-body').text().trim(); + response.usedData = document.body.innerHTML.match(/trafficDountChat\\s*\\(([^\\)]+)/)[1].match(/\\d[^\\']+/g)[0]; +} +completion(response); + `; + const response = await webView.evaluateJavaScript(js, true); + this.dataSource = { ...this.dataSource, ...response }; + } + + translateFlow(value) { + const unit = [ + { unit: "T", value: 1024 * 1024 }, + { unit: "G", value: 1024 }, + { unit: "M", value: 1 }, + { unit: "K", value: 1 / 1024 }, + ]; + const data = { unit: "", value: parseFloat(value) }; + unit.forEach((item) => { + if (value.indexOf(item.unit) > -1) { + data.unit = item.unit; + data.value = Math.floor(parseFloat(value) * item.value * 100) / 100; + } + }); + + return data; + } + + createChart = async (size) => { + let labels = [], + data = [], + text = []; + const rangeKey = Object.keys(this.range); + rangeKey.forEach((key) => { + labels.push(key); + const value = this.range[key].todayUsed.toLocaleUpperCase(); + const valueUnit = this.translateFlow(value); + data.push(valueUnit.value); + text.push(value); + }); + if (this.widgetSize === "small") { + labels = labels.slice(-3); + data = data.slice(-3); + } + console.log(data); + const chart = this.chartConfig(labels, data, text); + console.log(chart); + const url = `https://quickchart.io/chart?w=${size.w}&h=${ + size.h + }&f=png&c=${encodeURIComponent(chart)}`; + return await this.$request.get(url, "IMG"); + }; + + createDivider(w) { + const drawContext = new DrawContext(); + drawContext.size = new Size(543, this.widgetSize === "small" ? 4 : 2); + const path = new Path(); + path.addLine(new Point(1000, 20)); + drawContext.addPath(path); + drawContext.setStrokeColor(new Color("#000", 1)); + drawContext.setLineWidth(2); + drawContext.strokePath(); + + const stackLine = w.addStack(); + stackLine.borderWidth = 1; + stackLine.borderColor = new Color("#000", 0.4); + stackLine.addImage(drawContext.getImage()); + w.addSpacer(5); + } + + async setHeader(w, size) { + const header = w.addStack(); + header.centerAlignContent(); + const left = header.addStack(); + left.centerAlignContent(); + let icon = + "https://raw.githubusercontent.com/58xinian/icon/master/glados_animation.gif"; + if (this.account.icon) icon = this.account.icon; + const stackIcon = left.addStack(); + try { + const imgIcon = await this.$request.get(icon, "IMG"); + const imgIconItem = stackIcon.addImage(imgIcon); + let iconSize = new Size(16, 16); + if (this.widgetSize === "small") iconSize = new Size(12, 12); + imgIconItem.imageSize = iconSize; + imgIconItem.cornerRadius = 4; + left.addSpacer(5); + } catch (e) { + console.log(e); + } + + const vpnName = { ...this.textFormat.title }; + vpnName.size = size; + vpnName.color = this.widgetColor; + this.provideText(this.account.title, left, vpnName); + + header.addSpacer(); + + const right = header.addStack(); + right.bottomAlignContent(); + const vpnFlow = { ...this.textFormat.title }; + vpnFlow.color = new Color("#26c5bc"); + vpnFlow.size = size; + this.provideText( + this.dataSource.isCheckIn ? "已签到" : "未签到", + right, + vpnFlow + ); + + w.addSpacer(); + } + + setLabel(w, data, size) { + const stackLabel = w.addStack(); + const textBattery = { ...this.textFormat.battery }; + textBattery.size = size.label; + textBattery.color = this.widgetColor; + this.provideText(data.label, stackLabel, textBattery); + + textBattery.size = size.value; + const stackValue = w.addStack(); + stackValue.centerAlignContent(); + textBattery.color = new Color(data.color); + this.provideText(data.value, stackValue, textBattery); + } + + setFooter(w, size) { + const footer = w.addStack(); + footer.centerAlignContent(); + + this.setLabel( + footer, + { + label: "剩余:", + color: "#ff3e3e", + value: this.dataSource.restData, + unit: "GB", + }, + size + ); + footer.addSpacer(); + this.setLabel( + footer, + { + label: "累计:", + color: "#dc9e28", + value: this.dataSource.usedData, + unit: "GB", + }, + size + ); + } + + setContent = async (w, size) => { + const imageFlow = await this.createChart(size); + const stackContent = w.addStack(); + stackContent.addImage(imageFlow); + }; + + renderSmall = async (w) => { + await this.setHeader(w, 10); + await this.setContent(w, { w: 195, h: 85 }); + this.createDivider(w); + this.setFooter(w, { label: 6, value: 8 }); + return w; + }; + + renderMedium = async (w) => { + await this.setHeader(w, 16); + await this.setContent(w, { w: 390, h: 85 }); + this.createDivider(w); + this.setFooter(w, { label: 14, value: 18 }); + return w; + }; + + renderLarge = async (w) => { + w.addText("暂不支持"); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "medium") { + return await this.renderMedium(widget); + } else if (this.widgetFamily === "large") { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } + + Run = () => { + try { + if (config.runsInApp) { + this.registerAction("默认账号", this.actionSettings, { + name: "text.badge.star", + color: "#a0d911", + }); + this.registerAction( + "新增账号", + async () => { + const account = await this.setAlertInput( + "添加账号", + "添加账号数据,添加完成之后请去设置默认账号", + { + title: "机场名", + icon: "图标", + url: "登陆地址", + email: "邮箱账号", + password: "密码", + }, + false + ); + if (!this.settings.dataSource) this.settings.dataSource = []; + if (!account) return; + if ( + account.title && + account.url && + account.email && + account.password + ) { + this.settings.dataSource.push(account); + } + this.settings.dataSource = this.settings.dataSource.filter( + (item) => item + ); + this.saveSettings(); + }, + { + name: "text.badge.plus", + color: "#fadb14", + } + ); + this.registerAction("清除账号", this.deletedVpn, { + name: "text.badge.xmark", + color: "#f5222d", + }); + this.registerAction("基础设置", this.setWidgetConfig); + } + this.account = this.settings.account || this.account; + this.CACHE_KEY += "_" + this.account.title; + const index = + typeof args.widgetParameter === "string" + ? parseInt(args.widgetParameter) + : false; + if ( + this.settings.dataSource && + this.settings.dataSource[index] && + index !== false + ) { + this.account = this.settings.dataSource[index]; + } + } catch (e) { + console.log(e); + } + }; + + async actionSettings() { + try { + const table = new UITable(); + const dataSource = this.settings.dataSource || []; + dataSource.map((t, index) => { + const r = new UITableRow(); + r.addText( + `parameter:${index} 机场名:${t.title} 账号:${t.email}` + ); + r.onSelect = (n) => { + this.settings.account = t; + this.notify(t.title, `默认账号设置成功\n账号:${t.email}`); + this.saveSettings(false); + }; + table.addRow(r); + }); + table.present(false); + } catch (e) { + console.log(e); + } + } + + async deletedVpn() { + try { + const table = new UITable(); + const dataSource = this.settings.dataSource || []; + dataSource.map((t, index) => { + const r = new UITableRow(); + r.addText(`❌ 机场名:${t.title} 账号:${t.email}`); + r.onSelect = (n) => { + dataSource.splice(index, 1); + this.settings.dataSource = dataSource; + this.notify(t.title, `❌\n账号:${t.email}`); + this.saveSettings(false); + }; + table.addRow(r); + }); + table.present(false); + } catch (e) { + console.log(e); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", false); // 正式环境 +await Runing(Widget, "", false); //远程开发环境 diff --git a/Scripts/YouTube.js b/Scripts/YouTube.js new file mode 100644 index 0000000..251dbb6 --- /dev/null +++ b/Scripts/YouTube.js @@ -0,0 +1,261 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: magnet; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = "YouTube"; + this.en = "YouTube"; + this.inputValue = + arg || "https://m.youtube.com/channel/UCZVThl_MRppEdGUPGjXSSdg"; + this.url = `${this.baseUrl}/c/${this.inputValue}`; + if (this.inputValue.includes("m.youtube.com")) { + this.url = this.inputValue; + } + this.Run(); + } + + useBoxJS = false; + ytInitialData = {}; + videos = []; + baseUrl = "https://m.youtube.com"; + url; + + init = async () => { + try { + await this.getData(); + } catch (e) { + console.log(e); + } + }; + + getData = async () => { + const url = this.url; + + const webView = new WebView(); + await webView.loadURL(url); + const javascript = `completion(ytInitialData||window['ytInitialData']);`; + this.ytInitialData = await webView + .evaluateJavaScript(javascript, true) + .then(async (e) => { + return typeof e === "string" ? JSON.parse(e) : e; + }); + this.getCell(); + }; + + getCell() { + const { contents } = this.ytInitialData; + let videos = []; + Object.keys(contents).forEach((key) => { + const result = contents[key]; + const homeContent = + result.tabs[0].tabRenderer.content.sectionListRenderer.contents; + homeContent.forEach((item) => { + let contents; + if (item.itemSectionRenderer) { + contents = item.itemSectionRenderer.contents; + contents.forEach((video) => { + if (!video.shelfRenderer) return; + const cateVideo = video.shelfRenderer.content; + if (cateVideo) { + const data = []; + Object.keys(cateVideo).forEach((cv) => { + cateVideo[cv].items.forEach((cell) => { + const cellVideo = this.getCellVideo(cell); + if (cellVideo) data.push(cellVideo); + }); + videos = [...videos, ...data]; + }); + } + }); + } else if (item.shelfRenderer) { + contents = item.shelfRenderer; + contents = contents.content.verticalListRenderer.items; + contents.forEach((compactVideoRenderer) => { + const data = this.getCellVideo2(compactVideoRenderer); + if (data) videos.push(data); + }); + } + }); + }); + this.videos = videos; + } + + getCellVideo(data) { + try { + const { gridVideoRenderer } = data; + return { + thumb: gridVideoRenderer.thumbnail.thumbnails[0].url, + title: gridVideoRenderer.title.simpleText, + view: gridVideoRenderer.viewCountText.simpleText, + url: + gridVideoRenderer.navigationEndpoint.commandMetadata + .webCommandMetadata.url, + }; + } catch (e) { + console.log(e); + } + } + + getCellVideo2(data) { + try { + const { compactVideoRenderer } = data; + return { + thumb: compactVideoRenderer.thumbnail.thumbnails[0].url, + title: compactVideoRenderer.title.runs[0].text, + view: compactVideoRenderer.viewCountText.runs[0].text, + url: + compactVideoRenderer.navigationEndpoint.commandMetadata + .webCommandMetadata.url, + }; + } catch (e) { + console.log(e); + } + } + + setAvatar = async (stack) => { + stack.size = new Size(50, 50); + stack.cornerRadius = 5; + const { + metadata: { channelMetadataRenderer }, + } = this.ytInitialData; + const avatar = channelMetadataRenderer.avatar.thumbnails.find( + (item) => item.url + ); + const imgLogo = await this.$request.get(avatar, "IMG"); + const imgLogoItem = stack.addImage(imgLogo); + imgLogoItem.imageSize = new Size(50, 50); + return stack; + }; + + setTitleStack = (stack) => { + const textFormatNumber = this.textFormat.title; + textFormatNumber.color = this.backGroundColor; + const { + metadata: { channelMetadataRenderer }, + } = this.ytInitialData; + const title = channelMetadataRenderer.title; + textFormatNumber.size = + title.length > 20 || this.widgetFamily === "small" ? 16 : 20; + const titleItem = this.provideText(title, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setPathStack = (stack) => { + const textFormatNumber = this.textFormat.defaultText; + textFormatNumber.color = new Color("#2481cc"); + textFormatNumber.size = 12; + const { header } = this.ytInitialData; + let simpleText = ""; + Object.keys(header).forEach((key) => { + const item = header[key]; + if (item.subscriberCountText && !simpleText) { + simpleText = item.subscriberCountText.simpleText; + } + if (!simpleText && item.subscribeButton) { + simpleText = item.subscriberCountText.runs[0].text; + } + }); + const titleItem = this.provideText(simpleText, stack, textFormatNumber); + titleItem.lineLimit = 1; + }; + + setFooterCell = async (stack) => { + const datas = this.getRandomArrayElements(this.videos, 3); + for (let i = 0; i < datas.length; i++) { + if (i === 1) stack.addSpacer(); + const video = datas[i]; + const stackVideo = stack.addStack(); + stackVideo.setPadding(10, 10, 10, 10); + stackVideo.url = this.baseUrl + video.url; + stackVideo.backgroundColor = this.widgetColor; + stackVideo.centerAlignContent(); + stackVideo.layoutVertically(); + const img = await this.$request.get(video.thumb, "IMG"); + stackVideo.backgroundImage = await this.shadowImage(img, "#000", 0.3); + const title = { ...this.textFormat.defaultText }; + title.size = 8; + title.color = new Color("#fff"); + this.provideText(video.title, stackVideo, title); + stackVideo.addSpacer(); + title.color = new Color("#fff", 0.7); + this.provideText(video.view, stackVideo, title); + stackVideo.size = new Size(87, 56); + stackVideo.cornerRadius = 4; + if (i === 1) stack.addSpacer(); + } + }; + + renderSmall = async (w) => { + return w; + }; + + renderMedium = async (w) => { + const stackBody = w.addStack(); + stackBody.url = this.url; + stackBody.layoutVertically(); + const stackHeader = stackBody.addStack(); + stackHeader.setPadding(5, 10, 5, 10); + stackHeader.cornerRadius = 10; + stackHeader.backgroundColor = this.widgetColor; + + stackHeader.centerAlignContent(); + const stackLeft = stackHeader.addStack(); + await this.setAvatar(stackLeft); + stackHeader.addSpacer(20); + + const stackRight = stackHeader.addStack(); + stackRight.layoutVertically(); + this.setTitleStack(stackRight); + stackRight.addSpacer(5); + this.setPathStack(stackRight); + stackHeader.addSpacer(); + stackBody.addSpacer(); + + const stackFooter = stackBody.addStack(); + stackFooter.setPadding(10, 0, 10, 0); + stackFooter.cornerRadius = 10; + stackFooter.backgroundColor = this.widgetColor; + stackFooter.addSpacer(); + await this.setFooterCell(stackFooter); + stackFooter.addSpacer(); + return w; + }; + + renderLarge = async (w) => { + return w; + }; + + Run() { + if (config.runsInApp) { + this.registerAction("基础设置", this.setWidgetConfig); + } + } + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "medium") { + return await this.renderMedium(widget); + } else if (this.widgetFamily === "large") { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +// await Runing(Widget, "", true); // 正式环境 +await Runing(Widget, args.widgetParameter, false); //远程开发环境 diff --git a/Scripts/ZXTrains.js b/Scripts/ZXTrains.js index 48b3124..d6d24b8 100644 --- a/Scripts/ZXTrains.js +++ b/Scripts/ZXTrains.js @@ -7,224 +7,240 @@ // 2.自动获取待出行列表:https://raw.githubusercontent.com/dompling/Script/master/ZXTrians/ZXTrains.js 按照脚本内容配置 // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 -if (typeof require === "undefined") require = importModule; -const { DmYY, Runing } = require("./DmYY"); +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); // @组件代码开始 class Widget extends DmYY { - constructor(arg) { - super(arg); - this.name = "智行火车票"; - this.en = "ZXTrains"; - this.logo = "https://raw.githubusercontent.com/Orz-3/task/master/zxhc.png"; - this.cacheName = this.md5(`dataSouce_${this.en}`); - } - - dataSource = []; - - init = async () => { - try { - this.dataSource = await this.getTrainsList(); - } catch (e) { - console.log(e); - } - }; - - dateToUnixTimestamp(str) { - const dates = new Date(str.replace(/-/g, "/")); - return parseInt(dates.getTime()); - } - - timeAgo(o) { - var n = new Date().getTime(); - var f = n - o; - var bs = f >= 0 ? "前" : "后"; //判断时间点是在当前时间的 之前 还是 之后 - f = Math.abs(f); - if (f < 6e4) { - return "刚刚"; - } //小于60秒,刚刚 - if (f < 36e5) { - return parseInt(f / 6e4) + "分钟" + bs; - } //小于1小时,按分钟 - if (f < 864e5) { - return parseInt(f / 36e5) + "小时" + bs; - } //小于1天按小时 - if (f < 2592e6) { - return parseInt(f / 864e5) + "天" + bs; - } //小于1个月(30天),按天数 - if (f < 31536e6) { - return parseInt(f / 2592e6) + "个月" + bs; - } //小于1年(365天),按月数 - return parseInt(f / 31536e6) + "年" + bs; //大于365天,按年算 - } - - async getTrainsList() { - try { - const travels = await this.getCache("@ZXTrains.travels"); - console.log(travels); - if (travels) return travels; - } catch (e) { - console.log("未找到火车票缓存:" + e); - } - return false; - } - - setWidget = async (body) => { - let isNone = true; - for (const item of this.dataSource) { - let { trainFlights, timeDesc } = item.orders[0]; - const data = trainFlights[0]; - const passengerInfos = data.passengerInfos[0]; - const fromDate = this.dateToUnixTimestamp(data.fromTime); - const toDate = this.dateToUnixTimestamp(data.toTime); - const nowDate = parseInt(new Date().getTime()); - if (fromDate - nowDate < 1000 * 60 * 60 * 24 && nowDate < toDate) { - const header = body.addStack(); - this.name = data.title; - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - body.addSpacer(); - - const container = body.addStack(); - container.url = "suanya://"; - container.layoutVertically(); - const timeView = container.addStack(); - timeView.setPadding(10, 10, 10, 10); - timeView.backgroundColor = new Color("#1890ff"); - timeView.cornerRadius = 5; - - const left = timeView.addStack(); - left.layoutVertically(); - left.addSpacer(); - const leftTimer = left.addText(data.showFromTime); - leftTimer.font = Font.boldSystemFont(16); - leftTimer.textColor = Color.white(); - left.addSpacer(); - const leftDesc = left.addText(data.fromCityName); - leftDesc.font = Font.lightSystemFont(12); - leftDesc.textColor = Color.white(); - left.addSpacer(); - - timeView.addSpacer(); - - const center = timeView.addStack(); - center.addSpacer(); - center.layoutVertically(); - const image = await this.$request.get(data.trafficIcon, "IMG"); - const imageView = center.addImage(image); - imageView.imageSize = new Size(40, 40); - center.addSpacer(); - timeView.addSpacer(); - - const right = timeView.addStack(); - right.layoutVertically(); - right.addSpacer(); - const rightTimer = right.addText(data.showToTime); - rightTimer.font = Font.boldSystemFont(16); - rightTimer.textColor = Color.white(); - right.addSpacer(); - const rightDesc = right.addText(data.toCityName); - rightDesc.font = Font.lightSystemFont(12); - rightDesc.textColor = Color.white(); - right.addSpacer(); - - const footerView = container.addStack(); - footerView.setPadding(10, 10, 10, 10); - timeDesc = nowDate < fromDate ? `距离发车还有${this.timeAgo(fromDate)}` : `距离到达还有${this.timeAgo(toDate)}`; - const footerLeftText = footerView.addText(timeDesc); - footerLeftText.font = Font.boldSystemFont(12); - footerLeftText.textColor = this.widgetColor; - footerLeftText.textOpacity = 0.8; - - footerView.addSpacer(); - const footerRightText = footerView.addText( - `${passengerInfos.seatCategory} ${passengerInfos.carriageNo} ${passengerInfos.seatNo} `, - ); - footerRightText.font = Font.boldSystemFont(12); - footerRightText.textColor = this.widgetColor; - footerRightText.textOpacity = 0.8; - - isNone = false; - break; - } - } - if (isNone) await this.renderNone(body); - body.addStack(); - return body; - }; - - renderSmall = async (w) => { - return this.renderLarge(w); - }; - - renderLarge = async (w) => { - const header = w.addStack(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - w.addSpacer(20); - const text = w.addText("暂不支持"); - text.font = Font.boldSystemFont(20); - text.textColor = this.widgetColor; - w.addSpacer(); - return w; - }; - - renderMedium = async (w) => { - return await this.setWidget(w); - }; - - renderNone = async (widget) => { - const header = widget.addStack(); - await this.renderHeader(header, this.logo, this.name, this.widgetColor); - widget.addSpacer(); - const bodyIcon = await this.$request.get( - "https://images3.c-ctrip.com/ztrip/img/dcx_HUOCHE.png", - "IMG", - ); - - const body = widget.addStack(); - body.url = "suanya://"; - - const container = body.addStack(); - container.layoutVertically(); - const bodyIconView = container.addStack(); - bodyIconView.cornerRadius = 5; - - bodyIconView.addSpacer(); - bodyIconView.backgroundColor = new Color("#1890ff"); - const bodyIconItem = bodyIconView.addImage(bodyIcon); - bodyIconItem.imageSize = new Size(90, 60); - bodyIconView.addSpacer(); - - container.addSpacer(20); - const noneView = container.addStack(); - - noneView.addSpacer(); - const noneText = noneView.addText("暂无未出行行程"); - noneText.font = Font.boldSystemFont(14); - noneText.textColor = this.widgetColor; - noneView.addSpacer(); - - return widget; - }; - - /** - * 渲染函数,函数名固定 - * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 - */ - async render() { - await this.init(); - const widget = new ListWidget(); - await this.getWidgetBackgroundImage(widget); - if (this.widgetFamily === "medium") { - await this.renderMedium(widget); - } else if (this.widgetFamily === "large") { - await this.renderLarge(widget); - } else { - await this.renderSmall(widget); - } - return widget; - } + constructor(arg) { + super(arg); + this.name = '智行火车票'; + this.en = 'ZXTrains'; + this.logo = + 'https://raw.githubusercontent.com/Orz-3/mini/master/Color/zxhc.png'; + this.cacheName = this.md5(`dataSouce_${this.en}`); + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + dataSource = []; + + init = async () => { + try { + this.dataSource = await this.getTrainsList(); + } catch (e) { + console.log(e); + } + }; + + dateToUnixTimestamp(str) { + const dates = new Date(str.replace(/-/g, '/')); + return parseInt(dates.getTime()); + } + + timeAgo(o) { + var n = new Date().getTime(); + var f = n - o; + var bs = f >= 0 ? '前' : '后'; //判断时间点是在当前时间的 之前 还是 之后 + f = Math.abs(f); + if (f < 6e4) { + return '刚刚'; + } //小于60秒,刚刚 + if (f < 36e5) { + return parseInt(f / 6e4) + '分钟' + bs; + } //小于1小时,按分钟 + if (f < 864e5) { + return parseInt(f / 36e5) + '小时' + bs; + } //小于1天按小时 + if (f < 2592e6) { + return parseInt(f / 864e5) + '天' + bs; + } //小于1个月(30天),按天数 + if (f < 31536e6) { + return parseInt(f / 2592e6) + '个月' + bs; + } //小于1年(365天),按月数 + return parseInt(f / 31536e6) + '年' + bs; //大于365天,按年算 + } + + async getTrainsList() { + try { + const travels = await this.getCache('@ZXTrains.travels'); + console.log(travels); + if (travels) return travels; + } catch (e) { + console.log('未找到火车票缓存:' + e); + } + return false; + } + + setWidget = async (body) => { + let isNone = true; + try { + for (const item of this.dataSource) { + let { trainFlights, timeDesc } = item.orders[0]; + const data = trainFlights[0]; + const passengerInfos = data.passengerInfos[0]; + const fromDate = this.dateToUnixTimestamp(data.fromTime); + const toDate = this.dateToUnixTimestamp(data.toTime); + const nowDate = parseInt(new Date().getTime()); + if (fromDate - nowDate < 1000 * 60 * 60 * 24 && nowDate < toDate) { + const header = body.addStack(); + this.name = data.title; + await this.renderHeader( + header, + this.logo, + this.name, + this.widgetColor, + ); + body.addSpacer(); + + const container = body.addStack(); + container.url = 'suanya://'; + container.layoutVertically(); + const timeView = container.addStack(); + timeView.setPadding(10, 10, 10, 10); + timeView.backgroundColor = new Color('#1890ff'); + timeView.cornerRadius = 5; + + const left = timeView.addStack(); + left.layoutVertically(); + left.addSpacer(); + const leftTimer = left.addText(data.showFromTime); + leftTimer.font = Font.boldSystemFont(16); + leftTimer.textColor = Color.white(); + left.addSpacer(); + const leftDesc = left.addText(data.fromCityName); + leftDesc.font = Font.lightSystemFont(12); + leftDesc.textColor = Color.white(); + left.addSpacer(); + + timeView.addSpacer(); + + const center = timeView.addStack(); + center.addSpacer(); + center.layoutVertically(); + const image = await this.$request.get(data.trafficIcon, 'IMG'); + const imageView = center.addImage(image); + imageView.imageSize = new Size(40, 40); + center.addSpacer(); + timeView.addSpacer(); + + const right = timeView.addStack(); + right.layoutVertically(); + right.addSpacer(); + const rightTimer = right.addText(data.showToTime); + rightTimer.font = Font.boldSystemFont(16); + rightTimer.textColor = Color.white(); + right.addSpacer(); + const rightDesc = right.addText(data.toCityName); + rightDesc.font = Font.lightSystemFont(12); + rightDesc.textColor = Color.white(); + right.addSpacer(); + + const footerView = container.addStack(); + footerView.setPadding(10, 10, 10, 10); + timeDesc = + nowDate < fromDate + ? `距离发车还有${this.timeAgo(fromDate)}` + : `距离到达还有${this.timeAgo(toDate)}`; + const footerLeftText = footerView.addText(timeDesc); + footerLeftText.font = Font.boldSystemFont(12); + footerLeftText.textColor = this.widgetColor; + footerLeftText.textOpacity = 0.8; + + footerView.addSpacer(); + const footerRightText = footerView.addText( + `${passengerInfos.seatCategory} ${passengerInfos.carriageNo} ${passengerInfos.seatNo} `, + ); + footerRightText.font = Font.boldSystemFont(12); + footerRightText.textColor = this.widgetColor; + footerRightText.textOpacity = 0.8; + + isNone = false; + break; + } + } + } catch (e) { + console.log(e); + } + if (isNone) await this.renderNone(body); + body.addStack(); + return body; + }; + + renderSmall = async (w) => { + return this.renderLarge(w); + }; + + renderLarge = async (w) => { + const header = w.addStack(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + w.addSpacer(20); + const text = w.addText('暂不支持'); + text.font = Font.boldSystemFont(20); + text.textColor = this.widgetColor; + w.addSpacer(); + return w; + }; + + renderMedium = async (w) => { + return await this.setWidget(w); + }; + + renderNone = async (widget) => { + const header = widget.addStack(); + await this.renderHeader(header, this.logo, this.name, this.widgetColor); + widget.addSpacer(); + const bodyIcon = await this.$request.get( + 'https://images3.c-ctrip.com/ztrip/img/dcx_HUOCHE.png', + 'IMG', + ); + + const body = widget.addStack(); + body.url = 'suanya://'; + + const container = body.addStack(); + container.layoutVertically(); + const bodyIconView = container.addStack(); + bodyIconView.cornerRadius = 5; + + bodyIconView.addSpacer(); + bodyIconView.backgroundColor = new Color('#1890ff'); + const bodyIconItem = bodyIconView.addImage(bodyIcon); + bodyIconItem.imageSize = new Size(90, 60); + bodyIconView.addSpacer(); + + container.addSpacer(20); + const noneView = container.addStack(); + + noneView.addSpacer(); + const noneText = noneView.addText('暂无未出行行程'); + noneText.font = Font.boldSystemFont(14); + noneText.textColor = this.widgetColor; + noneView.addSpacer(); + + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + await this.renderLarge(widget); + } else { + await this.renderSmall(widget); + } + return widget; + } } // @组件代码结束 // await Runing(Widget, "", false); // 正式环境 -await Runing(Widget, "", false); //远程开发环境 +await Runing(Widget, '', false); //远程开发环境 diff --git a/Scripts/birthday.js b/Scripts/birthday.js new file mode 100644 index 0000000..255acf8 --- /dev/null +++ b/Scripts/birthday.js @@ -0,0 +1,694 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: car; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === "undefined") require = importModule; +const { DmYY, Runing } = require("./DmYY"); +let mainTextSize = 13; // 倒数、农历、生日文字大小 + +let widthMode = 110; // 中号组件图片尺寸 + +let heightMode = 100; // 中号组件图片尺寸 + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = "birthday"; + this.name = "破壳日"; + this.LEFT_IMG_KEY = `${this.cacheImage}/avatar`; + + if (config.runsInApp) { + this.registerAction({ + title: "头像设置", + menu: [ + { + icon: { name: "person.badge.plus", color: "#52c41a" }, + type: "img", + title: "头像", + name: "avatar", + val: this.cacheImage, + }, + { + icon: { name: "arrow.left.and.right", color: "#13c2c2" }, + type: "input", + title: "头像宽度", + name: "avatarWidth", + }, + { + icon: { name: "arrow.up.and.down", color: "#1890ff" }, + type: "input", + title: "头像高度", + name: "avatarHeight", + }, + ], + }); + + this.registerAction({ + icon: { name: "a.square", color: "#eb2f96" }, + type: "input", + title: "主文字大小", + name: "mainTextSize", + }); + + this.registerAction({ + icon: { + name: "rectangle.and.pencil.and.ellipsis", + color: "#f5222d", + }, + type: "input", + title: "昵称", + placeholder: "用户昵称", + name: "nickname", + }); + + this.registerAction({ + icon: { + name: "rectangle", + color: "#a68585", + }, + type: "color", + title: "昵称阴影", + placeholder: "昵称阴影", + name: "nicknameShadow", + }); + + this.registerAction({ + icon: { + name: "bubble.left", + color: "#faf61c", + }, + type: "input", + title: "寄语", + name: "bless", + }); + + this.registerAction({ + icon: { + name: "25.square.fill", + color: "#fa541c", + }, + type: "switch", + title: "农历", + name: "nongli", + }); + + this.registerAction({ + icon: { + name: "calendar", + color: "#fa8c16", + }, + type: "date", + title: "破壳日", + name: "birthday", + }); + + this.registerAction({ + icon: { + name: "calendar.badge.clock", + color: "#8016fa", + }, + type: "date", + title: "相识", + name: "eday", + }); + + this.registerAction("基础设置", this.setWidgetConfig); + } + } + + getAge = (beginStr) => { + let tmpBirth = {}; + tmpBirth.year = 0; + tmpBirth.month = 0; + tmpBirth.day = 0; + + if (beginStr == null || beginStr == "") { + return; + } + let startDate = new Date(beginStr.replace(/-/g, "/")); + let today = new Date(); + + let startYear = startDate.getFullYear(); + let endYear = today.getFullYear(); + let startMonth = startDate.getMonth() + 1; + let endMonth = today.getMonth() + 1; + let startDay = startDate.getDate(); + let endDay = today.getDate(); + let allDays = 0; + //准确的每个月天数数组,判断闰年平年。 + let allDayArr = []; + //当月计算 + if (startYear == endYear && startMonth == endMonth) { + tmpBirth.day = endDay - startDay; + return tmpBirth; + } + //正常计算 + for (let i = startYear; i <= endYear; i++) { + let currYear = 365; + let yearMonth = 12; + if ((i % 4 == 0 && i % 100 !== 0) || i % 400 == 0) { + allDays += 366; + currYear = 366; + } + let currMonth = 1; + if (i == startYear) { + currMonth = startMonth; + } + if (i == endYear) { + yearMonth = endMonth; + } + //闰年计算 + for (let m = currMonth; m <= yearMonth; m++) { + let fullDays = 30; + + if (m == 1 || m == 3 || m == 8 || m == 10 || m == 12) { + fullDays = 31; + } else if (m == 2) { + if ((i % 4 == 0 && i % 100 !== 0) || i % 400 == 0) { + fullDays = 29; + } else { + fullDays = 28; + } + } + let dayObj = { + fullDays: fullDays, + currDays: fullDays, + }; + if (m == startMonth && i == startYear) { + dayObj = { + fullDays: fullDays, + currDays: fullDays - startDay, + }; + } else if (m == endMonth && i == endYear) { + dayObj = { + fullDays: fullDays, + currDays: endDay, + }; + } + allDayArr.push(dayObj); + } + } + + if (allDayArr.length == 1) { + tmpBirth.day = allDayArr[0].currDays; + } else if (allDayArr.length == 2) { + var d1 = allDayArr[0].currDays; + var d2 = allDayArr[1].currDays; + //月份天数浮动因子决定准确性 + let cfDay = + allDayArr[0].fullDays > allDayArr[allDayArr.length - 1].fullDays + ? allDayArr[allDayArr.length - 1].fullDays + : allDayArr[0].fullDays; + if (d1 + d2 >= cfDay) { + tmpBirth.day = d1 + d2 - cfDay; + tmpBirth.month += 1; + } else { + tmpBirth.day = d1 + d2; + } + } else { + let d1 = allDayArr[0].currDays; + let d2 = allDayArr[allDayArr.length - 1].currDays; + let sumFullDay = 0; + for (let i = 0; i < allDayArr.length; i++) { + sumFullDay += allDayArr[i].fullDays; + } + //月份天数浮动因子决定准确性 + let cfDay = + allDayArr[0].fullDays > allDayArr[allDayArr.length - 1].fullDays + ? allDayArr[allDayArr.length - 1].fullDays + : allDayArr[0].fullDays; + if (d1 + d2 >= cfDay) { + tmpBirth.day = d1 + d2 - cfDay; + tmpBirth.month += 1; + } else { + tmpBirth.day = d1 + d2; + } + tmpBirth.month += allDayArr.length - 2; + + if (tmpBirth.month >= 12) { + tmpBirth.year += Math.floor(tmpBirth.month / 12); + tmpBirth.month = tmpBirth.month - tmpBirth.year * 12; + } + } + return tmpBirth; + }; + + daysBetween = (d) => { + let now = new Date(); + let date = new Date(d.cYear, d.cMonth - 1, d.cDay); + return parseInt((date.getTime() - now.getTime()) / (24 * 3600 * 1000)); + }; + + getAstroToEmoji = (astro) => { + const data = { + 白羊座: "♈", + 金牛座: "♉", + 双子座: "♊", + 巨蟹座: "♋", + 狮子座: "♌", + 处女座: "♍", + 天秤座: "♎", + 天蝎座: "♏", + 射手座: "♐", + 摩羯座: "♑", + 水瓶座: "♒", + 双鱼座: "♓", + 蛇夫座: "⛎", + }; + return data[astro] || ""; + }; + + getAnimalZodiacToEmoji = (zodiac) => { + const data = { + 鼠: "🐭", + 牛: "🐂", + 虎: "🐯", + 兔: "🐇", + 龙: "🐲", + 蛇: "🐍", + 马: "🐴", + 羊: "🐑", + 猴: "🐵", + 鸡: "🐔", + 狗: "🐶", + 猪: "🐷", + }; + return data[zodiac] || ""; + }; + + verifyTime(date) { + let dateFormat = /^(\d{4})-(\d{1,2})-(\d{1,2})$/; + return dateFormat.test(date); + } + + getEdayNumber = (date) => { + var initDay = date.split("-"); + var obj = { + cYear: parseInt(initDay[0]), + cMonth: parseInt(initDay[1]), + cDay: parseInt(initDay[2]), + }; + return Math.abs(this.daysBetween(obj)); + }; + + ajax = async (opt) => { + const type = opt.nongli ? "lunar" : "solar"; + return ( + await this.$request.post(`https://www.iamwawa.cn/home/nongli/ajax`, { + body: `type=${type}&year=${opt.year}&month=${opt.month}&day=${opt.day}`, + }) + ).data; + }; + + init = async () => { + widthMode = Number(this.settings.avatarWidth) || widthMode; + heightMode = Number(this.settings.avatarHeight) || heightMode; + mainTextSize = Number(this.settings.mainTextSize) || mainTextSize; + + await this.FILE_MGR.fileExistsExtra(this.LEFT_IMG_KEY); + this.defaultData = { + username: this.settings.nickname || "", // 姓名 + time: this.settings.birthday || "2022-12-19", // 生日日期 + nongli: this.settings.nongli === "true" || "", // 农历生日 + eday: this.settings.eday || "2022-12-19", //相识 + bless: this.settings.bless || "", + nicknameShadow: this.settings.nicknameShadow || "#e8e8e8", + isLeapMonth: false, //如果是农历闰月第四个参数赋值true即可 + }; + + const { time, nongli, isLeapMonth, eday } = this.defaultData; + const _data = time.split("-"); + const opt = { + year: parseInt(_data[0]), + month: parseInt(_data[1]), + day: parseInt(_data[2]), + nongli, + isLeapMonth, + }; + + if (this.settings.ajax) { + this.ajax(opt).then((res) => { + this.settings.ajax = res; + this.saveSettings(false); + }); + } else { + this.settings.ajax = await this.ajax(opt); + } + this.saveSettings(false); + const response = this.settings.ajax; + + response.animalEmoji = `${this.getAnimalZodiacToEmoji(response.sx)}`; + response.astro = `${this.getAstroToEmoji(response.xz)}`; + + if (this.verifyTime(eday)) { + response.meetDay = this.getEdayNumber(eday); + } + + this.contentText = { ...response, data: {} }; + + this.contentText.this_year_lunar_solar = + this.contentText.this_year_lunar_solar + .replace("年", "-") + .replace("月", "-") + .replace("日", ""); + this.contentText.next_year_lunar_solar = + this.contentText.next_year_lunar_solar + .replace("年", "-") + .replace("月", "-") + .replace("日", ""); + this.contentText.solar = this.contentText.solar + .replace("年", "-") + .replace("月", "-") + .replace("日", ""); + + const tmpBirth = this.getAge(this.defaultData.time); + let ageYear = tmpBirth.year > 0 ? `${tmpBirth.year}岁` : ""; + let ageMonth = tmpBirth.month > 0 ? `${tmpBirth.month}月` : ""; + let ageDay = tmpBirth.day > 0 ? `${tmpBirth.day}天` : "1天"; + const age = ageYear + ageMonth + ageDay; + const dayIcon = tmpBirth.day + ".circle.fill"; + + this.contentText.data = { + tmpBirth, + ageYear, + ageMonth, + ageDay, + age, + dayIcon, + }; + }; + + rowCell = (widget, { icon, color, title, text, dayImage = false }) => { + const subWidget = widget.addStack(); + subWidget.centerAlignContent(); + + const subImg = subWidget.addImage(SFSymbol.named(icon).image); + subImg.tintColor = new Color(color); + subImg.imageSize = new Size(mainTextSize, mainTextSize); + subWidget.addSpacer(4); + const subTitle = subWidget.addText(title || ""); + subTitle.font = Font.systemFont(mainTextSize); + subTitle.textColor = this.widgetColor; + subWidget.addSpacer(); + const subValue = subWidget.addText(text || ""); + subValue.font = Font.systemFont(mainTextSize); + subValue.textColor = this.widgetColor; + subValue.lineLimit = 1; + + if (dayImage) { + subWidget.addSpacer(2); + let dayIcon = subWidget.addImage(SFSymbol.named(dayImage).image); + dayIcon.imageSize = new Size(mainTextSize + 1, mainTextSize + 1); + dayIcon.tintColor = new Color("#1ab6f8"); + } + }; + + animalImg = (text) => { + const { this_year_lunar_solar, solar } = this.contentText; + + const nextBirthday = { + year: this_year_lunar_solar.split("-")[0], + month: this_year_lunar_solar.split("-")[1], + day: this_year_lunar_solar.split("-")[2], + }; + + const preData = { + year: solar.split("-")[0], + month: solar.split("-")[1], + day: solar.split("-")[2], + }; + + const extraTextColor = "fc8ac3"; //环形进度条中心背景颜色及名字、meetDay颜色 + const ringColor = "fc5ead"; //环形进度条颜色 + const canvSize = 172; + const canvTextSize = 45; + const canvas = new DrawContext(); + const canvWidth = 12; + const canvRadius = 80; + const cbgColor = new Color(ringColor, 0.2); + const cfgColor = new Color(ringColor); + const centerColor = new Color(extraTextColor); + const cfontColor = new Color("ffffff"); + canvas.size = new Size(canvSize, canvSize); + canvas.opaque = false; + canvas.respectScreenScale = true; + + const today = new Date(); + const thenDate = new Date( + `${nextBirthday.year}`, + `${nextBirthday.month}` - 1, + `${nextBirthday.day}` + ); + + const passDate = new Date(preData.year, preData.month - 1, preData.day); + + const gap = today.getTime() - passDate.getTime(); + const gap2 = thenDate.getTime() - passDate.getTime(); + const deg = Math.floor((gap / gap2) * 100 * 3.6); + + let ctr = new Point(canvSize / 2, canvSize / 2); + const bgx = ctr.x - canvRadius; + const bgy = ctr.y - canvRadius; + const bgd = 2 * canvRadius; + const bgr = new Rect(bgx, bgy, bgd, bgd); + + canvas.setFillColor(cfgColor); + canvas.setStrokeColor(cbgColor); + canvas.setLineWidth(canvWidth); + canvas.strokeEllipse(bgr); + + for (let t = 0; t < deg; t++) { + const rect_x = + ctr.x + canvRadius * Math.sin((t * Math.PI) / 180) - canvWidth / 2; + const rect_y = + ctr.y - canvRadius * Math.cos((t * Math.PI) / 180) - canvWidth / 2; + const rect_r = new Rect(rect_x, rect_y, canvWidth, canvWidth); + canvas.fillEllipse(rect_r); + } + + const ringBG = new Rect( + bgx + canvWidth / 2 + 8, + bgy + canvWidth / 2 + 8, + canvRadius * 2 - canvWidth - 16, + canvRadius * 2 - canvWidth - 16 + ); + canvas.setFillColor(centerColor); + canvas.setLineWidth(0); + canvas.fillEllipse(ringBG); + + const canvTextRect = new Rect(0, 70 - canvTextSize / 2, canvSize, 80); + canvas.setTextAlignedCenter(); + canvas.setTextColor(cfontColor); + canvas.setFont(Font.mediumRoundedSystemFont(canvTextSize)); + canvas.setFont(this.provideFont("ultralight", 68)); + canvas.drawTextInRect(`${text}`, canvTextRect); + + return canvas.getImage(); + }; + + renderMedium = (widget) => { + const { + this_year_lunar_solar, + lunar_date, + animalEmoji, + meetDay, + data: { tmpBirth, ageYear, ageMonth, age, dayIcon }, + } = this.contentText; + + const phoneSize = Device.screenSize(); + const radio = phoneSize.width / phoneSize.height; + const containerStack = widget.addStack(); + containerStack.layoutHorizontally(); + + const leftStack = containerStack.addStack(); + leftStack.size = new Size(radio * widthMode, radio * heightMode); + let avatarImg = SFSymbol.named(`photo`).image; + if (this.FILE_MGR.fileExists(this.LEFT_IMG_KEY)) { + avatarImg = Image.fromFile(this.LEFT_IMG_KEY); + } + leftStack.backgroundImage = avatarImg; + containerStack.addSpacer(); + const rightStack = containerStack.addStack(); + rightStack.setPadding(0, 0, 0, 10); + rightStack.layoutVertically(); + rightStack.centerAlignContent(); + + rightStack.addSpacer(); + + const userStack = rightStack.addStack(); + userStack.layoutHorizontally(); + userStack.centerAlignContent(); + + const nameStack = userStack.addStack(); + nameStack.layoutVertically(); + + const userWidgetText = nameStack.addText(this.defaultData.username); + userWidgetText.textColor = this.widgetColor; + userWidgetText.font = this.provideFont("italic", mainTextSize + 10); + userWidgetText.shadowColor = new Color(this.defaultData.nicknameShadow); + userWidgetText.shadowOffset = new Point(3, 3); + userWidgetText.shadowRadius = 3; + + nameStack.addSpacer(5); + this.provideText(`相遇${meetDay}天`, nameStack, { + font: "Party Let", + size: mainTextSize, + opacity: 0.8, + }); + + userStack.addSpacer(); + + userStack.addImage(this.animalImg(animalEmoji)); + + rightStack.addSpacer(20); + if (tmpBirth.year > 0 && tmpBirth.month > 0 && tmpBirth.day > 0) { + this.rowCell(rightStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: `${ageYear + ageMonth} ${tmpBirth.day} 天`, + }); + } else { + this.rowCell(rightStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: age, + }); + } + rightStack.addSpacer(); + + this.rowCell(rightStack, { + icon: "calendar", + color: "#30d15b", + title: "农历", + text: `${lunar_date}`, + }); + + rightStack.addSpacer(); + + this.rowCell(rightStack, { + icon: "app.gift.fill", + color: "#fc6d6d", + title: "生日", + text: `${this_year_lunar_solar}`, + }); + + rightStack.addSpacer(); + + return widget; + }; + + renderSmall = (widget) => { + const { + this_year_lunar_solar, + lunar_date, + meetDay, + data: { tmpBirth, ageYear, ageMonth, age, dayIcon }, + } = this.contentText; + + const containerStack = widget.addStack(); + containerStack.layoutVertically(); + + containerStack.addSpacer(); + + const topStack = containerStack.addStack(); + topStack.layoutHorizontally(); + topStack.centerAlignContent(); + + const avatarStack = topStack.addStack(); + let avatarImg = SFSymbol.named(`photo`).image; + if (this.FILE_MGR.fileExists(this.LEFT_IMG_KEY)) { + avatarImg = Image.fromFile(this.LEFT_IMG_KEY); + } + + avatarStack.backgroundImage = avatarImg; + avatarStack.size = new Size(42, 42); + avatarStack.cornerRadius = avatarStack.size.width / 2; + avatarStack.borderColor = Color.green(); + avatarStack.borderWidth = 1; + + topStack.addSpacer(20); + + const nameStack = topStack.addStack(); + nameStack.addSpacer(); + nameStack.layoutVertically(); + nameStack.centerAlignContent(); + + const userWidgetText = nameStack.addText(this.defaultData.username); + userWidgetText.textColor = this.widgetColor; + userWidgetText.font = this.provideFont("italic", 16); + userWidgetText.shadowColor = new Color(this.defaultData.nicknameShadow); + userWidgetText.shadowOffset = new Point(3, 3); + userWidgetText.shadowRadius = 3; + + nameStack.addSpacer(5); + this.provideText(`相遇${meetDay}天`, nameStack, { + font: "Party Let", + size: 12, + opacity: 0.8, + }); + + containerStack.addSpacer(); + + if (tmpBirth.year > 0 && tmpBirth.month > 0 && tmpBirth.day > 0) { + this.rowCell(containerStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: `${ageYear + ageMonth} ${tmpBirth.day} 天`, + }); + } else { + this.rowCell(containerStack, { + icon: "hourglass", + color: "#1ab6f8", + title: "年龄", + text: age, + }); + } + containerStack.addSpacer(); + + this.rowCell(containerStack, { + icon: "calendar", + color: "#30d15b", + title: "农历", + text: `${lunar_date}`, + }); + + containerStack.addSpacer(); + + this.rowCell(containerStack, { + icon: "app.gift.fill", + color: "#fc6d6d", + title: "生日", + text: `${this_year_lunar_solar}`, + }); + + containerStack.addSpacer(); + + return widget; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === "medium") { + widget.setPadding(0, 0, 0, 0); + return await this.renderMedium(widget); + } else if (this.widgetFamily === "large") { + return await this.renderLarge(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget); //远程开发环境 diff --git a/Scripts/crypto-js.min.js b/Scripts/crypto-js.min.js new file mode 100644 index 0000000..14b6acc --- /dev/null +++ b/Scripts/crypto-js.min.js @@ -0,0 +1,6 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: teal; icon-glyph: cogs; + +(function(t,e){"object"==typeof exports?module.exports=exports=e():"function"==typeof define&&define.amd?define([],e):t.CryptoJS=e()})(this,function(){var t,e,r,i,n,o,s,c,a,h,l=l||function(t,e){var r;if("undefined"!=typeof window&&window.crypto&&(r=window.crypto),!r&&"undefined"!=typeof window&&window.msCrypto&&(r=window.msCrypto),!r&&"undefined"!=typeof global&&global.crypto&&(r=global.crypto),!r&&"function"==typeof require)try{r=require("crypto")}catch(t){}var i=function(){if(r){if("function"==typeof r.getRandomValues)try{return r.getRandomValues(new Uint32Array(1))[0]}catch(t){}if("function"==typeof r.randomBytes)try{return r.randomBytes(4).readInt32LE()}catch(t){}}throw new Error("Native crypto module could not be used to get secure random number.")},n=Object.create||function(){function t(){}return function(e){var r;return t.prototype=e,r=new t,t.prototype=null,r}}(),o={},s=o.lib={},c=s.Base={extend:function(t){var e=n(this);return t&&e.mixIn(t),e.hasOwnProperty("init")&&this.init!==e.init||(e.init=function(){e.$super.init.apply(this,arguments)}),e.init.prototype=e,e.$super=this,e},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},a=s.WordArray=c.extend({init:function(t,r){t=this.words=t||[],this.sigBytes=r!=e?r:4*t.length},toString:function(t){return(t||l).stringify(this)},concat:function(t){var e=this.words,r=t.words,i=this.sigBytes,n=t.sigBytes;if(this.clamp(),i%4)for(var o=0;o>>2]>>>24-o%4*8&255;e[i+o>>>2]|=s<<24-(i+o)%4*8}else for(o=0;o>>2]=r[o>>>2];return this.sigBytes+=n,this},clamp:function(){var e=this.words,r=this.sigBytes;e[r>>>2]&=4294967295<<32-r%4*8,e.length=t.ceil(r/4)},clone:function(){var t=c.clone.call(this);return t.words=this.words.slice(0),t},random:function(t){for(var e=[],r=0;r>>2]>>>24-n%4*8&255;i.push((o>>>4).toString(16)),i.push((15&o).toString(16))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>3]|=parseInt(t.substr(i,2),16)<<24-i%8*4;return new a.init(r,e/2)}},f=h.Latin1={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n>>2]>>>24-n%4*8&255;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i>>2]|=(255&t.charCodeAt(i))<<24-i%4*8;return new a.init(r,e)}},u=h.Utf8={stringify:function(t){try{return decodeURIComponent(escape(f.stringify(t)))}catch(t){throw new Error("Malformed UTF-8 data")}},parse:function(t){return f.parse(unescape(encodeURIComponent(t)))}},d=s.BufferedBlockAlgorithm=c.extend({reset:function(){this._data=new a.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=u.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(e){var r,i=this._data,n=i.words,o=i.sigBytes,s=this.blockSize,c=4*s,h=o/c;h=e?t.ceil(h):t.max((0|h)-this._minBufferSize,0);var l=h*s,f=t.min(4*l,o);if(l){for(var u=0;u>>6-s%4*2,h=c|a;n[o>>>2]|=h<<24-o%4*8,o++}return i.create(n,o)}var e=l,r=e.lib,i=r.WordArray,n=e.enc;n.Base64={stringify:function(t){var e=t.words,r=t.sigBytes,i=this._map;t.clamp();for(var n=[],o=0;o>>2]>>>24-o%4*8&255,c=e[o+1>>>2]>>>24-(o+1)%4*8&255,a=e[o+2>>>2]>>>24-(o+2)%4*8&255,h=s<<16|c<<8|a,l=0;l<4&&o+.75*l>>6*(3-l)&63));var f=i.charAt(64);if(f)for(;n.length%4;)n.push(f);return n.join("")},parse:function(e){var r=e.length,i=this._map,n=this._reverseMap;if(!n){n=this._reverseMap=[];for(var o=0;o>>32-o)+e}function r(t,e,r,i,n,o,s){var c=t+(e&i|r&~i)+n+s;return(c<>>32-o)+e}function i(t,e,r,i,n,o,s){var c=t+(e^r^i)+n+s;return(c<>>32-o)+e}function n(t,e,r,i,n,o,s){var c=t+(r^(e|~i))+n+s;return(c<>>32-o)+e}var o=l,s=o.lib,c=s.WordArray,a=s.Hasher,h=o.algo,f=[];(function(){for(var e=0;e<64;e++)f[e]=4294967296*t.abs(t.sin(e+1))|0})();var u=h.MD5=a.extend({_doReset:function(){this._hash=new c.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(t,o){for(var s=0;s<16;s++){var c=o+s,a=t[c];t[c]=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8)}var h=this._hash.words,l=t[o+0],u=t[o+1],d=t[o+2],p=t[o+3],_=t[o+4],v=t[o+5],y=t[o+6],g=t[o+7],B=t[o+8],w=t[o+9],k=t[o+10],S=t[o+11],m=t[o+12],x=t[o+13],b=t[o+14],H=t[o+15],z=h[0],A=h[1],C=h[2],D=h[3];z=e(z,A,C,D,l,7,f[0]),D=e(D,z,A,C,u,12,f[1]),C=e(C,D,z,A,d,17,f[2]),A=e(A,C,D,z,p,22,f[3]),z=e(z,A,C,D,_,7,f[4]),D=e(D,z,A,C,v,12,f[5]),C=e(C,D,z,A,y,17,f[6]),A=e(A,C,D,z,g,22,f[7]),z=e(z,A,C,D,B,7,f[8]),D=e(D,z,A,C,w,12,f[9]),C=e(C,D,z,A,k,17,f[10]),A=e(A,C,D,z,S,22,f[11]),z=e(z,A,C,D,m,7,f[12]),D=e(D,z,A,C,x,12,f[13]),C=e(C,D,z,A,b,17,f[14]),A=e(A,C,D,z,H,22,f[15]),z=r(z,A,C,D,u,5,f[16]),D=r(D,z,A,C,y,9,f[17]),C=r(C,D,z,A,S,14,f[18]),A=r(A,C,D,z,l,20,f[19]),z=r(z,A,C,D,v,5,f[20]),D=r(D,z,A,C,k,9,f[21]),C=r(C,D,z,A,H,14,f[22]),A=r(A,C,D,z,_,20,f[23]),z=r(z,A,C,D,w,5,f[24]),D=r(D,z,A,C,b,9,f[25]),C=r(C,D,z,A,p,14,f[26]),A=r(A,C,D,z,B,20,f[27]),z=r(z,A,C,D,x,5,f[28]),D=r(D,z,A,C,d,9,f[29]),C=r(C,D,z,A,g,14,f[30]),A=r(A,C,D,z,m,20,f[31]),z=i(z,A,C,D,v,4,f[32]),D=i(D,z,A,C,B,11,f[33]),C=i(C,D,z,A,S,16,f[34]),A=i(A,C,D,z,b,23,f[35]),z=i(z,A,C,D,u,4,f[36]),D=i(D,z,A,C,_,11,f[37]),C=i(C,D,z,A,g,16,f[38]),A=i(A,C,D,z,k,23,f[39]),z=i(z,A,C,D,x,4,f[40]),D=i(D,z,A,C,l,11,f[41]),C=i(C,D,z,A,p,16,f[42]),A=i(A,C,D,z,y,23,f[43]),z=i(z,A,C,D,w,4,f[44]),D=i(D,z,A,C,m,11,f[45]),C=i(C,D,z,A,H,16,f[46]),A=i(A,C,D,z,d,23,f[47]),z=n(z,A,C,D,l,6,f[48]),D=n(D,z,A,C,g,10,f[49]),C=n(C,D,z,A,b,15,f[50]),A=n(A,C,D,z,v,21,f[51]),z=n(z,A,C,D,m,6,f[52]),D=n(D,z,A,C,p,10,f[53]),C=n(C,D,z,A,k,15,f[54]),A=n(A,C,D,z,u,21,f[55]),z=n(z,A,C,D,B,6,f[56]),D=n(D,z,A,C,H,10,f[57]),C=n(C,D,z,A,y,15,f[58]),A=n(A,C,D,z,x,21,f[59]),z=n(z,A,C,D,_,6,f[60]),D=n(D,z,A,C,S,10,f[61]),C=n(C,D,z,A,d,15,f[62]),A=n(A,C,D,z,w,21,f[63]),h[0]=h[0]+z|0,h[1]=h[1]+A|0,h[2]=h[2]+C|0,h[3]=h[3]+D|0},_doFinalize:function(){var e=this._data,r=e.words,i=8*this._nDataBytes,n=8*e.sigBytes;r[n>>>5]|=128<<24-n%32;var o=t.floor(i/4294967296),s=i;r[15+(n+64>>>9<<4)]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),r[14+(n+64>>>9<<4)]=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),e.sigBytes=4*(r.length+1),this._process();for(var c=this._hash,a=c.words,h=0;h<4;h++){var l=a[h];a[h]=16711935&(l<<8|l>>>24)|4278255360&(l<<24|l>>>8)}return c},clone:function(){var t=a.clone.call(this);return t._hash=this._hash.clone(),t}});o.MD5=a._createHelper(u),o.HmacMD5=a._createHmacHelper(u)}(Math),t=l,e=t.lib,r=e.WordArray,i=e.Hasher,n=t.algo,o=[],s=n.SHA1=i.extend({_doReset:function(){this._hash=new r.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],s=r[2],c=r[3],a=r[4],h=0;h<80;h++){if(h<16)o[h]=0|t[e+h];else{var l=o[h-3]^o[h-8]^o[h-14]^o[h-16];o[h]=l<<1|l>>>31}var f=(i<<5|i>>>27)+a+o[h];f+=h<20?1518500249+(n&s|~n&c):h<40?1859775393+(n^s^c):h<60?(n&s|n&c|s&c)-1894007588:(n^s^c)-899497514,a=c,c=s,s=n<<30|n>>>2,n=i,i=f}r[0]=r[0]+i|0,r[1]=r[1]+n|0,r[2]=r[2]+s|0,r[3]=r[3]+c|0,r[4]=r[4]+a|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(i+64>>>9<<4)]=Math.floor(r/4294967296),e[15+(i+64>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}}),t.SHA1=i._createHelper(s),t.HmacSHA1=i._createHmacHelper(s),function(t){var e=l,r=e.lib,i=r.WordArray,n=r.Hasher,o=e.algo,s=[],c=[];(function(){function e(e){for(var r=t.sqrt(e),i=2;i<=r;i++)if(!(e%i))return!1;return!0}function r(t){return 4294967296*(t-(0|t))|0}for(var i=2,n=0;n<64;)e(i)&&(n<8&&(s[n]=r(t.pow(i,.5))),c[n]=r(t.pow(i,1/3)),n++),i++})();var a=[],h=o.SHA256=n.extend({_doReset:function(){this._hash=new i.init(s.slice(0))},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],h=r[4],l=r[5],f=r[6],u=r[7],d=0;d<64;d++){if(d<16)a[d]=0|t[e+d];else{var p=a[d-15],_=(p<<25|p>>>7)^(p<<14|p>>>18)^p>>>3,v=a[d-2],y=(v<<15|v>>>17)^(v<<13|v>>>19)^v>>>10;a[d]=_+a[d-7]+y+a[d-16]}var g=h&l^~h&f,B=i&n^i&o^n&o,w=(i<<30|i>>>2)^(i<<19|i>>>13)^(i<<10|i>>>22),k=(h<<26|h>>>6)^(h<<21|h>>>11)^(h<<7|h>>>25),S=u+k+g+c[d]+a[d],m=w+B;u=f,f=l,l=h,h=s+S|0,s=o,o=n,n=i,i=S+m|0}r[0]=r[0]+i|0,r[1]=r[1]+n|0,r[2]=r[2]+o|0,r[3]=r[3]+s|0,r[4]=r[4]+h|0,r[5]=r[5]+l|0,r[6]=r[6]+f|0,r[7]=r[7]+u|0},_doFinalize:function(){var e=this._data,r=e.words,i=8*this._nDataBytes,n=8*e.sigBytes;return r[n>>>5]|=128<<24-n%32,r[14+(n+64>>>9<<4)]=t.floor(i/4294967296),r[15+(n+64>>>9<<4)]=i,e.sigBytes=4*r.length,this._process(),this._hash},clone:function(){var t=n.clone.call(this);return t._hash=this._hash.clone(),t}});e.SHA256=n._createHelper(h),e.HmacSHA256=n._createHmacHelper(h)}(Math),function(){function t(t){return t<<8&4278255360|t>>>8&16711935}var e=l,r=e.lib,i=r.WordArray,n=e.enc;n.Utf16=n.Utf16BE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n>>2]>>>16-n%4*8&65535;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],n=0;n>>1]|=t.charCodeAt(n)<<16-n%2*16;return i.create(r,2*e)}};n.Utf16LE={stringify:function(e){for(var r=e.words,i=e.sigBytes,n=[],o=0;o>>2]>>>16-o%4*8&65535);n.push(String.fromCharCode(s))}return n.join("")},parse:function(e){for(var r=e.length,n=[],o=0;o>>1]|=t(e.charCodeAt(o)<<16-o%2*16);return i.create(n,2*r)}}}(),function(){if("function"==typeof ArrayBuffer){var t=l,e=t.lib,r=e.WordArray,i=r.init,n=r.init=function(t){if(t instanceof ArrayBuffer&&(t=new Uint8Array(t)),(t instanceof Int8Array||"undefined"!=typeof Uint8ClampedArray&&t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array)&&(t=new Uint8Array(t.buffer,t.byteOffset,t.byteLength)),t instanceof Uint8Array){for(var e=t.byteLength,r=[],n=0;n>>2]|=t[n]<<24-n%4*8;i.call(this,r,e)}else i.apply(this,arguments)};n.prototype=r}}(),function(t){function e(t,e,r){return t^e^r}function r(t,e,r){return t&e|~t&r}function i(t,e,r){return(t|~e)^r}function n(t,e,r){return t&r|e&~r}function o(t,e,r){return t^(e|~r)}function s(t,e){return t<>>32-e}var c=l,a=c.lib,h=a.WordArray,f=a.Hasher,u=c.algo,d=h.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),p=h.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),_=h.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),v=h.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),y=h.create([0,1518500249,1859775393,2400959708,2840853838]),g=h.create([1352829926,1548603684,1836072691,2053994217,0]),B=u.RIPEMD160=f.extend({_doReset:function(){this._hash=h.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,c){for(var a=0;a<16;a++){var h=c+a,l=t[h];t[h]=16711935&(l<<8|l>>>24)|4278255360&(l<<24|l>>>8)}var f,u,B,w,k,S,m,x,b,H,z,A=this._hash.words,C=y.words,D=g.words,E=d.words,R=p.words,M=_.words,F=v.words;S=f=A[0],m=u=A[1],x=B=A[2],b=w=A[3],H=k=A[4];for(a=0;a<80;a+=1)z=f+t[c+E[a]]|0,z+=a<16?e(u,B,w)+C[0]:a<32?r(u,B,w)+C[1]:a<48?i(u,B,w)+C[2]:a<64?n(u,B,w)+C[3]:o(u,B,w)+C[4],z|=0,z=s(z,M[a]),z=z+k|0,f=k,k=w,w=s(B,10),B=u,u=z,z=S+t[c+R[a]]|0,z+=a<16?o(m,x,b)+D[0]:a<32?n(m,x,b)+D[1]:a<48?i(m,x,b)+D[2]:a<64?r(m,x,b)+D[3]:e(m,x,b)+D[4],z|=0,z=s(z,F[a]),z=z+H|0,S=H,H=b,b=s(x,10),x=m,m=z;z=A[1]+B+b|0,A[1]=A[2]+w+H|0,A[2]=A[3]+k+S|0,A[3]=A[4]+f+m|0,A[4]=A[0]+u+x|0,A[0]=z},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;e[i>>>5]|=128<<24-i%32,e[14+(i+64>>>9<<4)]=16711935&(r<<8|r>>>24)|4278255360&(r<<24|r>>>8),t.sigBytes=4*(e.length+1),this._process();for(var n=this._hash,o=n.words,s=0;s<5;s++){var c=o[s];o[s]=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8)}return n},clone:function(){var t=f.clone.call(this);return t._hash=this._hash.clone(),t}});c.RIPEMD160=f._createHelper(B),c.HmacRIPEMD160=f._createHmacHelper(B)}(Math),function(){var t=l,e=t.lib,r=e.Base,i=t.enc,n=i.Utf8,o=t.algo;o.HMAC=r.extend({init:function(t,e){t=this._hasher=new t.init,"string"==typeof e&&(e=n.parse(e));var r=t.blockSize,i=4*r;e.sigBytes>i&&(e=t.finalize(e)),e.clamp();for(var o=this._oKey=e.clone(),s=this._iKey=e.clone(),c=o.words,a=s.words,h=0;h>>24)|4278255360&(o<<24|o>>>8),s=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8);var c=r[n];c.high^=s,c.low^=o}for(var l=0;l<24;l++){for(var d=0;d<5;d++){for(var p=0,_=0,v=0;v<5;v++){c=r[d+5*v];p^=c.high,_^=c.low}var y=u[d];y.high=p,y.low=_}for(d=0;d<5;d++){var g=u[(d+4)%5],B=u[(d+1)%5],w=B.high,k=B.low;for(p=g.high^(w<<1|k>>>31),_=g.low^(k<<1|w>>>31),v=0;v<5;v++){c=r[d+5*v];c.high^=p,c.low^=_}}for(var S=1;S<25;S++){c=r[S];var m=c.high,x=c.low,b=a[S];b<32?(p=m<>>32-b,_=x<>>32-b):(p=x<>>64-b,_=m<>>64-b);var H=u[h[S]];H.high=p,H.low=_}var z=u[0],A=r[0];z.high=A.high,z.low=A.low;for(d=0;d<5;d++)for(v=0;v<5;v++){S=d+5*v,c=r[S];var C=u[S],D=u[(d+1)%5+5*v],E=u[(d+2)%5+5*v];c.high=C.high^~D.high&E.high,c.low=C.low^~D.low&E.low}c=r[0];var R=f[l];c.high^=R.high,c.low^=R.low}},_doFinalize:function(){var e=this._data,r=e.words,n=(this._nDataBytes,8*e.sigBytes),o=32*this.blockSize;r[n>>>5]|=1<<24-n%32,r[(t.ceil((n+1)/o)*o>>>5)-1]|=128,e.sigBytes=4*r.length,this._process();for(var s=this._state,c=this.cfg.outputLength/8,a=c/8,h=[],l=0;l>>24)|4278255360&(u<<24|u>>>8),d=16711935&(d<<8|d>>>24)|4278255360&(d<<24|d>>>8),h.push(d),h.push(u)}return new i.init(h,c)},clone:function(){for(var t=n.clone.call(this),e=t._state=this._state.slice(0),r=0;r<25;r++)e[r]=e[r].clone();return t}});e.SHA3=n._createHelper(d),e.HmacSHA3=n._createHmacHelper(d)}(Math),function(){function t(){return o.create.apply(o,arguments)}var e=l,r=e.lib,i=r.Hasher,n=e.x64,o=n.Word,s=n.WordArray,c=e.algo,a=[t(1116352408,3609767458),t(1899447441,602891725),t(3049323471,3964484399),t(3921009573,2173295548),t(961987163,4081628472),t(1508970993,3053834265),t(2453635748,2937671579),t(2870763221,3664609560),t(3624381080,2734883394),t(310598401,1164996542),t(607225278,1323610764),t(1426881987,3590304994),t(1925078388,4068182383),t(2162078206,991336113),t(2614888103,633803317),t(3248222580,3479774868),t(3835390401,2666613458),t(4022224774,944711139),t(264347078,2341262773),t(604807628,2007800933),t(770255983,1495990901),t(1249150122,1856431235),t(1555081692,3175218132),t(1996064986,2198950837),t(2554220882,3999719339),t(2821834349,766784016),t(2952996808,2566594879),t(3210313671,3203337956),t(3336571891,1034457026),t(3584528711,2466948901),t(113926993,3758326383),t(338241895,168717936),t(666307205,1188179964),t(773529912,1546045734),t(1294757372,1522805485),t(1396182291,2643833823),t(1695183700,2343527390),t(1986661051,1014477480),t(2177026350,1206759142),t(2456956037,344077627),t(2730485921,1290863460),t(2820302411,3158454273),t(3259730800,3505952657),t(3345764771,106217008),t(3516065817,3606008344),t(3600352804,1432725776),t(4094571909,1467031594),t(275423344,851169720),t(430227734,3100823752),t(506948616,1363258195),t(659060556,3750685593),t(883997877,3785050280),t(958139571,3318307427),t(1322822218,3812723403),t(1537002063,2003034995),t(1747873779,3602036899),t(1955562222,1575990012),t(2024104815,1125592928),t(2227730452,2716904306),t(2361852424,442776044),t(2428436474,593698344),t(2756734187,3733110249),t(3204031479,2999351573),t(3329325298,3815920427),t(3391569614,3928383900),t(3515267271,566280711),t(3940187606,3454069534),t(4118630271,4000239992),t(116418474,1914138554),t(174292421,2731055270),t(289380356,3203993006),t(460393269,320620315),t(685471733,587496836),t(852142971,1086792851),t(1017036298,365543100),t(1126000580,2618297676),t(1288033470,3409855158),t(1501505948,4234509866),t(1607167915,987167468),t(1816402316,1246189591)],h=[];(function(){for(var e=0;e<80;e++)h[e]=t()})();var f=c.SHA512=i.extend({_doReset:function(){this._hash=new s.init([new o.init(1779033703,4089235720),new o.init(3144134277,2227873595),new o.init(1013904242,4271175723),new o.init(2773480762,1595750129),new o.init(1359893119,2917565137),new o.init(2600822924,725511199),new o.init(528734635,4215389547),new o.init(1541459225,327033209)])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],l=r[5],f=r[6],u=r[7],d=i.high,p=i.low,_=n.high,v=n.low,y=o.high,g=o.low,B=s.high,w=s.low,k=c.high,S=c.low,m=l.high,x=l.low,b=f.high,H=f.low,z=u.high,A=u.low,C=d,D=p,E=_,R=v,M=y,F=g,P=B,W=w,O=k,I=S,U=m,K=x,X=b,L=H,j=z,N=A,T=0;T<80;T++){var q,Z,V=h[T];if(T<16)Z=V.high=0|t[e+2*T],q=V.low=0|t[e+2*T+1];else{var G=h[T-15],J=G.high,$=G.low,Q=(J>>>1|$<<31)^(J>>>8|$<<24)^J>>>7,Y=($>>>1|J<<31)^($>>>8|J<<24)^($>>>7|J<<25),tt=h[T-2],et=tt.high,rt=tt.low,it=(et>>>19|rt<<13)^(et<<3|rt>>>29)^et>>>6,nt=(rt>>>19|et<<13)^(rt<<3|et>>>29)^(rt>>>6|et<<26),ot=h[T-7],st=ot.high,ct=ot.low,at=h[T-16],ht=at.high,lt=at.low;q=Y+ct,Z=Q+st+(q>>>0>>0?1:0),q+=nt,Z=Z+it+(q>>>0>>0?1:0),q+=lt,Z=Z+ht+(q>>>0>>0?1:0),V.high=Z,V.low=q}var ft=O&U^~O&X,ut=I&K^~I&L,dt=C&E^C&M^E&M,pt=D&R^D&F^R&F,_t=(C>>>28|D<<4)^(C<<30|D>>>2)^(C<<25|D>>>7),vt=(D>>>28|C<<4)^(D<<30|C>>>2)^(D<<25|C>>>7),yt=(O>>>14|I<<18)^(O>>>18|I<<14)^(O<<23|I>>>9),gt=(I>>>14|O<<18)^(I>>>18|O<<14)^(I<<23|O>>>9),Bt=a[T],wt=Bt.high,kt=Bt.low,St=N+gt,mt=j+yt+(St>>>0>>0?1:0),xt=(St=St+ut,mt=mt+ft+(St>>>0>>0?1:0),St=St+kt,mt=mt+wt+(St>>>0>>0?1:0),St=St+q,mt=mt+Z+(St>>>0>>0?1:0),vt+pt),bt=_t+dt+(xt>>>0>>0?1:0);j=X,N=L,X=U,L=K,U=O,K=I,I=W+St|0,O=P+mt+(I>>>0>>0?1:0)|0,P=M,W=F,M=E,F=R,E=C,R=D,D=St+xt|0,C=mt+bt+(D>>>0>>0?1:0)|0}p=i.low=p+D,i.high=d+C+(p>>>0>>0?1:0),v=n.low=v+R,n.high=_+E+(v>>>0>>0?1:0),g=o.low=g+F,o.high=y+M+(g>>>0>>0?1:0),w=s.low=w+W,s.high=B+P+(w>>>0>>0?1:0),S=c.low=S+I,c.high=k+O+(S>>>0>>0?1:0),x=l.low=x+K,l.high=m+U+(x>>>0>>0?1:0),H=f.low=H+L,f.high=b+X+(H>>>0>>0?1:0),A=u.low=A+N,u.high=z+j+(A>>>0>>0?1:0)},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;e[i>>>5]|=128<<24-i%32,e[30+(i+128>>>10<<5)]=Math.floor(r/4294967296),e[31+(i+128>>>10<<5)]=r,t.sigBytes=4*e.length,this._process();var n=this._hash.toX32();return n},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t},blockSize:32});e.SHA512=i._createHelper(f),e.HmacSHA512=i._createHmacHelper(f)}(),function(){var t=l,e=t.x64,r=e.Word,i=e.WordArray,n=t.algo,o=n.SHA512,s=n.SHA384=o.extend({_doReset:function(){this._hash=new i.init([new r.init(3418070365,3238371032),new r.init(1654270250,914150663),new r.init(2438529370,812702999),new r.init(355462360,4144912697),new r.init(1731405415,4290775857),new r.init(2394180231,1750603025),new r.init(3675008525,1694076839),new r.init(1203062813,3204075428)])},_doFinalize:function(){var t=o._doFinalize.call(this);return t.sigBytes-=16,t}});t.SHA384=o._createHelper(s),t.HmacSHA384=o._createHmacHelper(s)}(),l.lib.Cipher||function(t){var e=l,r=e.lib,i=r.Base,n=r.WordArray,o=r.BufferedBlockAlgorithm,s=e.enc,c=(s.Utf8,s.Base64),a=e.algo,h=a.EvpKDF,f=r.Cipher=o.extend({cfg:i.extend(),createEncryptor:function(t,e){return this.create(this._ENC_XFORM_MODE,t,e)},createDecryptor:function(t,e){return this.create(this._DEC_XFORM_MODE,t,e)},init:function(t,e,r){this.cfg=this.cfg.extend(r),this._xformMode=t,this._key=e,this.reset()},reset:function(){o.reset.call(this),this._doReset()},process:function(t){return this._append(t),this._process()},finalize:function(t){t&&this._append(t);var e=this._doFinalize();return e},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(){function t(t){return"string"==typeof t?m:w}return function(e){return{encrypt:function(r,i,n){return t(i).encrypt(e,r,i,n)},decrypt:function(r,i,n){return t(i).decrypt(e,r,i,n)}}}}()}),u=(r.StreamCipher=f.extend({_doFinalize:function(){var t=this._process(!0);return t},blockSize:1}),e.mode={}),d=r.BlockCipherMode=i.extend({createEncryptor:function(t,e){return this.Encryptor.create(t,e)},createDecryptor:function(t,e){return this.Decryptor.create(t,e)},init:function(t,e){this._cipher=t,this._iv=e}}),p=u.CBC=function(){function e(e,r,i){var n,o=this._iv;o?(n=o,this._iv=t):n=this._prevBlock;for(var s=0;s>>2];t.sigBytes-=e}},y=(r.BlockCipher=f.extend({cfg:f.cfg.extend({mode:p,padding:v}),reset:function(){var t;f.reset.call(this);var e=this.cfg,r=e.iv,i=e.mode;this._xformMode==this._ENC_XFORM_MODE?t=i.createEncryptor:(t=i.createDecryptor,this._minBufferSize=1),this._mode&&this._mode.__creator==t?this._mode.init(this,r&&r.words):(this._mode=t.call(i,this,r&&r.words),this._mode.__creator=t)},_doProcessBlock:function(t,e){this._mode.processBlock(t,e)},_doFinalize:function(){var t,e=this.cfg.padding;return this._xformMode==this._ENC_XFORM_MODE?(e.pad(this._data,this.blockSize),t=this._process(!0)):(t=this._process(!0),e.unpad(t)),t},blockSize:4}),r.CipherParams=i.extend({init:function(t){this.mixIn(t)},toString:function(t){return(t||this.formatter).stringify(this)}})),g=e.format={},B=g.OpenSSL={stringify:function(t){var e,r=t.ciphertext,i=t.salt;return e=i?n.create([1398893684,1701076831]).concat(i).concat(r):r,e.toString(c)},parse:function(t){var e,r=c.parse(t),i=r.words;return 1398893684==i[0]&&1701076831==i[1]&&(e=n.create(i.slice(2,4)),i.splice(0,4),r.sigBytes-=16),y.create({ciphertext:r,salt:e})}},w=r.SerializableCipher=i.extend({cfg:i.extend({format:B}),encrypt:function(t,e,r,i){i=this.cfg.extend(i);var n=t.createEncryptor(r,i),o=n.finalize(e),s=n.cfg;return y.create({ciphertext:o,key:r,iv:s.iv,algorithm:t,mode:s.mode,padding:s.padding,blockSize:t.blockSize,formatter:i.format})},decrypt:function(t,e,r,i){i=this.cfg.extend(i),e=this._parse(e,i.format);var n=t.createDecryptor(r,i).finalize(e.ciphertext);return n},_parse:function(t,e){return"string"==typeof t?e.parse(t,this):t}}),k=e.kdf={},S=k.OpenSSL={execute:function(t,e,r,i){i||(i=n.random(8));var o=h.create({keySize:e+r}).compute(t,i),s=n.create(o.words.slice(e),4*r);return o.sigBytes=4*e,y.create({key:o,iv:s,salt:i})}},m=r.PasswordBasedCipher=w.extend({cfg:w.cfg.extend({kdf:S}),encrypt:function(t,e,r,i){i=this.cfg.extend(i);var n=i.kdf.execute(r,t.keySize,t.ivSize);i.iv=n.iv;var o=w.encrypt.call(this,t,e,n.key,i);return o.mixIn(n),o},decrypt:function(t,e,r,i){i=this.cfg.extend(i),e=this._parse(e,i.format);var n=i.kdf.execute(r,t.keySize,t.ivSize,e.salt);i.iv=n.iv;var o=w.decrypt.call(this,t,e,n.key,i);return o}})}(),l.mode.CFB=function(){function t(t,e,r,i){var n,o=this._iv;o?(n=o.slice(0),this._iv=void 0):n=this._prevBlock,i.encryptBlock(n,0);for(var s=0;s>>2]|=n<<24-o%4*8,t.sigBytes+=n},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},l.pad.Iso10126={pad:function(t,e){var r=4*e,i=r-t.sigBytes%r;t.concat(l.lib.WordArray.random(i-1)).concat(l.lib.WordArray.create([i<<24],1))},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},l.pad.Iso97971={pad:function(t,e){t.concat(l.lib.WordArray.create([2147483648],1)),l.pad.ZeroPadding.pad(t,e)},unpad:function(t){l.pad.ZeroPadding.unpad(t),t.sigBytes--}},l.mode.OFB=(a=l.lib.BlockCipherMode.extend(),h=a.Encryptor=a.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=this._iv,o=this._keystream;n&&(o=this._keystream=n.slice(0),this._iv=void 0),r.encryptBlock(o,0);for(var s=0;s>>8^255&l^99,n[r]=l,o[l]=r;var _=t[r],v=t[_],y=t[v],g=257*t[l]^16843008*l;s[r]=g<<24|g>>>8,c[r]=g<<16|g>>>16,a[r]=g<<8|g>>>24,h[r]=g;g=16843009*y^65537*v^257*_^16843008*r;f[l]=g<<24|g>>>8,u[l]=g<<16|g>>>16,d[l]=g<<8|g>>>24,p[l]=g,r?(r=_^t[t[t[y^_]]],i^=t[t[i]]):r=i=1}})();var _=[0,1,2,4,8,16,32,64,128,27,54],v=i.AES=r.extend({_doReset:function(){if(!this._nRounds||this._keyPriorReset!==this._key){for(var t=this._keyPriorReset=this._key,e=t.words,r=t.sigBytes/4,i=this._nRounds=r+6,o=4*(i+1),s=this._keySchedule=[],c=0;c6&&c%r==4&&(l=n[l>>>24]<<24|n[l>>>16&255]<<16|n[l>>>8&255]<<8|n[255&l]):(l=l<<8|l>>>24,l=n[l>>>24]<<24|n[l>>>16&255]<<16|n[l>>>8&255]<<8|n[255&l],l^=_[c/r|0]<<24),s[c]=s[c-r]^l);for(var a=this._invKeySchedule=[],h=0;h>>24]]^u[n[l>>>16&255]]^d[n[l>>>8&255]]^p[n[255&l]]}}},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._keySchedule,s,c,a,h,n)},decryptBlock:function(t,e){var r=t[e+1];t[e+1]=t[e+3],t[e+3]=r,this._doCryptBlock(t,e,this._invKeySchedule,f,u,d,p,o);r=t[e+1];t[e+1]=t[e+3],t[e+3]=r},_doCryptBlock:function(t,e,r,i,n,o,s,c){for(var a=this._nRounds,h=t[e]^r[0],l=t[e+1]^r[1],f=t[e+2]^r[2],u=t[e+3]^r[3],d=4,p=1;p>>24]^n[l>>>16&255]^o[f>>>8&255]^s[255&u]^r[d++],v=i[l>>>24]^n[f>>>16&255]^o[u>>>8&255]^s[255&h]^r[d++],y=i[f>>>24]^n[u>>>16&255]^o[h>>>8&255]^s[255&l]^r[d++],g=i[u>>>24]^n[h>>>16&255]^o[l>>>8&255]^s[255&f]^r[d++];h=_,l=v,f=y,u=g}_=(c[h>>>24]<<24|c[l>>>16&255]<<16|c[f>>>8&255]<<8|c[255&u])^r[d++],v=(c[l>>>24]<<24|c[f>>>16&255]<<16|c[u>>>8&255]<<8|c[255&h])^r[d++],y=(c[f>>>24]<<24|c[u>>>16&255]<<16|c[h>>>8&255]<<8|c[255&l])^r[d++],g=(c[u>>>24]<<24|c[h>>>16&255]<<16|c[l>>>8&255]<<8|c[255&f])^r[d++];t[e]=_,t[e+1]=v,t[e+2]=y,t[e+3]=g},keySize:8});t.AES=r._createHelper(v)}(),function(){function t(t,e){var r=(this._lBlock>>>t^this._rBlock)&e;this._rBlock^=r,this._lBlock^=r<>>t^this._lBlock)&e;this._lBlock^=r,this._rBlock^=r<>>5]>>>31-n%32&1}for(var o=this._subKeys=[],s=0;s<16;s++){var l=o[s]=[],f=h[s];for(i=0;i<24;i++)l[i/6|0]|=r[(a[i]-1+f)%28]<<31-i%6,l[4+(i/6|0)]|=r[28+(a[i+24]-1+f)%28]<<31-i%6;l[0]=l[0]<<1|l[0]>>>31;for(i=1;i<7;i++)l[i]=l[i]>>>4*(i-1)+3;l[7]=l[7]<<5|l[7]>>>27}var u=this._invSubKeys=[];for(i=0;i<16;i++)u[i]=o[15-i]},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._subKeys)},decryptBlock:function(t,e){this._doCryptBlock(t,e,this._invSubKeys)},_doCryptBlock:function(r,i,n){this._lBlock=r[i],this._rBlock=r[i+1],t.call(this,4,252645135),t.call(this,16,65535),e.call(this,2,858993459),e.call(this,8,16711935),t.call(this,1,1431655765);for(var o=0;o<16;o++){for(var s=n[o],c=this._lBlock,a=this._rBlock,h=0,l=0;l<8;l++)h|=f[l][((a^s[l])&u[l])>>>0];this._lBlock=a,this._rBlock=c^h}var d=this._lBlock;this._lBlock=this._rBlock,this._rBlock=d,t.call(this,1,1431655765),e.call(this,8,16711935),e.call(this,2,858993459),t.call(this,16,65535),t.call(this,4,252645135),r[i]=this._lBlock,r[i+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});r.DES=o._createHelper(d);var p=s.TripleDES=o.extend({_doReset:function(){var t=this._key,e=t.words;if(2!==e.length&&4!==e.length&&e.length<6)throw new Error("Invalid key length - 3DES requires the key length to be 64, 128, 192 or >192.");var r=e.slice(0,2),i=e.length<4?e.slice(0,2):e.slice(2,4),o=e.length<6?e.slice(0,2):e.slice(4,6);this._des1=d.createEncryptor(n.create(r)),this._des2=d.createEncryptor(n.create(i)),this._des3=d.createEncryptor(n.create(o))},encryptBlock:function(t,e){this._des1.encryptBlock(t,e),this._des2.decryptBlock(t,e),this._des3.encryptBlock(t,e)},decryptBlock:function(t,e){this._des3.decryptBlock(t,e),this._des2.encryptBlock(t,e),this._des1.decryptBlock(t,e)},keySize:6,ivSize:2,blockSize:2});r.TripleDES=o._createHelper(p)}(),function(){function t(){for(var t=this._S,e=this._i,r=this._j,i=0,n=0;n<4;n++){e=(e+1)%256,r=(r+t[e])%256;var o=t[e];t[e]=t[r],t[r]=o,i|=t[(t[e]+t[r])%256]<<24-8*n}return this._i=e,this._j=r,i}var e=l,r=e.lib,i=r.StreamCipher,n=e.algo,o=n.RC4=i.extend({_doReset:function(){for(var t=this._key,e=t.words,r=t.sigBytes,i=this._S=[],n=0;n<256;n++)i[n]=n;n=0;for(var o=0;n<256;n++){var s=n%r,c=e[s>>>2]>>>24-s%4*8&255;o=(o+i[n]+c)%256;var a=i[n];i[n]=i[o],i[o]=a}this._i=this._j=0},_doProcessBlock:function(e,r){e[r]^=t.call(this)},keySize:8,ivSize:0});e.RC4=i._createHelper(o);var s=n.RC4Drop=o.extend({cfg:o.cfg.extend({drop:192}),_doReset:function(){o._doReset.call(this);for(var e=this.cfg.drop;e>0;e--)t.call(this)}});e.RC4Drop=i._createHelper(s)}(),l.mode.CTRGladman=function(){function t(t){if(255==(t>>24&255)){var e=t>>16&255,r=t>>8&255,i=255&t;255===e?(e=0,255===r?(r=0,255===i?i=0:++i):++r):++e,t=0,t+=e<<16,t+=r<<8,t+=i}else t+=1<<24;return t}function e(e){return 0===(e[0]=t(e[0]))&&(e[1]=t(e[1])),e}var r=l.lib.BlockCipherMode.extend(),i=r.Encryptor=r.extend({processBlock:function(t,r){var i=this._cipher,n=i.blockSize,o=this._iv,s=this._counter;o&&(s=this._counter=o.slice(0),this._iv=void 0),e(s);var c=s.slice(0);i.encryptBlock(c,0);for(var a=0;a>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],n=65535&i,o=i>>>16,a=((n*n>>>17)+n*o>>>15)+o*o,h=((4294901760&i)*i|0)+((65535&i)*i|0);c[r]=a^h}t[0]=c[0]+(c[7]<<16|c[7]>>>16)+(c[6]<<16|c[6]>>>16)|0,t[1]=c[1]+(c[0]<<8|c[0]>>>24)+c[7]|0,t[2]=c[2]+(c[1]<<16|c[1]>>>16)+(c[0]<<16|c[0]>>>16)|0,t[3]=c[3]+(c[2]<<8|c[2]>>>24)+c[1]|0,t[4]=c[4]+(c[3]<<16|c[3]>>>16)+(c[2]<<16|c[2]>>>16)|0,t[5]=c[5]+(c[4]<<8|c[4]>>>24)+c[3]|0,t[6]=c[6]+(c[5]<<16|c[5]>>>16)+(c[4]<<16|c[4]>>>16)|0,t[7]=c[7]+(c[6]<<8|c[6]>>>24)+c[5]|0}var e=l,r=e.lib,i=r.StreamCipher,n=e.algo,o=[],s=[],c=[],a=n.Rabbit=i.extend({_doReset:function(){for(var e=this._key.words,r=this.cfg.iv,i=0;i<4;i++)e[i]=16711935&(e[i]<<8|e[i]>>>24)|4278255360&(e[i]<<24|e[i]>>>8);var n=this._X=[e[0],e[3]<<16|e[2]>>>16,e[1],e[0]<<16|e[3]>>>16,e[2],e[1]<<16|e[0]>>>16,e[3],e[2]<<16|e[1]>>>16],o=this._C=[e[2]<<16|e[2]>>>16,4294901760&e[0]|65535&e[1],e[3]<<16|e[3]>>>16,4294901760&e[1]|65535&e[2],e[0]<<16|e[0]>>>16,4294901760&e[2]|65535&e[3],e[1]<<16|e[1]>>>16,4294901760&e[3]|65535&e[0]];this._b=0;for(i=0;i<4;i++)t.call(this);for(i=0;i<8;i++)o[i]^=n[i+4&7];if(r){var s=r.words,c=s[0],a=s[1],h=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),l=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8),f=h>>>16|4294901760&l,u=l<<16|65535&h;o[0]^=h,o[1]^=f,o[2]^=l,o[3]^=u,o[4]^=h,o[5]^=f,o[6]^=l,o[7]^=u;for(i=0;i<4;i++)t.call(this)}},_doProcessBlock:function(e,r){var i=this._X;t.call(this),o[0]=i[0]^i[5]>>>16^i[3]<<16,o[1]=i[2]^i[7]>>>16^i[5]<<16,o[2]=i[4]^i[1]>>>16^i[7]<<16,o[3]=i[6]^i[3]>>>16^i[1]<<16;for(var n=0;n<4;n++)o[n]=16711935&(o[n]<<8|o[n]>>>24)|4278255360&(o[n]<<24|o[n]>>>8),e[r+n]^=o[n]},blockSize:4,ivSize:2});e.Rabbit=i._createHelper(a)}(),l.mode.CTR=function(){var t=l.lib.BlockCipherMode.extend(),e=t.Encryptor=t.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=this._iv,o=this._counter;n&&(o=this._counter=n.slice(0),this._iv=void 0);var s=o.slice(0);r.encryptBlock(s,0),o[i-1]=o[i-1]+1|0;for(var c=0;c>>0>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0>>0?1:0)|0,this._b=e[7]>>>0>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],n=65535&i,o=i>>>16,a=((n*n>>>17)+n*o>>>15)+o*o,h=((4294901760&i)*i|0)+((65535&i)*i|0);c[r]=a^h}t[0]=c[0]+(c[7]<<16|c[7]>>>16)+(c[6]<<16|c[6]>>>16)|0,t[1]=c[1]+(c[0]<<8|c[0]>>>24)+c[7]|0,t[2]=c[2]+(c[1]<<16|c[1]>>>16)+(c[0]<<16|c[0]>>>16)|0,t[3]=c[3]+(c[2]<<8|c[2]>>>24)+c[1]|0,t[4]=c[4]+(c[3]<<16|c[3]>>>16)+(c[2]<<16|c[2]>>>16)|0,t[5]=c[5]+(c[4]<<8|c[4]>>>24)+c[3]|0,t[6]=c[6]+(c[5]<<16|c[5]>>>16)+(c[4]<<16|c[4]>>>16)|0,t[7]=c[7]+(c[6]<<8|c[6]>>>24)+c[5]|0}var e=l,r=e.lib,i=r.StreamCipher,n=e.algo,o=[],s=[],c=[],a=n.RabbitLegacy=i.extend({_doReset:function(){var e=this._key.words,r=this.cfg.iv,i=this._X=[e[0],e[3]<<16|e[2]>>>16,e[1],e[0]<<16|e[3]>>>16,e[2],e[1]<<16|e[0]>>>16,e[3],e[2]<<16|e[1]>>>16],n=this._C=[e[2]<<16|e[2]>>>16,4294901760&e[0]|65535&e[1],e[3]<<16|e[3]>>>16,4294901760&e[1]|65535&e[2],e[0]<<16|e[0]>>>16,4294901760&e[2]|65535&e[3],e[1]<<16|e[1]>>>16,4294901760&e[3]|65535&e[0]];this._b=0;for(var o=0;o<4;o++)t.call(this);for(o=0;o<8;o++)n[o]^=i[o+4&7];if(r){var s=r.words,c=s[0],a=s[1],h=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),l=16711935&(a<<8|a>>>24)|4278255360&(a<<24|a>>>8),f=h>>>16|4294901760&l,u=l<<16|65535&h;n[0]^=h,n[1]^=f,n[2]^=l,n[3]^=u,n[4]^=h,n[5]^=f,n[6]^=l,n[7]^=u;for(o=0;o<4;o++)t.call(this)}},_doProcessBlock:function(e,r){var i=this._X;t.call(this),o[0]=i[0]^i[5]>>>16^i[3]<<16,o[1]=i[2]^i[7]>>>16^i[5]<<16,o[2]=i[4]^i[1]>>>16^i[7]<<16,o[3]=i[6]^i[3]>>>16^i[1]<<16;for(var n=0;n<4;n++)o[n]=16711935&(o[n]<<8|o[n]>>>24)|4278255360&(o[n]<<24|o[n]>>>8),e[r+n]^=o[n]},blockSize:4,ivSize:2});e.RabbitLegacy=i._createHelper(a)}(),l.pad.ZeroPadding={pad:function(t,e){var r=4*e;t.clamp(),t.sigBytes+=r-(t.sigBytes%r||r)},unpad:function(t){var e=t.words,r=t.sigBytes-1;for(r=t.sigBytes-1;r>=0;r--)if(e[r>>>2]>>>24-r%4*8&255){t.sigBytes=r+1;break}}},l}); \ No newline at end of file diff --git a/Scripts/webo.js b/Scripts/webo.js new file mode 100644 index 0000000..f079bb9 --- /dev/null +++ b/Scripts/webo.js @@ -0,0 +1,183 @@ +// +// iOS 桌面组件脚本 @「小件件」 +// 开发说明:请从 Widget 类开始编写,注释请勿修改 +// https://x.im3x.cn +// + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 + +// @组件代码开始 +let w = new ListWidget(); + +if (typeof require === 'undefined') require = importModule; // +const { DmYY, Runing } = require('./DmYY'); + +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.name = '微博热搜'; + this.en = 'weiboresou'; + if (config.runsInApp) { + this.registerAction('基础设置', this.setWidgetConfig); + this.registerAction('插件设置', this.actionSetting); + } + } + + actionUrl(name = '', data = '') { + let u = URLScheme.forRunningScript(); + let q = `act=${encodeURIComponent(name)}&data=${encodeURIComponent( + data, + )}&__arg=${encodeURIComponent(this.arg)}&__size=${this.widgetFamily}`; + let result = ''; + if (u.includes('run?')) { + result = `${u}&${q}`; + } else { + result = `${u}?${q}`; + } + return result; + } + + /** + * 渲染小尺寸组件 + */ + async renderSmall() { + let res = await this.$request.get( + 'https://m.weibo.cn/api/container/getIndex?containerid=106003%26filter_type%3Drealtimehot', + ); + let data = res['data']['cards'][0]['card_group']; + // 去除第一条 + data.shift(); + let topic = data[0]; + console.log(topic); + // 显示数据 + + w = await this.renderHeader( + w, + 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2225458401,2104443747&fm=26&gp=0.jpg', + '微博热搜', + ); + let body = w.addStack(); + let txt = body.addText(topic['desc']); + body.addSpacer(); + txt.leftAlignText(); + txt.font = Font.lightSystemFont(14); + txt.textColor = this.widgetColor; + w.addSpacer(); + let footer = w.addStack(); + footer.centerAlignContent(); + let img = footer.addImage(await this.$request.get(topic['pic'], 'IMG')); + img.imageSize = new Size(18, 18); + footer.addSpacer(5); + if (topic['icon']) { + let hot = footer.addImage(await this.$request.get(topic['icon'], 'IMG')); + hot.imageSize = new Size(18, 18); + footer.addSpacer(5); + } + let num = footer.addText(String(topic['desc_extr'])); + num.font = Font.lightSystemFont(10); + num.textOpacity = 0.5; + num.textColor = this.widgetColor; + body.url = topic['scheme']; + return w; + } + /** + * 渲染中尺寸组件 + */ + + async renderMedium(count = 4) { + let res = await this.$request.get( + 'https://m.weibo.cn/api/container/getIndex?containerid=106003%26filter_type%3Drealtimehot', + ); + let data = res['data']['cards'][0]['card_group']; + // 去除第一条 + data.shift(); + // 显示数据 + + w = await this.renderHeader( + w, + 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2225458401,2104443747&fm=26&gp=0.jpg', + '微博热搜', + ); + + // 布局:一行一个,左边顺序排序,中间标题,后边热/新 + const body = w.addStack(); + const bodyLeft = body.addStack(); + bodyLeft.layoutVertically(); + for (let i = 0; i < count; i++) { + let topic = data[i]; + let dom = bodyLeft.addStack(); + dom.centerAlignContent(); + let pic = dom.addImage(await this.$request.get(topic['pic'], 'IMG')); + pic.imageSize = new Size(18, 18); + dom.addSpacer(5); + let title = dom.addText(topic['desc']); + title.lineLimit = 1; + title.font = Font.lightSystemFont(14); + title.textColor = this.widgetColor; + dom.addSpacer(5); + if (topic['icon']) { + let iconDom = dom.addStack(); + let icon = iconDom.addImage( + await this.$request.get(topic['icon'], 'IMG'), + ); + icon.imageSize = new Size(18, 18); + } + dom.addSpacer(); + let extr = dom.addText(String(topic['desc_extr'])); + extr.font = Font.lightSystemFont(12); + extr.textColor = this.widgetColor; + extr.textOpacity = 0.6; + dom.url = topic['scheme']; + bodyLeft.addSpacer(5); + } + body.addSpacer(); + w.url = this.actionUrl('setting'); + return w; + } + /** + * 渲染大尺寸组件 + */ + async renderLarge() { + return await this.renderMedium(11); + } + + async actionSetting() { + const settings = this.getSettings(); + const arg = settings['type'] || '1'; + let a = new Alert(); + a.title = '打开方式'; + a.message = '点击小组件浏览热点的方式'; + a.addAction((arg === '0' ? '✅ ' : '') + '微博客户端'); + a.addAction((arg === '1' ? '✅ ' : '') + 'Vvebo'); + a.addCancelAction('取消设置'); + let i = await a.presentSheet(); + if (i === -1) return; + this.settings['type'] = String(i); + this.saveSettings(); + } + + async actionOpenUrl(url) { + const settings = this.getSettings(); + if (settings['type'] === '1') { + Safari.openInApp(url, false); + } else { + let k = decodeURIComponent(url).split('q=')[1].split('&')[0]; + Safari.open('vvebo://search?q=' + encodeURIComponent(k)); + } + } + + useBoxJS = false; + + async render() { + await this.getWidgetBackgroundImage(w); + if (this.widgetFamily === 'medium') { + return this.renderMedium(); + } else if (this.widgetFamily === 'large') { + return this.renderLarge(); + } else { + return this.renderSmall(); + } + } +} + +await Runing(Widget); diff --git a/Scripts/wsgw.js b/Scripts/wsgw.js new file mode 100644 index 0000000..a8c1007 --- /dev/null +++ b/Scripts/wsgw.js @@ -0,0 +1,381 @@ +// Variables used by Scriptable. +// These must be at the very top of the file. Do not edit. +// icon-color: deep-gray; icon-glyph: setting; + +// 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 +if (typeof require === 'undefined') require = importModule; +const { DmYY, Runing } = require('./DmYY'); + +/** + * 重写修改自作者 + * @channel https://t.me/yqc_123/ + * @feedback https://t.me/NobyDa_Chat + * @author 小白脸|𝐎𝐍𝐙𝟑𝐕 + * + * 添加重写:https://raw.githubusercontent.com/dompling/Script/master/wsgw/index.js + * + */ + +const defaultData = { + user: '**', + left: { + dayElePq: [], + balance: 0, + arrearsOfFees: false, + }, + right: { + previousBill: 0, + previousBillRate: 0, + thisYear: 0, + thisYearRate: 0, + }, + update: '', +}; + +// @组件代码开始 +class Widget extends DmYY { + constructor(arg) { + super(arg); + this.en = 'wsgw'; + this.name = '网上国网'; + this.userNum = args.widgetParameter || 0; + if (config.runsInApp) { + this.registerAction({ + icon: { name: 'photo.tv', color: '#5A74EF' }, + type: 'color', + title: '左侧背景', + desc: '左侧背景色', + val: 'leftColor', + }); + this.registerAction({ + icon: { name: 'arrow.clockwise', color: '#1890ff' }, + type: 'input', + title: '缓存时间', + desc: '默认3小时 (单位小时),填写方式数字', + placeholder: '3', + val: 'cacheTime', + }); + + this.registerAction({ + icon: { name: 'character.cursor.ibeam', color: '#EC6240' }, + type: 'input', + title: '文字缩放', + desc: '文字缩放比例,值为 0-1', + placeholder: '1', + val: 'scale', + }); + + this.registerAction('基础设置', this.setWidgetConfig); + } + } + + date = new Date(); + day = this.date.getTime(); + + dataSource = { ...defaultData }; + + init = async () => { + console.log(`当前用户下标:${this.userNum}`); + this.cacheTime = (this.settings.cacheTime || 3) * 3600000; + this.scale = parseFloat(this.settings.scale || '1'); + if ( + !this.settings.data || + this.settings.cacheDay + this.cacheTime < this.day + ) { + console.log(`缓存失效,重新获取`); + await this.cacheData(); + } else { + console.log( + `最后更新时间:${new Date(parseInt(this.settings.cacheDay)).toLocaleString()}` + ); + console.log( + `缓存失效时间:${new Date(parseInt(this.settings.cacheDay) + this.cacheTime).toLocaleString()}` + ); + this.dataSource = { ...this.settings.data[this.userNum] }; + if (!this.dataSource.user) await this.cacheData(); + console.log(this.dataSource); + } + }; + + cacheData = async () => { + try { + const response = await this.$request.get( + 'https://api.wsgw-rewrite.com/electricity/bill/all', + { timeoutInterval: 60 } + ); + console.log(response); + this.settings.data = []; + response?.forEach((dataInfo) => { + const dataSource = { + user: '**', + left: { + dayElePq: [], + balance: 0, + arrearsOfFees: false, + }, + right: { + previousBill: 0, + previousBillRate: 0, + thisYear: 0, + thisYearRate: 0, + }, + update: '', + }; + + dataSource.user = dataInfo.userInfo.consName_dst.replaceAll('*', ''); + dataSource.left.balance = parseFloat(dataInfo.eleBill.sumMoney); + dataSource.left.dayElePq = dataInfo.dayElecQuantity.sevenEleList + .filter((item) => item.dayElePq !== '-') + .map((item) => ({ + label: item.day, + value: parseFloat(item.dayElePq), + })); + + dataSource.left.arrearsOfFees = dataInfo.arrearsOfFees; + + dataSource.right.previousBill = parseFloat( + this.last(dataInfo.monthElecQuantity?.mothEleList || []) + ?.monthEleCost || 0 + ); + + const oldVal = + this.last(dataInfo.monthElecQuantity?.mothEleList || [], 2) + ?.monthEleCost || 1; + + dataSource.right.previousBillRate = + ((dataSource.right.previousBill - oldVal) / oldVal) * 100; + + dataSource.right.previousBillRate = parseFloat( + dataSource.right.previousBillRate.toFixed(2) + ); + + dataSource.right.thisYear = parseFloat( + dataInfo.monthElecQuantity?.dataInfo?.totalEleCost || 0 + ); + + const lastYearVal = dataInfo.lastYearElecQuantity.dataInfo.totalEleCost; + + dataSource.right.thisYearRate = + ((dataSource.right.thisYear - lastYearVal) / lastYearVal) * 100; + + dataSource.right.thisYearRate = parseFloat( + dataSource.right.thisYearRate.toFixed(2) + ); + + dataSource.update = dataInfo.eleBill.date; + this.settings.data.push({ ...dataSource }); + }); + console.log(this.settings.data); + this.dataSource = { ...this.settings.data[this.userNum] }; + this.settings.cacheDay = this.day; + this.saveSettings(false); + } catch (e) { + console.log(`接口数据异常:请检查 BoxJS 重写`); + console.log(e); + } + }; + + last = (data = [], index = 1) => { + return data[data.length - index]; + }; + + renderImage = async (uri) => { + return this.$request.get(uri, 'IMG'); + }; + + notSupport(w) { + const stack = w.addStack(); + stack.addText('暂不支持'); + return w; + } + + barChart() { + return ` + { + "type": "bar", + "data": { + "labels": ${JSON.stringify(this.dataSource.left.dayElePq.map((item) => item.label).reverse())}, + "datasets": [ + { + "label": "Sales", + "data": ${JSON.stringify(this.dataSource.left.dayElePq.map((item) => parseFloat(item.value)).reverse())}, + "backgroundColor": "#fff", + "borderColor": "#fff", + "borderWidth": 1, + "borderRadius": { + "topLeft": 30, + "topRight": 30, + "bottomLeft": 30, // 只为柱状图底部设置圆角 + "bottomRight": 30 + }, + "barPercentage": 0.8, // 控制柱子的宽度 + "categoryPercentage": 0.4, + "borderSkipped": false // 应用自定义的圆角设置 + } + ] + }, + "options": { + "plugins": { + "legend": { + "display": false // 隐藏图例 + }, + "title": { + "display": false // 隐藏标题 + } + }, + "scales": { + "x": { + "display": false // 完全隐藏 X 轴 + }, + "y": { + "display": false // 完全隐藏 Y 轴 + } + }, + "layout": { + "padding": 0 // 移除图表周围的内边距 + } + } +} +`; + } + + createLeft = async (widget) => { + const fontStyle = { + color: new Color('#fff'), + size: 20 * this.scale, + opacity: 0.8, + }; + const leftStack = widget.addStack(); + leftStack.cornerRadius = 10; + leftStack.layoutVertically(); + leftStack.backgroundColor = new Color( + this.settings.leftColor || '#5A74EF', + 0.8 + ); + leftStack.setPadding(10, 10, 10, 10); + + const chartStack = leftStack.addStack(); + + const chartImage = await this.renderImage( + `https://quickchart.io/chart?v=4&w=800&h=400&f=png&c=${encodeURIComponent(this.barChart())}` + ); + const chartImageStack = chartStack.addImage(chartImage); + chartImageStack.imageSize = new Size(120, 60); + + leftStack.addSpacer(); + + this.provideText('余额', leftStack, fontStyle); + + const todayStack = leftStack.addStack(); + todayStack.centerAlignContent(); + if (this.dataSource.left.arrearsOfFees) + fontStyle.color = new Color('#f65755'); + + fontStyle.size = 20 * this.scale; + this.provideText('¥ ', todayStack, fontStyle); + + fontStyle.opacity = 1; + const todayUse = this.dataSource.left.balance; + + this.provideText(` ${todayUse.toLocaleString()}`, todayStack, fontStyle); + }; + + createDot = (stack, color) => { + const dotStack = stack.addStack(); + dotStack.setPadding(0, 0, 2, 0); + const dot = dotStack.addStack(); + + dot.size = new Size(10, 10); + dot.backgroundColor = new Color(color); + dot.cornerRadius = 10; + }; + + createCell = (widget, data = { title: '', num: 0, radio: 0 }) => { + const cellStack = widget.addStack(); + cellStack.backgroundColor = new Color('#404045'); + cellStack.setPadding(10, 10, 10, 10); + cellStack.cornerRadius = 10; + cellStack.layoutVertically(); + + const fontStyle = { + color: new Color('#fff'), + size: 14 * this.scale, + opacity: 0.6, + }; + this.provideText(data.title, cellStack, fontStyle); + + const dataStack = cellStack.addStack(); + dataStack.bottomAlignContent(); + + fontStyle.size = 12 * this.scale; + this.provideText('¥ ', dataStack, fontStyle); + + fontStyle.opacity = 1; + fontStyle.size = 20 * this.scale; + this.provideText(` ${data.num.toLocaleString()}`, dataStack, fontStyle); + dataStack.addSpacer(); + + const dotStack = dataStack.addStack(); + this.createDot(dotStack, data.radio > 0 ? '#7EEF8F' : '#ED86A5'); + + fontStyle.size = 12 * this.scale; + this.provideText( + data.radio > 0 ? ` +${data.radio}%` : ` -${Math.abs(data.radio)}%`, + dataStack, + fontStyle + ); + }; + + createRight = async (widget) => { + const rightStack = widget.addStack(); + rightStack.layoutVertically(); + this.createCell(rightStack, { + title: '上期费用', + num: this.dataSource.right.previousBill, + radio: this.dataSource.right.previousBillRate, + }); + rightStack.addSpacer(); + this.createCell(rightStack, { + title: '今年费用', + num: this.dataSource.right.thisYear, + radio: this.dataSource.right.thisYearRate, + }); + }; + + renderSmall = async (w) => { + w.setPadding(10, 10, 10, 10); + await this.createLeft(w); + return w; + }; + + renderMedium = async (w) => { + w.setPadding(10, 10, 10, 10); + const containerStack = w.addStack(); + containerStack.layoutHorizontally(); + await this.createLeft(containerStack); + containerStack.addSpacer(10); + await this.createRight(containerStack); + return w; + }; + + /** + * 渲染函数,函数名固定 + * 可以根据 this.widgetFamily 来判断小组件尺寸,以返回不同大小的内容 + */ + async render() { + await this.init(); + const widget = new ListWidget(); + widget.url = `com.wsgw.e.zsdl://platformapi/`; + await this.getWidgetBackgroundImage(widget); + if (this.widgetFamily === 'medium') { + return await this.renderMedium(widget); + } else if (this.widgetFamily === 'large') { + return await this.notSupport(widget); + } else { + return await this.renderSmall(widget); + } + } +} + +// @组件代码结束 +await Runing(Widget, '0', false); //远程开发环境 diff --git a/app.js b/app.js deleted file mode 100644 index f98514e..0000000 --- a/app.js +++ /dev/null @@ -1,139 +0,0 @@ -const fs = require("fs"); -const os = require("os"); -const path = require("path"); -const express = require("express"); -const child_process = require("child_process"); -const multer = require("multer"); -const bodyParser = require("body-parser"); - -const HTTP_PORT = 5566; -const WORK_DIR = path.dirname(__filename); -const SCRIPTS_DIR = path.join(WORK_DIR, "Scripts"); - -const app = express(); -const upload = multer({ - dest: os.tmpdir(), -}); -app.use(upload.any()); -app.use( - bodyParser.urlencoded({ - extended: false, - }) -); -app.use(bodyParser.json()); - -app.get("/ping", (req, res) => { - console.log("[-] ping.."); - setTimeout(() => { - res.send("pong").end(); - }, 1000); -}); - -let FILE_DATE = null; - -app.get("/sync", (req, res) => { - // console.log('[-] 等待同步到手机..') - const { name } = req.query; - - const WIDGET_FILE = path.join(SCRIPTS_DIR, name + ".js"); - if (!fs.existsSync(WIDGET_FILE)) return res.send("nofile").end(); - - setTimeout(() => { - // 判断文件时间 - const _time = fs.statSync(WIDGET_FILE).mtimeMs; - if (_time === FILE_DATE) { - res.send("no").end(); - return; - // return console.log("[!] 文件没有更改,不同步") - } - // 同步 - res.sendFile(WIDGET_FILE); - console.log("[+] 同步到手机完毕"); - FILE_DATE = _time; - }, 1000); -}); - -app.post("/sync", (req, res) => { - if (req.files.length !== 1) return res.send("no"); - console.log("[+] Scriptalbe App 已连接"); - const _file = req.files[0]; - const FILE_NAME = _file["originalname"] + ".js"; - const WIDGET_FILE = path.join(SCRIPTS_DIR, FILE_NAME); - fs.renameSync(_file["path"], WIDGET_FILE); - res.send("ok"); - console.log(`[*] 小组件源码(${_file["originalname"]})已同步,请打开编辑`); - FILE_DATE = fs.statSync(WIDGET_FILE).mtimeMs; - // 尝试打开 - let cmd = `code "${WIDGET_FILE}"`; - if (os.platform() === "win32") { - cmd = `cmd.exe /c ${cmd}`; - } else if (os.platform() === "linux") { - let shell = process.env["SHELL"]; - cmd = `${shell} -c ${cmd}`; - } else { - cmd = `"/Applications/Visual Studio Code.app/Contents/MacOS/Electron" "${WIDGET_FILE}"`; - } - child_process.execSync(cmd); -}); - -// 远程 console,调试中把调试输出内容传送到服务端控制台输出 -app.post("/console", (req, res) => { - const { t, data } = req.body; - const _time = new Date().toLocaleString().split(" ")[1]; - switch (t) { - case "warn": - console.warn( - `[console.warn / ${_time}]`, - typeof data === "string" ? data : "" - ); - if (typeof data === "object") console.warn(data); - break; - case "error": - console.error( - `[console.error / ${_time}]`, - typeof data === "string" ? data : "" - ); - if (typeof data === "object") console.error(data); - break; - default: - console.log( - `[console.log / ${_time}]`, - typeof data === "string" ? data : "" - ); - if (typeof data === "object") console.log(data); - } - res.send("ok"); -}); - -// 获取当前电脑IP -function getIPAdress() { - var interfaces = os.networkInterfaces(); - for (var devName in interfaces) { - var iface = interfaces[devName]; - for (var i = 0; i < iface.length; i++) { - var alias = iface[i]; - if ( - alias.family === "IPv4" && - alias.address !== "127.0.0.1" && - !alias.internal - ) { - return alias.address; - } - } - } -} - -const _ip = getIPAdress(); -const _host = `http://${_ip}:${HTTP_PORT}`; - -console.log("[*] 「小件件」开发服务运行中"); -console.log(`[-] 地址:${_host}`); -console.log( - `[-] 如果你的手机还没有配置开发环境,请手机 Safari 访问上述地址,查看引导` -); -console.log( - "[+] 如果你的手机已经安装好环境和小组件模板,请在 Scriptable 里点击小组件模板->远程开发,服务器地址输入:", - _ip -); -console.log("[*] 更多帮助:https://github.com/im3x/scriptables"); -app.listen(HTTP_PORT); diff --git a/birthdayCountDown/2Ya.jpg b/birthdayCountDown/2Ya.jpg new file mode 100644 index 0000000..19b31fc Binary files /dev/null and b/birthdayCountDown/2Ya.jpg differ diff --git a/birthdayCountDown/index.js b/birthdayCountDown/index.js index e80a4b3..de35e94 100644 --- a/birthdayCountDown/index.js +++ b/birthdayCountDown/index.js @@ -344,7 +344,7 @@ class YaYaBirthday extends Calendar { widget.setPadding(0, 10, 0, 10); await this.setHeader( widget, - "https://raw.githubusercontent.com/Orz-3/task/master/birthday.png", + "https://raw.githubusercontent.com/Orz-3/mini/master/Color/birthday.png", "破壳日🎂" ); let body = await this.getEnableLeft(widget); diff --git a/extra_install.json b/extra_install.json new file mode 100644 index 0000000..03231c7 --- /dev/null +++ b/extra_install.json @@ -0,0 +1,60 @@ +{ + "author": "YaYa推荐", + "scriptable": true, + "icon": "https://avatars3.githubusercontent.com/u/23498579?s=460&u=1e87605e4abc4e6ecf3edd8b1d730227f54db4d4&v=4", + "repo": "https://github.com/dompling/Scriptable", + "apps": [ + { + "version": "1.0.0", + "author": "KeiKinn", + "description": "透明贴纸,随机换磁贴图片", + "scriptURL": "https://raw.githubusercontent.com/KeiKinn/StickerOnScreen/main/src/StickerOnScreen.js", + "thumb": "https://img.icons8.com/clouds/344/apple-app-store.png", + "name": "StickerOnScreen", + "title": "透明贴纸", + "html": [ + "作者:@KeiKinn" + ], + "images": [ + "https://raw.githubusercontent.com/KeiKinn/StickerOnScreen/main/img/IMG_3804.JPEG" + ] + }, + { + "version": "1.0.0", + "author": "@Michael Lee", + "description": "根据脚本注释,自行填写 API, 和频道ID", + "scriptURL": "https://raw.githubusercontent.com/zc-nju-med/own_JS/master/YouTube%20Plus.js", + "thumb": "https://www.gstatic.com/youtube/img/branding/favicon/favicon_144x144.png", + "name": "YouTubePlus", + "title": "油管订阅", + "html": [ + "

作者:@Michael Lee

", + "

Michael Lee 大佬博客里一篇文章详细的描述了关于这个小组件开发和使用,有一定英语水平的大佬可以自行摸索,了解了解代码

", + "

博客:iOS Scriptable YouTube widget

", + "

谷歌的 API KEY 需要自行申请,地址:YouTube DataAPI

" + ] + }, + { + "version": "1.0.0", + "author": "@Unknown", + "description": "根据脚本注释自行使用", + "scriptURL": "https://raw.githubusercontent.com/zc-nju-med/own_JS/master/NBA.js", + "thumb": "https://img.icons8.com/nolan/344/basketball.png", + "name": "NBA", + "title": "NBA", + "html": ["

喜欢篮球的,可以看看

"] + }, + { + "version": "1.0.0", + "author": "@三行", + "description": "根据脚本注释自行使用", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/webo.js", + "thumb": "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2225458401,2104443747&fm=26&gp=0.jpg", + "name": "Webo", + "title": "微博热搜", + "html": [ + "

修改自三行大佬的脚本@三行

" + ] + } + ] +} diff --git a/historyToDay/README.md b/historyToDay/README.md deleted file mode 100644 index f59c3e1..0000000 --- a/historyToDay/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# 历史上的今天 - -![](https://raw.githubusercontent.com/dompling/Scriptable/master/historyToDay/historyToday.gif) - -> 项目地址:[https://github.com/dompling/Scriptable/tree/master/historyToday](https://github.com/dompling/Scriptable/tree/master/historyToday) - -## 特别感谢 - -1. [@GideonSenku](https://github.com/GideonSenku) 提供帮助 diff --git a/historyToDay/historyToday.gif b/historyToDay/historyToday.gif deleted file mode 100644 index 313b528..0000000 Binary files a/historyToDay/historyToday.gif and /dev/null differ diff --git a/historyToDay/index.js b/historyToDay/index.js deleted file mode 100644 index b47f68e..0000000 --- a/historyToDay/index.js +++ /dev/null @@ -1,298 +0,0 @@ -// Variables used by Scriptable. -// These must be at the very top of the file. Do not edit. -// icon-color: teal; icon-glyph: map-pin; - -/* - * Author: 2Ya - * Github: https://github.com/dompling - * 本脚本使用了@Gideon_Senku的Env - */ - -const $ = importModule("Env"); - -const blurBackground = true; // 开启背景虚化 true 值类型布尔或数字 ,默认 0.7 取值范围 0 至 1 -const imageBackground = true; // 设置配置背景图片 -const forceImageUpdate = false; // 设置为true将重置小部件的背景图像 -const cacheBackgroundName = "historyDay-image"; // 缓存背景图片名字 - -const textFormat = { - // Set the default font and color. - defaultText: { size: 14, color: "ffffff", font: "regular" }, // 默认字体颜色 - light: { size: 14, color: "D0D3D4", font: "light" }, // 夜间字体颜色 - title: { size: 16, color: "ff651a", font: "semibold" }, - hot: { size: 20, color: "ffffff", font: "semibold" }, - more: { size: 14, color: "ffffff", font: "regular" }, -}; - -// 设置widget 背景色 -const skinColor = { - defaultColor: { - color: [new Color("#a18cd1"), new Color("#fbc2eb")], - position: [0.0, 1.0], - }, - night: { - color: [new Color("#030079"), new Color("#000000")], - position: [0.0, 1.0], - }, -}; - -// 设置单行内容背景色 -const cellBgColor = { - color: [new Color("#aaa")], - position: [1.0], -}; - -const imgUri = "http://img.lssdjt.com"; -const date = new Date(); -let month = date.getMonth() + 1; -month = month >= 10 ? month : `0${month}`; -let day = date.getDate(); -class YaYaHistory { - constructor(widgetParameter) { - this.start = parseInt(widgetParameter) > 0 ? widgetParameter : 0; - this.size = this.start > 0 ? 9 : 3; - this.widgetSize = config.runsInWidget ? config.widgetFamily : "large"; - this.mode = Device.isUsingDarkAppearance(); - this.textFormat = this.mode ? textFormat.light : textFormat.defaultText; - this.dataSource = []; - if (blurBackground) { - if (typeof blurBackground === "number") { - this.backgroundOpacity = blurBackground; - } else { - this.backgroundOpacity = this.mode ? 0.7 : 0.4; - } - } - } - - init = async () => { - // const url = `https://api.nowtime.cc/v1/today_in_history`; - const url = `http://code.lssdjt.com/jsondata/history.${month}.${day}.js`; - const response = await $.get({ url }); - this.dataSource = response.d; - }; - - // 给图片加透明遮罩 - setShadowImage = async (img, opacity) => { - if (!opacity) return img; - let ctx = new DrawContext(); - // 获取图片的尺寸 - ctx.size = img.size; - - ctx.drawImageInRect( - img, - new Rect(0, 0, img.size["width"], img.size["height"]) - ); - ctx.setFillColor(new Color("#000000", opacity)); - ctx.fillRect(new Rect(0, 0, img.size["width"], img.size["height"])); - - let res = await ctx.getImage(); - return res; - }; - - setWidgetBackGround = async (widget) => { - if (imageBackground) { - const files = FileManager.local(); - const path = files.joinPath( - files.documentsDirectory(), - cacheBackgroundName - ); - const exists = files.fileExists(path); - if (exists && (config.runsInWidget || !forceImageUpdate)) { - const image = files.readImage(path); - widget.backgroundImage = await this.setShadowImage( - image, - this.backgroundOpacity - ); - } else if (!exists && config.runsInWidget) { - widget.backgroundColor = Color.gray(); - } else { - const img = await Photos.fromLibrary(); - widget.backgroundImage = await this.setShadowImage( - img, - this.backgroundOpacity - ); - files.writeImage(path, img); - } - } else { - let gradient = new LinearGradient(); - let gradientSettings = this.mode - ? skinColor.night - : skinColor.defaultColor; - gradient.colors = gradientSettings.color(); - gradient.locations = gradientSettings.position(); - widget.backgroundGradient = gradient; - } - return widget; - }; - - // Draw the vertical line in the tomorrow view. - drawVerticalLine(color, height) { - const width = 2; - - let draw = new DrawContext(); - draw.opaque = false; - draw.respectScreenScale = true; - draw.size = new Size(width, height); - - let barPath = new Path(); - // const barHeight = height; - barPath.addRoundedRect( - new Rect(0, 0, height, height), - width / 2, - width / 2 - ); - draw.addPath(barPath); - draw.setFillColor(color); - draw.fillPath(); - return draw.getImage(); - } - - setHeader = async (widget) => { - const headerBody = widget.addStack(); - headerBody.centerAlignContent(); - // 左边内容 - const headerLeft = headerBody.addStack(); - const icon = - "https://raw.githubusercontent.com/Orz-3/task/master/historyToday.png"; - const title = "历史上的今天"; - let _icon = headerLeft.addImage(await this.fetchImg(icon)); - _icon.imageSize = new Size(14, 16); - _icon.cornerRadius = 4; - headerLeft.addSpacer(5); - $.provideText(title, headerLeft, textFormat.title); - headerBody.addSpacer(170); - // 右边更多 - const headerRight = headerBody.addStack(); - headerRight.url = `https://m.8684.cn/today_d${month}${day}`; - headerRight.setPadding(1, 10, 1, 10); - headerRight.cornerRadius = 10; - headerRight.backgroundColor = new Color("#fff", 0.5); - - $.provideText("更多", headerRight, textFormat.more); - - widget.addSpacer(10); - return widget; - }; - - setCell = async ( - text, - cell, - prefixColor = "fff", - format = this.textFormat - ) => { - if (prefixColor) { - let tomorrowLine = cell.addImage( - this.drawVerticalLine(new Color(prefixColor, 0.8), 12) - ); - tomorrowLine.imageSize = new Size(3, 28); - } - cell.addSpacer(5); - $.provideText(text, cell, format); - cell.addSpacer(2); - }; - - randomHexColor() { - var hex = Math.floor(Math.random() * 16777216).toString(16); //生成ffffff以内16进制数 - while (hex.length < 6) { - //while循环判断hex位数,少于6位前面加0凑够6位 - hex = "0" + hex; - } - return hex; //返回‘#’开头16进制颜色 - } - - setWidget = async (widget, start = 0, number) => { - await this.setHeader(widget); - if (this.dataSource.length) { - const data = this.dataSource.splice(start, number); - data.forEach((item) => { - let dom = widget.addStack(); - dom.url = `https://www.lssdjt.com/d/${item.f}.htm`; - dom.centerAlignContent(); - dom.cornerRadius = 5; - let prefixColor = this.randomHexColor(); - this.setCell(`${item.t}`, dom, prefixColor); - widget.addSpacer(5); - }); - } - return widget; - }; - - fetchImg = async (url) => { - const response = new Request(url); - return await response.loadImage(); - }; - - renderErrorWidget = (widget) => { - widget.addText("暂不支持该尺寸组件"); - return widget; - }; - - renderSmall = async (widget) => { - return await this.setWidget(widget, this.start, 1); - }; - - renderMedium = async (widget) => { - await this.setWidget(widget, this.start, 3); - return widget; - }; - - renderLarge = async (widget) => { - if (this.start === 0) { - const topItem = - this.dataSource.find((item) => item.g === 1) || - this.dataSource.find((item) => item.j !== ""); - const hotBody = widget.addStack(); - hotBody.setPadding(10, 10, 10, 10); - hotBody.url = `https://www.lssdjt.com/d/${topItem.f}.htm`; - hotBody.centerAlignContent(); - hotBody.size = new Size(340, 200); - hotBody.borderWidth = 15; - hotBody.borderColor = new Color("#fff"); - hotBody.cornerRadius = 20; - const hotImg = await this.fetchImg(`${imgUri}/${topItem.j}`); - hotBody.backgroundImage = await this.setShadowImage( - hotImg, - this.backgroundOpacity - ); - this.setCell(`${topItem.t}`, hotBody, false, textFormat.hot); - hotBody.addSpacer(10); - widget.addSpacer(10); - } - await this.setWidget(widget, this.start, this.size); - return widget; - }; - - render = async () => { - const widget = new ListWidget(); - widget.setPadding(10, 10, 10, 10); - let w = await this.setWidgetBackGround(widget); - switch (this.widgetSize) { - case "small": { - w = await this.renderSmall(w); - w.presentSmall(); - break; - } - case "medium": { - w = await this.renderMedium(w); - w.presentMedium(); - break; - } - case "large": { - w = await this.renderLarge(w); - w.presentLarge(); - break; - } - default: { - w = await this.renderErrorWidget(w); - w.presentSmall(); - break; - } - } - Script.setWidget(w); - Script.complete(); - }; -} - -const _2YaHistory = new YaYaHistory(args.widgetParameter); -await _2YaHistory.init(); // 初始化数据 -await _2YaHistory.render(); // 加载widget diff --git a/images/count.png b/images/count.png new file mode 100644 index 0000000..919c7fb Binary files /dev/null and b/images/count.png differ diff --git a/images/ftms.png b/images/ftms.png new file mode 100644 index 0000000..59c4bfe Binary files /dev/null and b/images/ftms.png differ diff --git a/images/gas-night.png b/images/gas-night.png new file mode 100644 index 0000000..74f0508 Binary files /dev/null and b/images/gas-night.png differ diff --git a/images/jdk.jpg b/images/jdk.jpg new file mode 100644 index 0000000..e869613 Binary files /dev/null and b/images/jdk.jpg differ diff --git a/images/large.png b/images/large.png new file mode 100644 index 0000000..31eba02 Binary files /dev/null and b/images/large.png differ diff --git a/images/medium.png b/images/medium.png new file mode 100644 index 0000000..c336dca Binary files /dev/null and b/images/medium.png differ diff --git a/images/more.png b/images/more.png new file mode 100644 index 0000000..e1e36cb Binary files /dev/null and b/images/more.png differ diff --git a/images/small.png b/images/small.png new file mode 100644 index 0000000..b2fe824 Binary files /dev/null and b/images/small.png differ diff --git a/install.json b/install.json index c4cc432..a8b7d63 100644 --- a/install.json +++ b/install.json @@ -1,97 +1,542 @@ { - "author": "@DomplingYaYa", + "author": "DomplingYaYa", + "scriptable": true, "icon": "https://avatars3.githubusercontent.com/u/23498579?s=460&u=1e87605e4abc4e6ecf3edd8b1d730227f54db4d4&v=4", - "repo": "https://github.com/dompling/Script", + "repo": "https://github.com/dompling/Scriptable", "apps": [ { - "version": "1.0.0", - "description": "一键安装所有包", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/2YaInstall.js", - "thumb": "https://avatars0.githubusercontent.com/u/23498579?s=60&v=4", - "name": "2YaInstall", - "title": "一键安装所有包" - }, - { - "version": "1.0.0", + "version": "1.1.2", "description": "DmYY组件库", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js", - "thumb": "https://avatars0.githubusercontent.com/u/23498579?s=60&v=4", + "thumb": "https://img.icons8.com/clouds/344/settings.png", "name": "DmYY", - "title": "DmYY" + "title": "DmYY", + "html": [ + "

更新说明

", + "v1.1.2", + "
  • fix: 输入默认值问题
  • ", + "v1.1.1", + "
  • feat: DmYY 判断有无 actions
  • ", + "v1.1.0", + "
  • 修复 boxjs 读取问题
  • ", + "v1.0.8", + "
  • 兼容 ios15 系统,图标引起脚本一直转圈问题
  • ", + "v1.0.7", + "
  • 处理背景图在 12mini 下错位的情况
  • ", + "v1.0.6", + "
  • 优化菜单,增加菜单图标
  • ", + "v1.0.5", + "
  • 修改调整菜单更多图标
  • ", + "
  • 预览关闭菜单功能
  • ", + "
    ", + "

    历史版本

    ", + "
  • 可视化界面版本
  • " + ] }, { "version": "1.0.0", - "description": "依赖DmYY,根据代理软件获取到京东 CK ,或者自行输入。显示京东最近的未确认收货的订单和信息。", + "description": "根据代理软件获取到京东 CK ,或者自行输入。显示京东最近的未确认收货的订单和信息。组件使用,参考下方京东豆走势视频", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/JDWuLiu.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/jd.png", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png", "name": "JDWuLiu", "homepage": "https://github.com/dompling/Scriptable/blob/master/JDWuLiu/README.md", - "title": "京东物流" + "title": "京东物流", + "html": [ + "

    京东组件教程

    ", + "

    " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", - "description": "依赖DmYY,根据代理软件获取到京东 CK ,或者自行输入。显示京东最近的京豆收支。", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/JDDou.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/jd.png", + "description": "根据代理软件获取到京东 CK ,或者自行输入。显示京豆总量,和昨日收支情况。组件使用,参考下方京东豆走势视频", + "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/JDDou.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png", "name": "JDDou", "homepage": "https://github.com/dompling/Scriptable/blob/master/JDDou/README.md", - "title": "京东豆" + "title": "京东豆", + "html": [ + "

    京东组件教程

    ", + "

    " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", - "description": "依赖DmYY,根据代理软件获取到京东 CK ,或者自行输入。显示京东最近的京豆收支。", + "description": "根据代理软件获取到京东 CK ,或者自行输入。显示京东最近的京豆收支。组件使用,参考下方视频", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/JDDouK.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/jd.png", - "name": "JDDou", - "title": "京东豆走势" + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/jd.png", + "name": "JDDouK", + "title": "京东豆走势", + "html": [ + "

    京东组件教程

    ", + "

    " + ], + "images": [ + "https://raw.githubusercontent.com/dompling/Scriptable/master/images/jdk.jpg" + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { - "version": "1.0.0", - "description": "依赖DmYY", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/BiliBili.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/bilibili.png", + "version": "1.0.1", + "description": "哔哩哔哩今日的推送番剧情况", + "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/BiliBili.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png", "name": "BiliBili", - "title": "哔哩哔哩今日番剧" + "title": "今日番剧", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", - "description": "依赖DmYY", + "description": "哔哩哔哩关注中心的最近更新", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/BiliBiliWatch.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/bilibili.png", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png", "name": "BiliBiliWatch", - "title": "哔哩哔哩关注中心" + "title": "关注中心", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "哔哩哔哩Up主订阅控件的视频信息等", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/BiliBiliUp.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/bilibili.png", + "name": "BiliBiliUp", + "title": "哔哩UP主", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", - "description": "依赖DmYY,Canlendar", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Canlendar.js", - "thumb": "https://avatars0.githubusercontent.com/u/23498579?s=60&v=4", - "name": "Canlendar", - "title": "日历函数" + "description": "日历函数插件,组件的依赖包", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Calendar.js", + "thumb": "https://img.icons8.com/clouds/344/edit-calendar.png", + "name": "Calendar", + "title": "日历函数", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", - "description": "依赖DmYY,Canlendar", + "description": "生日的信息和倒计时", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Birthday.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/birthday.png", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/birthday.png", "name": "Birthday", - "title": "破壳日" + "title": "破壳日", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + }, + { + "name": "Calendar", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Calendar.js" + } + ] }, { - "version": "1.0.0", - "description": "依赖DmYY", - "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/HistoryDay.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/historyToday.png", - "name": "HistoryDay", - "title": "历史上的今天" + "version": "1.0.2", + "description": "显示历史上的今日带图片情况", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/HistoryToday.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/historyToday.png", + "name": "HistoryToday", + "title": "历史上的今天", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] }, { "version": "1.0.0", - "description": "依赖DmYY", + "description": "智行火车票的出行提醒,需要手动的去智行火车的待出行列表使用重写获取待出行数据", "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/ZXTrains.js", - "thumb": "https://raw.githubusercontent.com/Orz-3/task/master/zxhc.png", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/zxhc.png", "name": "ZXTrains", - "title": "智行火车票提醒" + "title": "车票提醒", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "根据快捷指令,可以获取到相关的健康数据,然后使用本组件可以查询最近的步行跑步的数据", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Health.js", + "thumb": "https://img.icons8.com/clouds/344/apple-health.png", + "name": "Health", + "title": "健康步数", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "在文本框里面输入电话簿中的联系人,可以在桌面显示联系人信息,可以进行电话拨号等", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Contact.js", + "thumb": "https://img.icons8.com/clouds/344/apple-contacts.png", + "name": "Contact", + "title": "桌面联系人", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "在文本框里面输入电报频道的信息,可以显示订阅的人数等信息", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Telegram.js", + "thumb": "https://img.icons8.com/clouds/344/telegram-app.png", + "name": "Telegram", + "title": "Telegram", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "在文本框里面输入YouTube 作者的信息,可以展示作者的粉丝数,视频信息等", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/YouTube.js", + "thumb": "https://img.icons8.com/clouds/2x/youtube.png", + "name": "YouTube", + "title": "YouTube", + "html": [ + "

    组件教程

    ", + "

    " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "显示中国电信的手机号的套餐信息,可以在 app 里面登录,登录成功之后可以展示信息,也可以使用代理缓存读取 boxjs 的账号信息", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/ChinaTelecom.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/10000.png", + "name": "ChinaTelecom", + "title": "中国电信", + "html": [ + "

    组件教程

    ", + "

    " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "显示移动的套餐信息,目前暂不支持登录,请自行使用代理软件抓取数据信息。推荐使用 boxjs 代理缓存", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/ChinaMobile.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/10086.png", + "name": "ChinaMobile", + "title": "中国移动", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + }, + { + "name": "crypto-js", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/crypto-js.min.js" + } + ] + }, + { + "version": "1.0.0", + "description": "显示联通的套餐信息,目前暂不支持登录,请自行使用代理软件抓取数据信息。推荐使用 boxjs 代理缓存", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/ChinaUnicom.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/10010.png", + "name": "ChinaUnicom", + "title": "中国联通", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "Vpn 折线图面板,带签到功能。支持的机场 url 地址:https://**/auth/login", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/VpnBoard.js", + "thumb": "https://img.icons8.com/clouds/344/paper-plane-message-1.png", + "name": "VPNBoard", + "title": "VPN面板", + "html": [ + "

    机场模板要求

    ", + "

    登陆地址如下:https://***/auth/login , 以/auth/login 结尾的地址则符合要求

    " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "Vpn 圆形进度条,带签到功能。 url 地址:https://**/auth/login", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/VPNBoardPress.js", + "thumb": "https://img.icons8.com/clouds/344/paper-plane-message-1.png", + "name": "VPNBoardPress", + "title": "VPN圆形", + "html": [ + "

    机场模板要求

    ", + "

    登陆地址如下:https://***/auth/login , 以/auth/login 结尾的地址则符合要求

    " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.1", + "description": "VPN 圆形进度条,vpn 的订阅地址返回的信息中需要有带各种流量的使用情况的订阅地址", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/VPNSubscription.js", + "thumb": "https://img.icons8.com/clouds/344/paper-plane-message-1.png", + "name": "VPNSubscription", + "title": "VPN订阅", + "html": [ + "

    机场模板要求

    ", + "

    PC:订阅链接

    ", + "
  • 打开订阅链接
  • ", + "
  • 电脑端使用 F12 打开调试控制台,找到NetWork
  • ", + "
  • 找到订阅地址的请求
  • ", + "
  • 如果 response里面含有subscription-userinfo的关键字则满足条件
  • ", + "
    ", + "

    Mobile:

    ", + "
  • 操作方式类似 PC 登陆地址的查看步骤
  • " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "VPN 圆形进度条,vpn 登陆地址必须是 https://****/api/v1/passport/auth/login", + "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/VPNV2Bord.js", + "thumb": "https://img.icons8.com/clouds/344/paper-plane-message-1.png", + "name": "VPNV2Board", + "title": "V2模板", + "html": [ + "

    VPN 小组件教学

    ", + "

    ", + "

    机场模板要求

    ", + "

    登陆地址如下:https://xn--mesr8b36x.net/#/login , 以/#/login 结尾的地址则符合要求

    ", + "

    PC登陆地址接口:

    ", + "
  • 输入账号密码(错误的账号密码即可)点击登陆
  • ", + "
  • 电脑端使用 F12 打开调试控制台,找到NetWork
  • ", + "
  • 找到 login 接口:https://*****/api/v1/passport/auth/login
  • ", + "
  • 在 scriptable中的登陆地址输入/api/v1/passport/auth/login前面的那段地址
  • ", + "
    ", + "

    Mobile登陆地址接口:

    ", + "
  • 请自行使用代理软件查看搜索即可,操作方式类似 PC 登陆地址的查看步骤
  • " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "毒汤日历,每天一杯毒鸡汤,扎心不怕百年享。", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/PoisonCalendar.js", + "thumb": "https://img.icons8.com/clouds/344/google-calendar.png", + "name": "PoisonCalendar", + "title": "毒汤日历", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + }, + { + "name": "Calendar", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Calendar.js" + } + ] + }, + { + "version": "1.0.0", + "description": "毒汤日历纯文字版,无需抓包信息", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/PoisonCalendarText.js", + "thumb": "https://img.icons8.com/clouds/344/google-calendar.png", + "name": "PoisonCalendarText", + "title": "毒汤纯文字", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.1", + "description": "获取当前省份油价", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/Oild.js", + "thumb": "https://img.icons8.com/clouds/344/engine-oil-level.png", + "name": "TodayOilPrice", + "title": "今日油价", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ], + "html": [ + "

    今日油价

    ", + "
      1.0.1
    ", + "
      增加自动定位设置
    ", + "
      增加省份选择
    " + ] + }, + { + "version": "1.0.0", + "description": "根据定位信息,显示本地区的新冠病毒的数据情况,可能有数据更新不及时的情况", + "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/COVID-19.js", + "thumb": "https://img.icons8.com/clouds/344/coronavirus.png", + "name": "COVID-19", + "title": "疫情日报", + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.1", + "description": "显示公历和农历,根据近期的信息会出现一个倒计时的提示信息", + "scriptURL": "https://raw.githubusercontent.com/dompling/scriptableTsx/master/scripts/CountDown.js", + "thumb": "https://raw.githubusercontent.com/Orz-3/mini/master/Color/daysmatter.png", + "name": "CountDownCalendar", + "title": "日历倒计时", + "html": [ + "

    更新说明

    ", + "v1.0.1", + "
  • 调整大尺寸排列效果
  • ", + "
  • 调整下班倒计时默认不开启
  • " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "交管 12123 违章和扣分查询", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/12123.js", + "thumb": "https://is5-ssl.mzstatic.com/image/thumb/Purple112/v4/a7/a1/61/a7a16170-e644-079e-a362-9eb273becf9d/AppIcon-1x_U007emarketing-0-4-0-0-85-220.png/492x0w.webp", + "name": "12123", + "title": "交管 12123", + "html": [ + "

    Token获取说明

    ", + "
  • 获取Token重写:Surge 12123重写模块
  • ", + "
  • 使用方法:配置重写规则,手动运行小组件,按提示跳转到 支付宝12123小程序 登录即可自动抓取/更新Token。
  • ", + "
  • 使用前,请确保您的代理APP已配置好BoxJs重写,BoxJs配置方法:BoxJS教程
  • " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.0", + "description": "币种涨幅", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/PriceWidgets.js", + "thumb": "https://assets.coingecko.com/coins/images/1/large/bitcoin.png", + "name": "PriceWidgets", + "title": "网络货币", + "html": [ + "

    迁移自JSBox脚本

    ", + "
  • 原项目:wuzeyou/PriceWidgets
  • ", + "
  • 特别感谢大佬:@Jackie Xiang
  • " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] + }, + { + "version": "1.0.2", + "description": "网上国网", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/wsgw.js", + "thumb": "https://is1-ssl.mzstatic.com/image/thumb/Purple116/v4/83/d8/8a/83d88a92-5a4d-7a2f-118c-80d795e7a9f6/AppIcon-0-0-1x_U007emarketing-0-5-0-0-sRGB-85-220.png/144x144.png", + "name": "wsgw", + "title": "网上国网", + "html": [ + "

    网上国网

    ", + "
  • 1.填写账号密码BoxJS订阅
  • ", + "
  • 2.参考借用【@小白脸】大佬的重写和【@Yuheng0101】大佬的脚本
  • ", + "
  • 3.Surge 添加远程重写模块【网上国网重写】、其他的请自行使用 https://script.hub 转换
  • " + ], + "depend": [ + { + "name": "DmYY", + "scriptURL": "https://raw.githubusercontent.com/dompling/Scriptable/master/Scripts/DmYY.js" + } + ] } ] } diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 9cdcb2a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,836 +0,0 @@ -{ - "name": "scriptable", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/scriptable-ios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@types/scriptable-ios/-/scriptable-ios-1.6.0.tgz", - "integrity": "sha512-zVZkVpDZYQTUE6nx/fHOl0u6Q06UCMNyIItOTaWZOJgEZlczEp3kkMOgKbjKKDuLYt07i0n523gb3Mjny+N4Gg==" - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", - "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" - } - }, - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", - "requires": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - } - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - } - } -} diff --git a/package.json b/package.json index 7ba4242..7d7494d 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,17 @@ { - "name": "scriptable", + "name": "scriptable-types", "version": "1.0.0", - "description": "> 将文件 [2YaInstall.scriptable](https://raw.githubusercontent.com/dompling/Scriptable/master/2YaInstall.scriptable) 导入到 scriptable 软件中,点击运行即可", - "repository": { - "type": "git", - "url": "git+https://github.com/dompling/Scriptable.git" - }, - "bugs": { - "url": "https://github.com/dompling/Scriptable/issues" - }, - "homepage": "https://github.com/dompling/Scriptable#readme", + "author": "2Ya", + "description": "", "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node app" - }, - "author": "", + "keywords": [ + "scriptable", + "ios", + "widget" + ], "license": "ISC", - "dependencies": { - "@types/scriptable-ios": "^1.5.0", - "body-parser": "^1.19.0", - "express": "^4.17.1", - "multer": "^1.4.2", - "request": "^2.88.2" + "devDependencies": { + "@types/scriptable-ios": "^1.6.1", + "prettier": "^3.3.3" } } diff --git a/widget.Install.js b/widget.Install.js index 6b12033..95ae3fb 100644 --- a/widget.Install.js +++ b/widget.Install.js @@ -2,20 +2,11 @@ // These must be at the very top of the file. Do not edit. // icon-color: brown; icon-glyph: download; -const mainAlert = new Alert(); +// version:'1.0.0'; + const Files = FileManager.iCloud(); const RootPath = Files.documentsDirectory(); -const saveFile = async ({ moduleName, url }) => { - const header = `// Variables used by Scriptable. - // These must be at the very top of the file. Do not edit. - // icon-color: deep-gray; icon-glyph: file-code;\n`; - const req = new Request(url); - const content = await req.loadString(); - const fileHeader = content.includes("icon-color") ? `` : header; - write(`${moduleName}`, `${fileHeader}${content}`); -}; - const saveFileName = (fileName) => { const hasSuffix = fileName.lastIndexOf(".") + 1; return !hasSuffix ? `${fileName}.js` : fileName; @@ -23,11 +14,18 @@ const saveFileName = (fileName) => { const write = (fileName, content) => { let file = saveFileName(fileName); - const filePath = `${RootPath}/${file}`; + const filePath = Files.joinPath(RootPath, file); Files.writeString(filePath, content); return true; }; +const saveFile = async ({ moduleName, url }) => { + const req = new Request(encodeURI(url)); + const content = await req.loadString(); + write(`${moduleName}`, content); + return true; +}; + const notify = async (title, body, url, opts = {}) => { let n = new Notification(); n = Object.assign(n, opts); @@ -59,8 +57,26 @@ const renderTableList = async (data) => { downloadCell.centerAligned(); downloadCell.dismissOnTap = true; downloadCell.onTap = async () => { - const res = await new Request(item.scriptURL).loadString(); - const isWrite = await write(item.name, res); + if (item.depend) { + try { + for (let i = 0; i < item.depend.length; i++) { + const relyItem = item.depend[i]; + const _isWrite = await saveFile({ + moduleName: relyItem.name, + url: relyItem.scriptURL, + }); + if (_isWrite) { + notify("下载提示", `依赖插件:${relyItem.name}下载/更新成功`); + } + } + } catch (e) { + console.log(e); + } + } + const isWrite = await saveFile({ + moduleName: item.name, + url: item.scriptURL, + }); if (isWrite) { notify("下载提示", `插件:${item.title}下载/更新成功`); } @@ -74,79 +90,87 @@ const renderTableList = async (data) => { notify("错误提示", "订阅获取失败"); } }; - -mainAlert.title = "组件下载"; -mainAlert.message = "可以自行添加订阅地址"; -try { - const cacheKey = "subscriptionList"; - const render = async () => { - let subscriptionList = []; - if (Keychain.contains(cacheKey)) { - subscriptionList = JSON.parse(Keychain.get(cacheKey)); - } - const _actions = []; - console.log(subscriptionList); - subscriptionList.forEach((item) => { - const { author } = item; - mainAlert.addAction("作者:" + author); - _actions.push(async () => { - await renderTableList(item); +const Run = async () => { + try { + const mainAlert = new Alert(); + mainAlert.title = "组件下载"; + mainAlert.message = "可以自行添加订阅地址"; + const cacheKey = "subscriptionList"; + const render = async () => { + let subscriptionList = []; + if (Keychain.contains(cacheKey)) { + subscriptionList = JSON.parse(Keychain.get(cacheKey)); + } + const _actions = []; + console.log(subscriptionList); + subscriptionList.forEach((item) => { + const { author } = item; + mainAlert.addAction("作者:" + author); + _actions.push(async () => { + await renderTableList(item); + }); }); - }); - _actions.push(async () => { - const a = new Alert(); - a.title = "订阅地址"; - a.addTextField( - "URL", - "https://raw.githubusercontent.com/dompling/Scriptable/master/install.json" - ); - a.addAction("确定"); - a.addCancelAction("取消"); - const id = await a.presentAlert(); - if (id === -1) return; - try { - const url = a.textFieldValue(0); - const response = await new Request(url).loadJSON(); - delete response.apps; - const data = []; - for (let i in subscriptionList) { - const item = subscriptionList[i]; - if (response.author === item.author) { - data.push({ ...response, subscription: url }); - } else { - data.push(item); + _actions.push(async () => { + const a = new Alert(); + a.title = "订阅地址"; + a.addTextField( + "URL", + "https://raw.githubusercontent.com/dompling/Scriptable/master/install.json" + ); + a.addAction("确定"); + a.addCancelAction("取消"); + const id = await a.presentAlert(); + if (id === -1) return; + try { + const url = a.textFieldValue(0); + const response = await new Request(url).loadJSON(); + delete response.apps; + const data = []; + let isPush = true; + for (let i in subscriptionList) { + const item = subscriptionList[i]; + if (response.author === item.author) { + isPush = false; + data.push({ ...response, subscription: url }); + } else { + data.push(item); + } } + if (isPush) data.push({ author: response.author, subscription: url }); + Keychain.set(cacheKey, JSON.stringify(data)); + notify("更新成功", "请重新运行本脚本"); + } catch (e) { + console.log(e); + notify("错误提示", "订阅地址错误,不是一个 JSON 格式"); } - if (!subscriptionList.length) - data.push({ author: response.author, subscription: url }); - Keychain.set(cacheKey, JSON.stringify(data)); - notify("更新成功", "请重新运行本脚本"); - } catch (e) { - console.log(e); - notify("错误提示", "订阅地址错误,不是一个 JSON 格式"); - } - }); - - mainAlert.addAction("添加订阅"); - mainAlert.addCancelAction("取消操作"); - const _actionsIndex = await mainAlert.presentSheet(); - if (_actions[_actionsIndex]) { - const func = _actions[_actionsIndex]; - await func(); - } - }; - await render(); -} catch (e) { - console.log("缓存读取错误" + e); -} + }); -const REMOTE_REQ = new Request( - "https://raw.githubusercontent.com/dompling/Scriptable/master/widget.Install.js" -); -const REMOTE_RES = await REMOTE_REQ.loadString(); -const result = await write("widget.Install", REMOTE_RES); -console.log(result); -if (result) { - console.log("🤖自我更新成功"); -} + mainAlert.addAction("添加订阅"); + mainAlert.addCancelAction("取消操作"); + const _actionsIndex = await mainAlert.presentSheet(); + if (_actions[_actionsIndex]) { + const func = _actions[_actionsIndex]; + await func(); + } + }; + await render(); + } catch (e) { + console.log("缓存读取错误" + e); + } +}; +(async () => { + try { + console.log("🤖自更新开始"); + const modules = { + moduleName: "widget.Install", + url: + "https://raw.githubusercontent.com/dompling/Scriptable/master/widget.Install.js", + }; + const result = await saveFile(modules); + if (result) console.log("🤖自更新成功"); + } catch (e) { + console.log(e); + } +})(); +await Run(); diff --git a/yarn.lock b/yarn.lock index 6c1e2e4..789a8f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,810 +2,12 @@ # yarn lockfile v1 -"@types/scriptable-ios@^1.5.0": - version "1.5.0" - resolved "https://registry.npm.taobao.org/@types/scriptable-ios/download/@types/scriptable-ios-1.5.0.tgz#bf0545452e8882a4634b279cfc29d7f7dd8cef8d" - integrity sha1-vwVFRS6IgqRjSyec/CnX992M740= - -accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha1-UxvHJlF6OytB+FACHGzBXqq1B80= - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -ajv@^6.12.3: - version "6.12.6" - resolved "https://registry.npm.taobao.org/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1604564396010&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fajv%2Fdownload%2Fajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha1-uvWmLoArB9l3A0WG+MO69a3ybfQ= - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -append-field@^1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/append-field/download/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" - integrity sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.npm.taobao.org/asn1/download/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha1-jSR136tVO7M+d7VOWeiAu4ziMTY= - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.11.0.tgz?cache=0&sync_timestamp=1604101210422&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -body-parser@1.19.0, body-parser@^1.19.0: - version "1.19.0" - resolved "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io= - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8= - -busboy@^0.2.11: - version "0.2.14" - resolved "https://registry.npm.taobao.org/busboy/download/busboy-0.2.14.tgz#6c2a622efcf47c57bbbe1e2a9c37ad36c7925453" - integrity sha1-bCpiLvz0fFe7vh4qnDetNseSVFM= - dependencies: - dicer "0.2.5" - readable-stream "1.1.x" - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.npm.taobao.org/bytes/download/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY= - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha1-w9RaizT9cwYxoRCoolIGgrMdWn8= - dependencies: - delayed-stream "~1.0.0" - -concat-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.npm.taobao.org/concat-stream/download/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ= - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70= - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha1-4TjMdeBAxyexlm/l5fjJruJW/js= - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.npm.taobao.org/cookie/download/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo= - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz?cache=0&sync_timestamp=1601073714105&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdashdash%2Fdownload%2Fdashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&sync_timestamp=1600502871403&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8= - dependencies: - ms "2.0.0" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -dicer@0.2.5: - version "0.2.5" - resolved "https://registry.npm.taobao.org/dicer/download/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f" - integrity sha1-WZbAhrszIYyBLAkL3cCc0S+stw8= - dependencies: - readable-stream "1.1.x" - streamsearch "0.1.2" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fexpress%2Fdownload%2Fexpress-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ= - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.npm.taobao.org/extend/download/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo= - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.npm.taobao.org/extsprintf/download/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU= - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.npm.taobao.org/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM= - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0= - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.npm.taobao.org/form-data/download/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha1-3M5SwF9kTymManq5Nr1yTO/786Y= - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.npm.taobao.org/har-validator/download/har-validator-5.1.5.tgz?cache=0&sync_timestamp=1596082605533&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhar-validator%2Fdownload%2Fhar-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha1-HwgDufjLIMD6E4It8ezds2veHv0= - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.2.tgz?cache=0&sync_timestamp=1593407647372&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-errors%2Fdownload%2Fhttp-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.3.tgz?cache=0&sync_timestamp=1593407647372&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-errors%2Fdownload%2Fhttp-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha1-bGGeT5xgMIw4UZSYwU+7EKrOuwY= - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz?cache=0&sync_timestamp=1600868470262&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-signature%2Fdownload%2Fhttp-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz?cache=0&sync_timestamp=1594184278451&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ficonv-lite%2Fdownload%2Ficonv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha1-ICK0sl+93CHS9SSXSkdKr+czkIs= - dependencies: - safer-buffer ">= 2.1.2 < 3" - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w= - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha1-v/OFQ+64mEglB5/zoqjmy9RngbM= - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.npm.taobao.org/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz?cache=0&sync_timestamp=1599333925809&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjson-schema-traverse%2Fdownload%2Fjson-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha1-afaofZUTq4u4/mO9sJecRI5oRmA= - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz?cache=0&sync_timestamp=1600831159918&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I= - -mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz?cache=0&sync_timestamp=1587700357245&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-types%2Fdownload%2Fmime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8= - dependencies: - mime-db "1.44.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz?cache=0&sync_timestamp=1590596637243&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE= - -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.npm.taobao.org/minimist/download/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha1-Z9ZgFLZqaoqqDAg8X9WN9OTpdgI= - -mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1591257007439&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8= - dependencies: - minimist "^1.2.5" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo= - -multer@^1.4.2: - version "1.4.2" - resolved "https://registry.npm.taobao.org/multer/download/multer-1.4.2.tgz#2f1f4d12dbaeeba74cb37e623f234bf4d3d2057a" - integrity sha1-Lx9NEtuu66dMs35iPyNL9NPSBXo= - dependencies: - append-field "^1.0.0" - busboy "^0.2.11" - concat-stream "^1.5.2" - mkdirp "^0.5.1" - object-assign "^4.1.1" - on-finished "^2.3.0" - type-is "^1.6.4" - xtend "^4.0.0" - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.npm.taobao.org/oauth-sign/download/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha1-R6ewFrqmi1+g7PPe4IqFxnmsZFU= - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -on-finished@^2.3.0, on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ= - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz?cache=0&sync_timestamp=1601400433519&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-to-regexp%2Fdownload%2Fpath-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha1-eCDZsWEgzFXKmud5JoCufbptf+I= - -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha1-/cIzZQVEfT8vLGOO0nLK9hS7sr8= - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.npm.taobao.org/psl/download/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha1-kyb4vPsBOtzABf3/BWrM4CDlHCQ= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha1-tYsBCsQMIsVldhbI0sLALHv0eew= - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha1-QdwaAV49WB8WIXdr4xr7KHapsbw= - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.npm.taobao.org/qs/download/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha1-yzroBuh0BERYTvFUzo7pjUA/PjY= - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE= - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.npm.taobao.org/raw-body/download/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha1-oc5vucm8NWylLoklarWQWeE9AzI= - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.npm.taobao.org/readable-stream/download/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -request@^2.88.2: - version "2.88.2" - resolved "https://registry.npm.taobao.org/request/download/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha1-1zyRhzHLWofaBH4gcjQUb2ZNErM= - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha1-mR7GnSluAxN0fVm9/St0XDX4go0= - -safe-buffer@^5.0.1, safe-buffer@^5.1.2: - version "5.2.1" - resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY= - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo= - -send@0.17.1: - version "0.17.1" - resolved "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg= - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk= - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.npm.taobao.org/sshpk/download/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha1-+2YcC+8ps520B2nuOfpwCT1vaHc= - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -streamsearch@0.1.2: - version "0.1.2" - resolved "https://registry.npm.taobao.org/streamsearch/download/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.npm.taobao.org/string_decoder/download/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha1-nPFhG6YmhdcDCunkujQUnDrwP8g= - dependencies: - safe-buffer "~5.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM= - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.npm.taobao.org/tough-cookie/download/tough-cookie-2.5.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftough-cookie%2Fdownload%2Ftough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha1-zZ+yoKodWhK0c72fuW+j3P9lreI= - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-is@^1.6.4, type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha1-TlUs0F3wlGfcvE73Od6J8s83wTE= - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -uri-js@^4.2.2: - version "4.4.0" - resolved "https://registry.npm.taobao.org/uri-js/download/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" - integrity sha1-qnFCYd55PoqCNHp7zJznTobyhgI= - dependencies: - punycode "^2.1.0" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.npm.taobao.org/uuid/download/uuid-3.4.0.tgz?cache=0&sync_timestamp=1601826526166&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fuuid%2Fdownload%2Fuuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha1-sj5DWK+oogL+ehAK8fX4g/AgB+4= - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -xtend@^4.0.0: - version "4.0.2" - resolved "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q= +"@types/scriptable-ios@^1.6.1": + version "1.6.1" + resolved "https://registry.npm.taobao.org/@types/scriptable-ios/download/@types/scriptable-ios-1.6.1.tgz#44766b47a0c0c9f92a3c1bf46214288cf3d926f4" + integrity sha1-RHZrR6DAyfkqPBv0YhQojPPZJvQ= + +prettier@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==