From ac1263fe092ec790d02753b606398927bd25c8aa Mon Sep 17 00:00:00 2001 From: blackmatrix7 <27717518+blackmatrix7@users.noreply.github.com> Date: Mon, 27 Sep 2021 16:31:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=BE=A4=E6=99=96Download=20?= =?UTF-8?q?Station=E4=B8=8B=E8=BD=BD=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/boxjs.json | 55 ++++++ script/synology/README.md | 80 ++++++++ script/synology/downloadstation.js | 258 +++++++++++++++++++++++++ script/synology/icon/synology.jpg | Bin 0 -> 2343 bytes script/synology/icon/synology_dark.jpg | Bin 0 -> 3472 bytes script/synology/twitter.lnplugin | 8 + script/synology/twitter.qxrewrite | 4 + script/synology/twitter.sgmodule | 8 + 8 files changed, 413 insertions(+) create mode 100644 script/synology/README.md create mode 100644 script/synology/downloadstation.js create mode 100644 script/synology/icon/synology.jpg create mode 100644 script/synology/icon/synology_dark.jpg create mode 100644 script/synology/twitter.lnplugin create mode 100644 script/synology/twitter.qxrewrite create mode 100644 script/synology/twitter.sgmodule diff --git a/script/boxjs.json b/script/boxjs.json index 373076ff1..0683934b8 100644 --- a/script/boxjs.json +++ b/script/boxjs.json @@ -222,5 +222,60 @@ } ] } + { + "id": "blackmatrix7.synology", + "name": "Synology", + "keys": ["syno_https_url", "syno_account", "syno_passwd", "syno_sid"], + "settings": [ + { + "id": "syno_https_url", + "name": "群晖地址", + "val": "", + "type": "input", + "placeholder": "http://192.168.1.100:5000", + "autoGrow": true, + "desc": "目前暂不支持https,请使用http" + }, + { + "id": "syno_account", + "name": "群晖账户", + "val": "", + "type": "input", + "placeholder": "admin", + "autoGrow": true, + "desc": "可访问DownloadStation的账号,记得设置默认下载路径" + }, + { + "id": "syno_passwd", + "name": "群晖密码", + "val": "", + "type": "input", + "placeholder": "", + "autoGrow": true, + "desc": "建议单独建立一个账户保证安全" + }, + { + "id": "syno_sid", + "name": "群晖Sid", + "val": "", + "type": "input", + "placeholder": "", + "autoGrow": true, + "desc": "除非无法正常获取Sid,否则保持现在的值,不要修改" + } + ], + "author": "@blackmatrix7", + "repo": "https://github.com/blackmatrix7/ios_rule_script/tree/master/script/synology", + "icons": [ + "https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/icon/synology_dark.jpg", + "https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/icon/synology.jpg" + ], + "scripts": [ + { + "name": "登录Synology", + "script": "https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/downloadstation.js" + } + ] + } ] } diff --git a/script/synology/README.md b/script/synology/README.md new file mode 100644 index 000000000..44ff66636 --- /dev/null +++ b/script/synology/README.md @@ -0,0 +1,80 @@ +# Download Station + +利用Synology的Download Station下载互联网资源。 + +## 说明 + +一个小玩具,实现将互联网某些资源添加到群晖的Download Stations下载,目前暂时支持推特第三方客户端的图片视频下载。 + +这其实是个架子,提供一种思路:通过脚本获取资源后添加到群晖的Download Station,欢迎有兴趣的大佬一起完善,添加其他功能。~~计划添加自动下载京东电子发票,看什么时候有时间再实现。~~ + +在使用脚本前,需要有一些前提条件: + +1. 一台群晖,安装Download Station +2. 会使用BoxJs +3. 保证能在各种情况下访问群晖 +4. 一点点动手和排查问题的能力 + +## 基础配置 + +### 新建群晖账户 + +访问你的群晖,新增User账户,**一定要新建账户**。为了数据安全,强烈建议不要给予管理员权限。仅需要DSM、~~File Station(实际上不需要,看着给)~~、Download Station三个权限。 + +**目录权限方面,仅需要下载目录的读写权限,其他目录的权限不需要给**,如果你一定要给我也不拦着。 + +### 登录账户 + +登录新建的账户,打开Download Station,首次使用会提示选择下载目录,设置好下载目录后完成配置。 + +### 配置BoxJS + +在BoxJS里填入群晖的http地址,不建议使用https,可能因为证书问题会导致访问失败,账户密码根据最近的设置输入,sid不需要填,会自动获取。 + +目前仅在DSM6.2上验证,DSM7上没有测试是否正常,如果群晖的DSM7接口没有变化应该是可以的。 + +## 存在的问题 + +群晖的sid一段时间后会失效,观察了3个多月,sid有效期大概1个月不到,具体的时间没有统计。sid失效后,需要进入boxjs手动将sid删除,让脚本重新获取。~~自动更新sid可以实现,不过比较懒,看看什么时候手动操作烦了再考虑加这个功能。~~ + +可能会导致用于下载的群晖账户出现异常,表现为sid无论如何重新获取,都无法正常添加下载任务。这个时候需要删除这个用于下载的账户,再根据上述的步骤重新添加账户。目前仅偶尔出现,可能是sid失效后多过重试导致。 + +**所以新建一个专用的账户给脚本使用就非常重要了。** + +## Twitter资源下载 + +### Twitter第三方客户端 + +因为Twitter官方的客户端带SSL Pinning,没办法使用此脚本,所以必须使用第三方客户端。并且**开启此脚本的复写后,会导致Twitter官方客户端无法正常使用。** + +### 操作方式 + +在Twitter第三方客户端中,看到喜欢的推文,点击Like,会自动将推文内的图片和视频下载到群晖中。当然还要保证群晖能访问推特,否则下载资源就无从谈起了…… + +### 配置说明 + +#### Surge + +使用模块 + +```ini +https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/twitter.sgmodule +``` + +#### Loon + +使用插件 + +```ini +https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/twitter.lnplugin +``` + +#### Quantumult X + +配置文件 + +```ini +[rewrite_remote] +https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/twitter.qxrewrite, tag=Twitter_离线下载收藏内容, enabled=true +``` + diff --git a/script/synology/downloadstation.js b/script/synology/downloadstation.js new file mode 100644 index 000000000..cc5db6292 --- /dev/null +++ b/script/synology/downloadstation.js @@ -0,0 +1,258 @@ +const scriptName = "Download Station"; +const synoUrlKey = "syno_https_url"; +const synoAccountKey = "syno_account"; +const synoPasswdKey = "syno_passwd"; +const synoSidKey = "syno_sid"; +let magicJS = MagicJS(scriptName, "INFO"); + +function SynoAuth(synoUrl, account, passwd) { + return new Promise((resolve, reject) => { + let options = { + url: `${synoUrl}/webapi/auth.cgi?api=SYNO.API.Auth&version=2&method=login&account=${account}&passwd=${passwd}&session=DownloadStation&format=cookie`, + headers: { + "User-Agent": "Mozilla/5.0", + }, + }; + magicJS.get(options, (err, resp, data) => { + if (err) { + magicJS.logError(`登录DownloadStation失败,请求异常:${err}`); + reject("❌登录DownloadStation失败,请求异常,请查阅日志!"); + } else { + try { + magicJS.logDebug(`接口响应数据:${data}`); + let obj = typeof data === "string" ? JSON.parse(data) : data; + if (obj.success === true) { + magicJS.logDebug(`登录获取的sid:${obj.data.sid}`); + resolve(obj.data.sid); + } else { + magicJS.logWarning(`登录DownloadStation失败,接口响应:${data}`); + reject(data); + } + } catch (err) { + magicJS.logError(`登录DownloadStation失败,执行异常:${err},接口响应:${data}`); + reject(data); + } + } + }); + }); +} + +function AddTask(synoUrl, sid, uri, destination) { + return new Promise((resolve) => { + let options = { + url: `${synoUrl}/webapi/DownloadStation/task.cgi`, + body: `_sid=${sid}&api=SYNO.DownloadStation.Task&version=1&method=create&uri=${uri}&destination=${destination}`, + }; + magicJS.post(options, (err, resp, data) => { + if (err) { + magicJS.logError(`添加下载任务失败,请求异常:${err}`); + resolve(false); + } else { + try { + let obj = typeof data === "string" ? JSON.parse(data) : data; + if (obj.success === true) { + resolve(true); + } else { + magicJS.logWarning(`添加下载任务失败,接口响应:${data}`); + resolve(false); + } + } catch (err) { + magicJS.logError(`添加下载任务失败,执行异常:${err},接口响应:${data}`); + resolve(false); + } + } + }); + }); +} + +/** + * 获取DownloadStation下载位置 + * @param {*} synoUrl + * @param {*} sid + * @returns + */ +function GetLocation(synoUrl, sid) { + return new Promise((resolve) => { + let options = { + url: `${synoUrl}/webapi/DownloadStation/entry.cgi`, + body: `_sid=${sid}&api=SYNO.DownloadStation2.Settings.Location&version=1&method=get`, + }; + magicJS.post(options, (err, resp, data) => { + if (err) { + magicJS.logError(`添加下载任务失败,请求异常:${err}`); + resolve(false); + } else { + try { + let obj = typeof data === "string" ? JSON.parse(data) : data; + if (obj.success === true) { + resolve(obj.data.default_destination); + } else { + magicJS.logWarning(`添加下载任务失败,接口响应:${data}`); + resolve(""); + } + } catch (err) { + magicJS.logError(`添加下载任务失败,执行异常:${err},接口响应:${data}`); + resolve(""); + } + } + }); + }); +} + +/** + * 创建下载目录 + * @param {*} synoUrl + * @param {*} sid + * @param {*} folderPath + * @param {*} folderName + * @returns + */ +function CreateFolder(synoUrl, sid, folderPath, folderName) { + magicJS.logDebug(`创建下载目录,路径: ${folderPath}/${folderName}`); + return new Promise((resolve, reject) => { + let options = { + url: encodeURI(`${synoUrl}/webapi/entry.cgi?_sid=${sid}&api=SYNO.FileStation.CreateFolder&force_parent=true&version=2&method=create&folder_path=["${folderPath}"]&name=["${folderName}"]`), + }; + magicJS.get(options, (err, resp, data) => { + if (err) { + magicJS.logError(`创建文件目录失败,请求异常:${err}`); + reject(false); + } else { + try { + let obj = typeof data === "string" ? JSON.parse(data) : data; + if (obj.success === true) { + resolve(true); + } else if (obj.error.code === "119") { + magicJS.logWarning(`SID not found,接口响应:${data}`); + magicJS.write(synoSidKey, ""); + reject(data); + } else { + magicJS.logWarning(`创建文件目录失败,接口响应:${data}`); + reject(data); + } + } catch (err) { + magicJS.logError(`创建文件目录失败,执行异常:${err},接口响应:${data}`); + reject(data); + } + } + }); + }); +} + +async function CreateTasks(mediaList, synoSid, synoUrl, synoAccount, synoPasswd, appName, userName) { + let decodeMediaList = []; + let synoDestination = ""; + for (let i = 0; i < mediaList.length; i++) { + let decodeMedia = escape(mediaList[i]); + decodeMediaList.push(decodeMedia); + } + // 获取sid + if (!!!synoSid) { + await SynoAuth(synoUrl, synoAccount, synoPasswd) + .then((value) => { + synoSid = value; + magicJS.write(synoSidKey, synoSid); + }) + .catch((err) => magicJS.notify(`登录失败,异常信息:${err}`)); + } + // 获取DownloadStation默认目录 + await GetLocation(synoUrl, synoSid).then((value) => { + synoDestination = value; + magicJS.logDebug(`当前下载目录:${synoDestination}`); + }); + // 根据用户名创建目录 + await magicJS + .retry(CreateFolder, 1, 100)(synoUrl, synoSid, `/${synoDestination}/${appName}`, userName) + .then((value) => { + if (value === true) { + magicJS.logDebug("创建下载目录成功"); + } + }) + .catch((err) => { + magicJS.logError(`在群晖上创建目录失败,异常信息:${err}`); + }); + // 添加下载任务 + let uri = decodeMediaList.join(","); + let downloadDir = `${synoDestination}/${appName}/${userName}`; + let result = await AddTask(synoUrl, synoSid, uri, downloadDir); + if (result === true) { + magicJS.notify(`${scriptName}`, `添加成功 ${downloadDir}`, `${mediaList.join("\n")}`); + } else if (mediaList) { + magicJS.notify(scriptName, `添加失败 ${downloadDir}`, `${mediaList.join("\n")}`); + } else { + magicJS.notify(scriptName, `添加失败 ${downloadDir}`); + } + return { synoSid, synoDestination }; +} + +(async () => { + // 群晖 Download Station 配置检查 + let synoUrl = magicJS.read(synoUrlKey); + let synoAccount = magicJS.read(synoAccountKey); + let synoPasswd = magicJS.read(synoPasswdKey); + let synoSid = magicJS.read(synoSidKey); + let mediaList = []; + let appName = ""; + let userName = ""; + if (!synoUrl || !synoAccount || !synoPasswd) { + magicJS.logWarning("请先在BoxJS中配置DownloadStation"); + magicJS.notify("请先在BoxJS中配置DownloadStation"); + return; + } + + if (magicJS.isResponse) { + // Twitter收藏下载 + if (/^https?:\/\/api\.twitter\.com\/[0-9.]*\/favorites\/create.json/.test(magicJS.request.url) === true) { + try { + appName = "twitter"; + // 获取媒体url + let obj = JSON.parse(magicJS.response.body); + if (obj.extended_entities && obj.extended_entities.media) { + obj.extended_entities.media.forEach((element) => { + // 使用推文作者名称作为子目录名 + userName = obj.user.screen_name.replace(/[^a-zA-Z0-9]+/, ""); + magicJS.logDebug(`当前推文的用户:${userName}`); + if (element.type == "photo") { + mediaList.push(element.media_url); + } else if (element.type == "video") { + let maxBitrate = 0; + let videoUrl = ""; + element.video_info.variants.forEach((video) => { + if (video.bitrate && video.bitrate > maxBitrate) { + maxBitrate = video.bitrate; + videoUrl = video.url; + } + }); + mediaList.push(videoUrl); + magicJS.logDebug(videoUrl); + } + }); + } + } catch (err) { + magicJS.logError(`添加下载任务失败,异常信息:${err}`); + magicJS.notify("添加下载任务失败,请查阅日志"); + } + } + + // 添加下载任务至DownloadStation + if (mediaList.length > 0) { + await CreateTasks(mediaList, synoSid, synoUrl, synoAccount, synoPasswd, appName, userName); + } + } else { + await SynoAuth(synoUrl, synoAccount, synoPasswd) + .then((value) => { + let msg = `登录成功,获取Sid:${value}`; + magicJS.notify(msg); + magicJS.logInfo(msg); + synoSid = value; + magicJS.write(synoSidKey, synoSid); + }) + .catch((err) => { + magicJS.notify(`登录失败,异常信息:${err}`); + }); + } + magicJS.done(); +})(); + +// prettier-ignore +function MagicJS(scriptName="MagicJS",logLevel="INFO"){return new class{constructor(){if(this.version="2.2.3.3",this.scriptName=scriptName,this.logLevels={DEBUG:5,INFO:4,NOTIFY:3,WARNING:2,ERROR:1,CRITICAL:0,NONE:-1},this.isLoon="undefined"!=typeof $loon,this.isQuanX="undefined"!=typeof $task,this.isJSBox="undefined"!=typeof $drive,this.isNode="undefined"!=typeof module&&!this.isJSBox,this.isSurge="undefined"!=typeof $httpClient&&!this.isLoon,this.node={request:void 0,fs:void 0,data:{}},this.iOSUserAgent="Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Mobile/15E148 Safari/604.1",this.pcUserAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36 Edg/84.0.522.59",this.logLevel=logLevel,this._barkUrl="",this.isNode){this.node.fs=require("fs"),this.node.request=require("request");try{this.node.fs.accessSync("./magic.json",this.node.fs.constants.R_OK|this.node.fs.constants.W_OK)}catch(err){this.node.fs.writeFileSync("./magic.json","{}",{encoding:"utf8"})}this.node.data=require("./magic.json")}else this.isJSBox&&($file.exists("drive://MagicJS")||$file.mkdir("drive://MagicJS"),$file.exists("drive://MagicJS/magic.json")||$file.write({data:$data({string:"{}"}),path:"drive://MagicJS/magic.json"}))}set barkUrl(url){this._barkUrl=url.replace(/\/+$/g,"")}set logLevel(level){this._logLevel="string"==typeof level?level.toUpperCase():"DEBUG"}get logLevel(){return this._logLevel}get isRequest(){return"undefined"!=typeof $request&&"undefined"==typeof $response}get isResponse(){return"undefined"!=typeof $response}get request(){return"undefined"!=typeof $request?$request:void 0}get response(){return"undefined"!=typeof $response?($response.hasOwnProperty("status")&&($response.statusCode=$response.status),$response.hasOwnProperty("statusCode")&&($response.status=$response.statusCode),$response):void 0}get platform(){return this.isSurge?"Surge":this.isQuanX?"Quantumult X":this.isLoon?"Loon":this.isJSBox?"JSBox":this.isNode?"Node.js":"Unknown"}read(key,session=""){let val="";this.isSurge||this.isLoon?val=$persistentStore.read(key):this.isQuanX?val=$prefs.valueForKey(key):this.isNode?val=this.node.data:this.isJSBox&&(val=$file.read("drive://MagicJS/magic.json").string);try{this.isNode&&(val=val[key]),this.isJSBox&&(val=JSON.parse(val)[key]),session&&("string"==typeof val&&(val=JSON.parse(val)),val=val&&"object"==typeof val?val[session]:null)}catch(err){this.logError(err),val=session?{}:null,this.del(key)}void 0===val&&(val=null);try{val&&"string"==typeof val&&(val=JSON.parse(val))}catch(err){}return this.logDebug(`READ DATA [${key}]${session?`[${session}]`:""}(${typeof val})\n${JSON.stringify(val)}`),val}write(key,val,session=""){let data=session?{}:"";if(session&&(this.isSurge||this.isLoon)?data=$persistentStore.read(key):session&&this.isQuanX?data=$prefs.valueForKey(key):this.isNode?data=this.node.data:this.isJSBox&&(data=JSON.parse($file.read("drive://MagicJS/magic.json").string)),session){try{"string"==typeof data&&(data=JSON.parse(data)),data="object"==typeof data&&data?data:{}}catch(err){this.logError(err),this.del(key),data={}}this.isJSBox||this.isNode?(data[key]&&"object"==typeof data[key]||(data[key]={}),data[key].hasOwnProperty(session)||(data[key][session]=null),void 0===val?delete data[key][session]:data[key][session]=val):void 0===val?delete data[session]:data[session]=val}else this.isNode||this.isJSBox?void 0===val?delete data[key]:data[key]=val:data=void 0===val?null:val;"object"==typeof data&&(data=JSON.stringify(data)),this.isSurge||this.isLoon?$persistentStore.write(data,key):this.isQuanX?$prefs.setValueForKey(data,key):this.isNode?this.node.fs.writeFileSync("./magic.json",data):this.isJSBox&&$file.write({data:$data({string:data}),path:"drive://MagicJS/magic.json"}),this.logDebug(`WRITE DATA [${key}]${session?`[${session}]`:""}(${typeof val})\n${JSON.stringify(val)}`)}del(key,session=""){this.logDebug(`DELETE KEY [${key}]${session?`[${session}]`:""}`),this.write(key,null,session)}notify(title=this.scriptName,subTitle="",body="",opts=""){let convertOptions;if(opts=(_opts=>{let newOpts={};if("string"==typeof _opts)this.isLoon?newOpts={openUrl:_opts}:this.isQuanX?newOpts={"open-url":_opts}:this.isSurge&&(newOpts={url:_opts});else if("object"==typeof _opts)if(this.isLoon)newOpts.openUrl=_opts["open-url"]?_opts["open-url"]:"",newOpts.mediaUrl=_opts["media-url"]?_opts["media-url"]:"";else if(this.isQuanX)newOpts=_opts["open-url"]||_opts["media-url"]?_opts:{};else if(this.isSurge){let openUrl=_opts["open-url"]||_opts.openUrl;newOpts=openUrl?{url:openUrl}:{}}return newOpts})(opts),1==arguments.length&&(title=this.scriptName,subTitle="",body=arguments[0]),this.logNotify(`title:${title}\nsubTitle:${subTitle}\nbody:${body}\noptions:${"object"==typeof opts?JSON.stringify(opts):opts}`),this.isSurge)$notification.post(title,subTitle,body,opts);else if(this.isLoon)opts?$notification.post(title,subTitle,body,opts):$notification.post(title,subTitle,body);else if(this.isQuanX)$notify(title,subTitle,body,opts);else if(this.isNode){if(this._barkUrl){let content=encodeURI(`${title}/${subTitle}\n${body}`);this.get(`${this._barkUrl}/${content}`,()=>{})}}else if(this.isJSBox){let push={title:title,body:subTitle?`${subTitle}\n${body}`:body};$push.schedule(push)}}notifyDebug(title=this.scriptName,subTitle="",body="",opts=""){"DEBUG"===this.logLevel&&(1==arguments.length&&(title=this.scriptName,subTitle="",body=arguments[0]),this.notify(title,subTitle,body,opts))}log(msg,level="INFO"){this.logLevels[this._logLevel]void 0===_options.body?"":`${encodeURIComponent(key)}=${encodeURIComponent(_options.body[key])}`).join("&");_options.url.indexOf("?")<0&&(_options.url+="?"),_options.url.lastIndexOf("&")+1!=_options.url.length&&_options.url.lastIndexOf("?")+1!=_options.url.length&&(_options.url+="&"),_options.url+=qs,delete _options.body}return this.isQuanX?(_options.hasOwnProperty("body")&&"string"!=typeof _options.body&&(_options.body=JSON.stringify(_options.body)),_options.method=method):this.isNode?(delete _options.headers["Accept-Encoding"],"object"==typeof _options.body&&("GET"===method?(_options.qs=_options.body,delete _options.body):"POST"===method&&(_options.json=!0,_options.body=_options.body))):this.isJSBox&&(_options.header=_options.headers,delete _options.headers),_options}adapterHttpResponse(resp){let _resp={body:resp.body,headers:resp.headers,json:()=>JSON.parse(_resp.body)};return resp.hasOwnProperty("statusCode")&&resp.statusCode&&(_resp.status=resp.statusCode),_resp}get(options,callback){let _options=this.adapterHttpOptions(options,"GET");this.logDebug(`HTTP GET: ${JSON.stringify(_options)}`),this.isSurge||this.isLoon?$httpClient.get(_options,callback):this.isQuanX?$task.fetch(_options).then(resp=>{resp.status=resp.statusCode,callback(null,resp,resp.body)},reason=>callback(reason.error,null,null)):this.isNode?this.node.request.get(_options,(err,resp,data)=>{resp=this.adapterHttpResponse(resp),callback(err,resp,data)}):this.isJSBox&&(_options.handler=resp=>{let err=resp.error?JSON.stringify(resp.error):void 0,data="object"==typeof resp.data?JSON.stringify(resp.data):resp.data;callback(err,resp.response,data)},$http.get(_options))}getPromise(options){return new Promise((resolve,reject)=>{magicJS.get(options,(err,resp)=>{err?reject(err):resolve(resp)})})}post(options,callback){let _options=this.adapterHttpOptions(options,"POST");if(this.logDebug(`HTTP POST: ${JSON.stringify(_options)}`),this.isSurge||this.isLoon)$httpClient.post(_options,callback);else if(this.isQuanX)$task.fetch(_options).then(resp=>{resp.status=resp.statusCode,callback(null,resp,resp.body)},reason=>{callback(reason.error,null,null)});else if(this.isNode){let resp=this.node.request.post(_options,callback);resp.status=resp.statusCode,delete resp.statusCode}else this.isJSBox&&(_options.handler=resp=>{let err=resp.error?JSON.stringify(resp.error):void 0,data="object"==typeof resp.data?JSON.stringify(resp.data):resp.data;callback(err,resp.response,data)},$http.post(_options))}get http(){return{get:this.getPromise,post:this.post}}done(value={}){"undefined"!=typeof $done&&$done(value)}isToday(day){if(null==day)return!1;{let today=new Date;return"string"==typeof day&&(day=new Date(day)),today.getFullYear()==day.getFullYear()&&today.getMonth()==day.getMonth()&&today.getDay()==day.getDay()}}isNumber(val){return"NaN"!==parseFloat(val).toString()}attempt(promise,defaultValue=null){return promise.then(args=>[null,args]).catch(ex=>(this.logError(ex),[ex,defaultValue]))}retry(fn,retries=5,interval=0,callback=null){return(...args)=>new Promise((resolve,reject)=>{function _retry(...args){Promise.resolve().then(()=>fn.apply(this,args)).then(result=>{"function"==typeof callback?Promise.resolve().then(()=>callback(result)).then(()=>{resolve(result)}).catch(ex=>{retries>=1?interval>0?setTimeout(()=>_retry.apply(this,args),interval):_retry.apply(this,args):reject(ex),retries--}):resolve(result)}).catch(ex=>{this.logRetry(ex),retries>=1&&interval>0?setTimeout(()=>_retry.apply(this,args),interval):retries>=1?_retry.apply(this,args):reject(ex),retries--})}_retry.apply(this,args)})}formatTime(time,fmt="yyyy-MM-dd hh:mm:ss"){var o={"M+":time.getMonth()+1,"d+":time.getDate(),"h+":time.getHours(),"m+":time.getMinutes(),"s+":time.getSeconds(),"q+":Math.floor((time.getMonth()+3)/3),S:time.getMilliseconds()};/(y+)/.test(fmt)&&(fmt=fmt.replace(RegExp.$1,(time.getFullYear()+"").substr(4-RegExp.$1.length)));for(let k in o)new RegExp("("+k+")").test(fmt)&&(fmt=fmt.replace(RegExp.$1,1==RegExp.$1.length?o[k]:("00"+o[k]).substr((""+o[k]).length)));return fmt}now(){return this.formatTime(new Date,"yyyy-MM-dd hh:mm:ss")}today(){return this.formatTime(new Date,"yyyy-MM-dd")}sleep(time){return new Promise(resolve=>setTimeout(resolve,time))}}(scriptName)} \ No newline at end of file diff --git a/script/synology/icon/synology.jpg b/script/synology/icon/synology.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c4b5f20a6a1f1167229231d2a8fda34d6931fe98 GIT binary patch literal 2343 zcmbW13pCX07QnwTFNPCpC{s*A#uUeEQVw?x@|ezbLNOj;QACgHk;jzBRC>r!qL50e zDSBWk#POP07lsQ!p-{jL`2aiwGM!nG;Q+Y111bPO8K90v0t{lI5FsG~04C=Mz$4lU5s!-~ zfJQWG-hWZ)Uy&Ga5&hLh{J>$86N7X|iw&kf`>yx%rTI8{|J{}Lle6m@ zM=xt9FK;V94^K05GfPt%fTzG(kBF$as67!;(aWvO%)wemhC61y8f1O3jlP%~y}Fq% zvcMh8_fQ)nui*$Xy@&8;XB{WhmURi~piUd??03{R}t%N~ivGc&F6vPiO zn%IT<77n;Yo+0=ZF+|G)*_V_J9B<#(^6H*8vn)B!=}yNK1^Rf|E=4?!na36?${F-pODDfyD#m~;Uh=Wk7eYXI-Q%Bf2QE< zWzLnW#U@yv&h zl21~Ze0H7-xeb3{{Ymz3T$%`%5(b0D;ODteN{NV|H8EIy3*15nPkczsq7{}0l!=bn zmu}xzF|hKQ)(Va7CTJU44;hK)(Y}!VcVGwqFS0+uzH;>gH8cu2JhUdDgA!*VbG-E= z(bF0Hl?3)!*F6c6m|DyHBhTHMv}a7N;*wSE9Dg*POdEItg9#rP+)F=s+&`4lv6N_2 zZ7tMFcI&Gw(h&L%+>hvXSlMS*Vwm(HNH;m0$|?w%Zcn;-y_*#6;Z?Lr9nuO;x8F?{ ziD2NLCG$>QE~$h8k2klY2-5DXhQZ(X!P3X<yfFpp3P6EMV3YB-*xN3onF$LmYub)vusyq zrY?i>Vu1z6IBd@;7#wE4Ru9K{B>Hg1UdNSM$|j2AK%X1`6%wD(UZkb&)&-Pig}Uk>8$nc%weMEk9xdmGGs4_ zl5!xIn(x!uGq&%9lIJkc4;0;F%cOn0S6On>V52?^+9qzY1zQz|S*$3zPT$AiOWpLr z2cr>}Go%Zm%SD4yMP`1mWYqX$`NBG0nsCN4kS#c0)KgPImio<-yky-i>$BTauiIVN z?7mW0T*$FI?<2~nY*5`;eV{Thkb5Ou|7r1>%=l6Yhr#8q+otINMKYa+gf-%dh0z#C8aBD^tsb*K)eY_Ek5j%Qf?%+V&u=Bp8s9UJG2H z`1d?z`iM*L?1T}^Tck+c2!p_-a>2PgcEf9Xb_c)u{z4MvazpcZ%H<3ofx&(lh($Nq z4dfZx{bVN?WDAgcSKcZoh*HroXl*QmKC;^cSg|_f8W;f8kw;cFH?!M1xtHe_9D@PQ z_y^>s*X%i*$j0+Vw$;LbyN-S}I`=n)9+E0&0;(T@avz?7L2cL5p8S>*dQp-tNL;+> zEey86Ktu{lFwN{rk~zR&g(XtH->N1qdw7ODJ0|RG5DeiJ>5q}}w{Zy#Fvx00-UNqj zE4J*C*1K}$g<6fuC%c`s-Y*++CVJqx3Oo{%5->N0Y+SDTnyffcTRoRRZ)5JACeOM2 zfw6S>vubaq^yF+EsE-V|4# zrLD!9U5Yz3>RR*!`V|JRtRFS3IhXR5H>=zAiayiMYby9QNo}9o zKOc4;UQ~FhrwJ1hb8PdITe!@q`vp-$q$E}kd->u!Jwc!VDQ*?M9-k{j``^m z2cgAVE-dcdcCsbB>I^o|JeoD;+K__g?9h1EAJ!nLy_FIF@=~k8MDQxUBF(CU=g!8ygn$(mBUHX^ex{fZ785n=Lh^2hD`c1X1kJF?NK6oZe^~sMdA0 z-*GCr8wRCzRGeH literal 0 HcmV?d00001 diff --git a/script/synology/icon/synology_dark.jpg b/script/synology/icon/synology_dark.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb6611399440764e586bc5647becc2febcb6122f GIT binary patch literal 3472 zcmeHIX;4#F6uvJn3&aHiF$hT6AwU@+Y$k#Tluc9+7pN$UQrTPp1q4}zy1*y`MbxS+ zB8XNZV%#xBL9}WWM8Z-*WECldJ#4vs0coi-?X>;TUz|7J-1F}J=DzctGjBdv3*Q0B zRW|lE06`F7hdlr`lYsSpza0SB+iL?c000g!5FDUlnFa>G!+3yTD+|L)3=BPnK1+|u zVwfoD8?*G5vGfeFT~VL_MW(q#>{EUT}qZEWrASFPW$ag&Sd*PFL{f3w5K*Ux|N zzL3zc{oxVu3Ev(%eB@|iYT9?{Co@i+K9h4H_hMfDrGm@Xi*Iml-nxC~?!!lA<&P_# zJpHNab@iJXUTs}{^ZS<8w)T$B4_$rz1A{}uBZAQ}kuC%q+e}Og`%D)F(}lz1Q9Mbc z3&Dk9Kq+{FvOZDDVjamVNLqyvCq}hQ$u24tS2b|vFWMGdNv5e8HmmoEq)jRNXTlEt zCuP42`$Jbfn2#dZ@K6dc2S4W|(!t*Vz07w!*lW6>>b~PMlDTj)JOgN4t8 z)Q(9TZoeA@*ROKpZTH0G>XZ(&F((M&%6)r|SeW;*xDaq3$C{x+YSRq!!mONa4ne}L zGV?*XDPpp~A<_y0iz^U}Pxzv*%$wK7R0lwy&K=jrav$M#k5OF+>Rd8SlI~l@Lm)lH z3@8$$v%7{(A}s6tU7W-X1+9UplsiY6nvJoBv#(%B@uS<9`vmAZGxJbowIt6yJ6l`& z3l)V@y^@)u$2=ftYH`otySY?{*oSx(xc{H;dFZ%)h#mU*P8J?( zIU-@Tl~*1kn0y66x2(*7(ZlnH?y(ag=o`<5pq8<;-51qpDhn;zzw1VE_J>}kC3mOS z`17=LZw*q03LD-jj!gzk)^dj<-h1}3%A0RinO`?-SC?H;T`3$$JKIqm!P~``n2hEK zyFFfXq6vMy6UWc+CL=kXH6zI}t@~=YAKR`FW6AWKDVR0A>zFta3;{1Z<^)^Z%(13~ zH+zSS?Zir98u4Ze!aNp1&@zGz>zcR6a~?TDuIGUc1Qk7pA@F>{J)N~suEYnc?^jWM z-}BO3ewaWZ*m^bQp3Fc5Xf{0nfodKH0uNnRqmq`UQ3WIR*yx{IX!2PL*9jb27MMF+ z%dmTsE2(4WuH_n;9|#I6)~0uI`3VBwfyrY&5JWnWUQk_1-fo^S%90>q0%B;(N@m*L*e%#?J5kEj-^7^*0n)Wp+GC5~`WtpCmfZ{A zD1hNCiOefC(Fg?W8t&U92z&}(uI6{dbKLd@<@=3AJy*V&aV(gzJes54-6+r3Eq>Yh z$a^F6CixB`I>*vo#^_!q65(%iwf2Kj3Efqj2qpk4d^+E zQ`oTFRj(`Gs4+RQQDv|Q`+YPaNGv2Kg5m;qhf7^DG8yubA6gkJD&tav1GPC+C&ZfS zL~?9q%Xc*@ysm$?me%ONOdCl#$y5q^GI+^!*kzi&Uqr{oM+B?=A38FdHz@D`3;L|=?ee= literal 0 HcmV?d00001 diff --git a/script/synology/twitter.lnplugin b/script/synology/twitter.lnplugin new file mode 100644 index 000000000..84e122af9 --- /dev/null +++ b/script/synology/twitter.lnplugin @@ -0,0 +1,8 @@ +# Twitter离线下载收藏内容 + +[Script] +http-response ^https?:\/\/api\.twitter\.com\/[0-9.]*\/favorites\/create.json requires-body=1,script-path=https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/downloadstation.js,tag=Twitter_离线下载收藏内容 + + +[MITM] +hostname = api.twitter.com \ No newline at end of file diff --git a/script/synology/twitter.qxrewrite b/script/synology/twitter.qxrewrite new file mode 100644 index 000000000..a2f088f84 --- /dev/null +++ b/script/synology/twitter.qxrewrite @@ -0,0 +1,4 @@ +# Twitter_离线下载收藏内容 +^https?:\/\/api\.twitter\.com\/[0-9.]*\/favorites\/create.json url script-response-body https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/downloadstation.js + +hostname = api.twitter.com diff --git a/script/synology/twitter.sgmodule b/script/synology/twitter.sgmodule new file mode 100644 index 000000000..ce8e4ce0b --- /dev/null +++ b/script/synology/twitter.sgmodule @@ -0,0 +1,8 @@ +#!name=Twitter +#!desc=Twitter离线下载收藏内容 + +[Script] +Twitter_收藏离线下载 = type=http-response,requires-body=1,max-size=0,pattern=^https?:\/\/api\.twitter\.com\/[0-9.]*\/favorites\/create.json,script-path=https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/script/synology/downloadstation.js + +[MITM] +hostname = %APPEND% api.twitter.com \ No newline at end of file