From 6f0d03bd589173bde60c0d7290045e12ce7ccde3 Mon Sep 17 00:00:00 2001 From: tickstep Date: Thu, 6 Mar 2025 11:06:50 +0800 Subject: [PATCH] add javascript plugin manual --- README.md | 2 +- build.sh | 1 + docs/manual.md | 468 +--------------------------------------- docs/plugin_manual.md | 486 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 489 insertions(+), 468 deletions(-) create mode 100644 docs/plugin_manual.md diff --git a/README.md b/README.md index ca50f3f..787d4d9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ 5. [上传](docs/manual.md#上传文件目录)本地文件, 支持多个文件或目录上传,支持排除指定文件夹/文件(正则表达式)功能。支持软链接(符号链接)文件。 6. [同步备份功能](docs/manual.md#同步备份功能)支持备份本地文件到云盘,备份云盘文件到本地,双向同步备份保持本地文件和网盘文件同步。常用于嵌入式或者NAS等设备,支持docker镜像部署。 7. 命令和文件路径输入支持Tab键自动补全,路径支持通配符匹配模式 -8. 支持[JavaScript插件](docs/manual.md#JavaScript插件),你可以按照自己的需要定制上传/下载中关键步骤的行为,最大程度满足自己的个性化需求 +8. 支持[JavaScript插件](docs/plugin_manual.md#简介),你可以按照自己的需要定制上传/下载中关键步骤的行为,最大程度满足自己的个性化需求 9. 支持共享相册的相关操作,支持批量下载相册所有普通照片、实况照片文件到本地 10. 支持多用户联合下载功能,对下载速度有极致追求的用户可以尝试使用该选项。详情请查看文档[多用户联合下载](docs/manual.md#多用户联合下载) diff --git a/build.sh b/build.sh index 65ab2d9..43ddacf 100755 --- a/build.sh +++ b/build.sh @@ -76,6 +76,7 @@ Pack() { cp README.md "$output/$1" cp docs/manual.md "$output/$1" + cp docs/plugin_manual.md "$output/$1" cp -rf assets/scripts/* "$output/$1" cp -rf assets/plugin "$output/$1" cp -rf assets/sync_drive "$output/$1" diff --git a/docs/manual.md b/docs/manual.md index 975a212..186c8f0 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -39,16 +39,6 @@ + [Windows后台启动](#Windows后台启动) + [Docker运行](#Docker运行) * [JavaScript插件](#JavaScript插件) - + [如何使用](#如何使用) - + [JS中内置的函数](#JS中内置的函数) - + [常见场景样例](#常见场景样例) - + [1.禁止特定文件上传](#1.禁止特定文件上传) - + [2.上传文件后删除本地文件](#2.上传文件后删除本地文件) - + [3.下载文件并截断过长的文件名](#3.下载文件并截断过长的文件名) - + [4.上传文件去掉文件名包含的部分字符](#4.上传文件去掉文件名包含的部分字符) - + [5.上传文件时去掉指定目录或者文件](#5.上传文件时去掉指定目录或者文件) - + [6.下载云盘文件到本地后删除云盘对应的文件](#6.下载云盘文件到本地后删除云盘对应的文件) - + [7.Token刷新失败发送外部通知](#7.Token刷新失败发送外部通知) * [显示和修改程序配置项](#显示和修改程序配置项) - [常见问题Q&A](#常见问题Q&A) * [1. 如何开启Debug调试日志](#1-如何开启Debug调试日志) @@ -905,463 +895,7 @@ function syncScanPanFilePrepareCallback(context, params) { ``` ## JavaScript插件 -支持javascript插件,你可以按照自己的需要定制上传/下载中关键步骤的行为,最大程度满足自己的个性化需求。 -例如: -1.排除某个特定敏感文件的上传 -2.上传文件进行改名,但是本地文件不做更改 -3.上传完成文件,删除本地文件 -4.上传的文件路径进行更改,但是本地的文件保持不变 -5.上传文件成功后,通过HTTP通知其他服务 -6.排除某些网盘文件的下载 -7.下载的文件进行改名,但是网盘的文件保持不变 -8.下载的文件路径进行更改,但是网盘的文件保持不变 -9.下载文件完成后,通过HTTP通知其他服务 -10.同步备份功能,支持过滤本地文件,或者过滤云盘文件。定制上传或者下载需要同步的文件 -11.Token刷新失败或者过期,发送外部通知(HTTP&邮件) - -### 如何使用 -JS插件的样本文件默认存放在程序所在的plugin/js文件夹下,分为下载(download_handler.js.sample)、上传(upload_handler.js.sample)、同步备份(sync_handler.js.sample)、用户Token(token_handler.js.sample)插件。 - -建议拷贝一份并将后缀名更改为.js,例如:upload_handler.js,不然插件不会生效。 -你必须具备一定的JS语言基础,然后按照里面的样例根据自己所需进行改动即可。 -如果你有通过环境变量ALIYUNPAN_CONFIG_DIR设置配置目录,则需要将plugin文件夹拷贝到配置的目录中才可以生效。 - -### JS中内置的函数 -目前只开放了如下函数,你可以在你的js脚本中直接调用 -1.console.log() -打印日志,这个日志需要开启debug日志才会在控制台窗口显示 -```js -console.log("hello world"); -``` -2.console.println() -打印日志,这个日志会直接在控制台窗口显示,无需开启debug日志 -```js -console.println("hello world"); -``` - -3.PluginUtil.Http.get() -发起HTTP的get请求 -```js - var header = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36", - "Content-Type": "application/json", - "Accept": "application/json" - }; - try { - var r = PluginUtil.Http.get(header, "https://625f528c53a42eaa07f37e13.mockapi.io/files/1"); - console.log(r); - } catch (e) { - if (e !== "Error") { - throw e; - } - } -``` - -4.PluginUtil.Http.post() -发起HTTP的post请求 -```js - var header = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36", - "Content-Type": "application/json", - "Accept": "application/json" - }; - try { - var reqDataStr = JSON.stringify({ - "id": "1", - "localFilePath": "/usr/local/src/borders_burundi_producer.gram.aab", - "localFileSize": 1111, - "uploadApproved": false - }); - var r = PluginUtil.Http.post(header, "https://625f528c53a42eaa07f37e13.mockapi.io/files", reqDataStr); - console.log(r); - } catch (e) { - if (e !== "Error") { - throw e; - } - } -``` - -5.PluginUtil.LocalFS.deleteFile() -删除本地指定文件,不支持文件夹 -``` -PluginUtil.LocalFS.deleteFile(localFilePath); -其中: -localFilePath - 本地文件的绝对完整路径 -``` -样例 -```js -PluginUtil.LocalFS.deleteFile("/Users/tickstep/Downloads/IMG_0884.HEIC"); -``` - -6.PluginUtil.PanFS.deleteFile() -删除云盘指定文件,支持文件、文件夹 -``` -PluginUtil.PanFS.deleteFile(userId, driveId, panFileId); -其中: -userId - 登录的用户ID -driveId - 网盘ID -panFileId - 网盘文件ID -``` -样例 -```js -var userId = context["userId"] -var driveId = params["driveId"] -var driveFileId = params["driveFileId"] -if (PluginUtil.PanFS.deleteFile(userId, driveId, driveFileId)) { - console.println("插件删除云盘文件成功") -} -``` - -7.PluginUtil.Email.sendTextMail() -发送文本邮件,定义如下 -``` -PluginUtil.Email.sendTextMail(mailServer, userName, password, to, subject, body) - -其中: -mailServer - smtp服务器+端口 -userName - 发件人邮箱地址 -password - 发件人邮箱密码 -to - 收件人邮箱地址 -subject - 邮件标题 -body - 邮件内容,纯文本 -``` -样例 -```js -PluginUtil.Email.sendTextMail("smtp.qq.com:465", "123xxx@qq.com", "pwdxxxxxx", "12545xxx@qq.com", "文件上传通知", "该文件已经上传完毕"); -``` - -8.PluginUtil.Email.sendHtmlMail() -发送HTML富文本邮件,定义如下 -``` -PluginUtil.Email.sendHtmlMail(mailServer, userName, password, to, subject, body) - -其中: -mailServer - smtp服务器+端口 -userName - 发件人邮箱地址 -password - 发件人邮箱密码 -to - 收件人邮箱地址 -subject - 邮件标题 -body - 邮件内容,HTML富文本 -``` -样例 -```js -var html = "" - +"" - +"

文件上传通知

" - +"

该文件已经上传完毕

" - +"" - +""; -PluginUtil.Email.sendHtmlMail("smtp.qq.com:465", "123xxx@qq.com", "pwdxxxxxx", "12545xxx@qq.com", "文件上传通知", html); -``` - -9.PluginUtil.KV.putString() -存储键值对,定义如下 -``` -PluginUtil.KV.putString(key, value) - -其中: -key - 键,字符串 -value - 值,字符串 -``` -样例 -```js -PluginUtil.KV.putString("mykey", "1670419352"); -``` - -10.PluginUtil.KV.getString() -获取存储的键值,指定对应的键,返回存储的值,如果没有则返回空字符串,定义如下 -``` -PluginUtil.KV.getString(key) - -其中: -key - 键,字符串 -``` -样例 -```js -var value = PluginUtil.KV.getString("mykey"); -``` - -### 常见场景样例 -这里收集了一些常见的需求样例,可以作为插件定制的样例模板。 - -#### 1.禁止特定文件上传 -使用JavaScript上传插件中的`uploadFilePrepareCallback`函数。如下所示: -```js -function uploadFilePrepareCallback(context, params) { - console.log(params) - - var result = { - "uploadApproved": "yes", - "driveFilePath": "" - }; - if (params["localFileType"] != "file") { - // do nothing - return result; - } - - // 禁止点号.开头的文件上传 - if (params["localFileName"].indexOf(".") == 0) { - result["uploadApproved"] = "no"; - } - - // 禁止.txt文件上传 - if (params["localFileName"].search(/.txt$/i) >= 0) { - result["uploadApproved"] = "no"; - } - - // 禁止password.key文件上传 - if (params["localFileName"] == "password.key") { - result["uploadApproved"] = "no"; - } - - return result; -} -``` - -#### 2.上传文件后删除本地文件 -使用JavaScript上传插件中的`uploadFileFinishCallback`函数。如下所示: -```js -function uploadFileFinishCallback(context, params) { - console.log(params); - if (params["localFileType"] != "file") { - // do nothing - return; - } - if (params["uploadResult"] == "success") { - PluginUtil.LocalFS.deleteFile(params["localFilePath"]); - } -} -``` - -#### 3.下载文件并截断过长的文件名 -有些文件的路径或者名称太长,下载的时候可能会由于路径名称过程导致无法下载,这时候可以使用JavaScript下载插件中的`downloadFilePrepareCallback`函数定制下载保存的文件名。 -如下所示: -```js -function downloadFilePrepareCallback(context, params) { - console.log(params) - - var result = { - "downloadApproved": "yes", - "localFilePath": "" - }; - if (params["driveFileType"] != "file") { - return result; - } - - // 下面的代码都是分隔路径,方便后面修改路径时使用 - var filePath = params["localFilePath"]; - filePath = filePath.replace(/\\/g, "/"); - - // 目录完整路径 - var dirPath = ""; - // 文件名,不包括后缀名 - var fileName = ""; - // 文件后缀名 - var fileExt = ""; - - var idx = filePath.lastIndexOf('/'); - if (idx > 0) { - dirPath = filePath.substring(0,idx); - fileName = filePath.substring(idx+1,filePath.length); - } else { - fileName = filePath; - } - idx = fileName.lastIndexOf(".") - if (idx > 0) { - fileExt = fileName.substring(idx,fileName.length); - fileName = fileName.substring(0, fileName.length-fileExt.length) - } - - // 开始按照需要截断太长的文件路径,例如下面的这个例子: - // - // dirPath + "/" ==> 这个的意思是保留前面的文件夹路径,如果不需要就去掉 - // fileName.substr(0,10) ==> 文件名只取前面10个字符,其他的不要了 - // + fileExt ==> 把文件的后缀名补上 - var saveFilePath = dirPath + "/" + fileName.substr(0,10) + fileExt; - - // 返回 - result["localFilePath"] = saveFilePath; - return result; -} -``` -效果如下: -1)假如网盘源路径是:`/亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi` -2)下载后保存本地的路径是:`D:\test\亚马逊书籍合集\亚马逊 kindle ebook 大合集5289册\经济金融 (2)\解密Instagra.mobi` -``` -[1] ---- -文件ID: 630f0623e67c0a1d2e554b1994a29108d089f0d5 -文件名: 解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi -文件类型: 文件 -文件路径: /亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi - -插件修改文件下载保存路径为: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi -[1] 准备下载: /亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi -[1] 将会下载到路径: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi -[1] 下载开始 - -[1] 下载完成, 保存位置: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi -[1] 检验文件有效性成功: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi -``` -#### 4.上传文件去掉文件名包含的部分字符 -例如本地文件夹名称为```[周杰伦]范特西[mp3]```,上传到网盘希望能更改成名称```[周杰伦]范特西```但保持本地文件夹名称不变,因为文件夹有很多不希望每个手动更改,可以这样实现: -```js -function uploadFilePrepareCallback(context, params) { - var result = { - "uploadApproved": "yes", - "driveFilePath": "" - }; - - // 去掉网盘保存路径中包含的[mp3]字段 - var filePath = params["driveFilePath"]; - filePath = filePath.replace(/\[mp3\]/g, ""); - result["driveFilePath"] = filePath; - - return result; -} -``` -#### 5.上传文件时去掉指定目录或者文件 -upload命令本身支持exn参数排除上传文件或目录,但是只能指定文件名称,不能指定文件的路径。如果需要排除指定路径则可以通过插件脚本实现,样例如下: -```js -function uploadFilePrepareCallback(context, params) { - //(自行配置)禁止上传的本地【目录列表】,使用绝对路径,可以配置多个路径用逗号分隔 - var forbiddenUploadFolders = ["/Users/tickstep/Downloads/up/target","/Users/tickstep/Downloads/up/.idea"] - //(自行配置)禁止上传的本地【文件列表】,使用绝对路径,可以配置多个路径用逗号分隔 - var forbiddenUploadFiles = ["/Users/tickstep/Downloads/up/pom.xml"] - - // -------------------- 以下代码不要修改 -------------------- - var result = { - "uploadApproved": "yes", - "driveFilePath": "" - }; - - // 下面的代码都是分隔路径,方便后面使用 - var filePath = params["localFilePath"]; - filePath = filePath.replace(/\\/g, "/"); - // 目录完整路径 - var dirPath = ""; - // 文件名,不包括后缀名 - var fileName = ""; - // 文件后缀名 - var fileExt = ""; - var idx = filePath.lastIndexOf('/'); - if (idx > 0) { - dirPath = filePath.substring(0,idx); - fileName = filePath.substring(idx+1,filePath.length); - } else { - fileName = filePath; - } - idx = fileName.lastIndexOf(".") - if (idx > 0) { - fileExt = fileName.substring(idx,fileName.length); - fileName = fileName.substring(0, fileName.length-fileExt.length) - } - - if (params["localFileType"] == "file") { - for (var i = 0; i < forbiddenUploadFiles.length; i++) { - if (forbiddenUploadFiles[i].replace(/\\/g, "/") == params["localFilePath"]) { - result["uploadApproved"] = "no"; // 禁止文件上传 - break - } - } - for (var i = 0; i < forbiddenUploadFolders.length; i++) { - if (forbiddenUploadFolders[i].replace(/\\/g, "/") == dirPath) { - result["uploadApproved"] = "no"; // 禁止文件上传 - break - } - } - } else if (params["localFileType"] == "folder") { - for (var i = 0; i < forbiddenUploadFolders.length; i++) { - if (forbiddenUploadFolders[i].replace(/\\/g, "/") == params["localFilePath"]) { - result["uploadApproved"] = "no"; // 禁止文件上传 - break - } - } - } - - return result; -} -``` -#### 6.下载云盘文件到本地后删除云盘对应的文件 -```js -function downloadFileFinishCallback(context, params) { - console.log(params) - // 云盘文件成功下载到本地后,删除云盘的文件 - if (params["downloadResult"] == "success") { - if (params["driveFileType"] == "file") { - // 文件下载成功,删除该云盘文件 - var userId = context["userId"] - var driveId = params["driveId"] - var driveFileId = params["driveFileId"] - if (PluginUtil.PanFS.deleteFile(userId, driveId, driveFileId)) { - console.log("插件删除云盘文件成功:" + params["driveFilePath"]) - } - } - } -} -``` - -#### 7.Token刷新失败发送外部通知 -Token刷新失败发送外部通知,例如Server酱 -```js -function userTokenRefreshFinishCallback(context, params) { - var header = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36", - "Content-Type": "application/json", - "Accept": "application/json" - }; - try { - if (params["result"] === "fail") { - // 避免频繁发送 - var ONE_MINUTE = 60 * 1000; - var lastSendEmailTime = PluginUtil.KV.getString("email_last_send_time"); - if (lastSendEmailTime != "") { - if ((Date.now() - lastSendEmailTime) < (10 * ONE_MINUTE)) { - console.log("距离上次发送邮件小于10分钟,先不发送了"); - return - } - } - PluginUtil.KV.putString("email_last_send_time", Date.now()); - - var reqData = { - "text": "Token刷新失败", - "desp": params["message"] - }; - var r = PluginUtil.Http.post(header, "https://sctapi.ftqq.com/xxxxxkeyxxxxxx.send", JSON.stringify(reqData)); - } - } catch (e) { - if (e !== "Error") { - throw e; - } - } -} -``` -或者发送邮件通知 -```js -function userTokenRefreshFinishCallback(context, params) { - try { - if (params["result"] === "fail") { - // 避免频繁发送 - var ONE_MINUTE = 60 * 1000; - var lastSendEmailTime = PluginUtil.KV.getString("email_last_send_time"); - if (lastSendEmailTime != "") { - if ((Date.now() - lastSendEmailTime) < (10 * ONE_MINUTE)) { - console.log("距离上次发送邮件小于10分钟,先不发送了"); - return - } - } - PluginUtil.KV.putString("email_last_send_time", Date.now()); - - // 发送通知邮件,请确保你的发送邮箱具备smtp发送权限,没有的需要找邮箱提供商配置开启 - console.log("发送通知邮件"); - PluginUtil.Email.sendTextMail("smtp.qq.com:465", "123xxx@qq.com", "pwdxxxxxx", "12545xxx@qq.com", "Token刷新失败了", "Token过期了,骚年快去手动恢复吧"); - } - } catch (e) { - if (e !== "Error") { - throw e; - } - } -} -``` +本程序支持javascript插件,更多细节请查看文档:[JavaScript插件手册](https://github.com/tickstep/aliyunpan/blob/master/docs/plugin_manual.md) ## 显示和修改程序配置项 ``` diff --git a/docs/plugin_manual.md b/docs/plugin_manual.md new file mode 100644 index 0000000..e503d5b --- /dev/null +++ b/docs/plugin_manual.md @@ -0,0 +1,486 @@ +# 目录 +- [简介](#简介) +- [如何使用](#如何使用) +- [JS中内置的函数](#JS中内置的函数) + + [console.log()](#console.log()) + + [console.println()](#console.println()) + + [PluginUtil.Http.get()](#PluginUtil.Http.get()) + + [PluginUtil.Http.post()](#PluginUtil.Http.post()) + + [PluginUtil.LocalFS.deleteFile()](#PluginUtil.LocalFS.deleteFile()) + + [PluginUtil.PanFS.deleteFile()](#PluginUtil.PanFS.deleteFile()) + + [PluginUtil.Email.sendTextMail()](#PluginUtil.Email.sendTextMail()) + + [PluginUtil.Email.sendHtmlMail()](#PluginUtil.Email.sendHtmlMail()) + + [PluginUtil.KV.putString()](#PluginUtil.KV.putString()) + + [PluginUtil.KV.getString()](#PluginUtil.KV.getString()) +- [常见场景样例](#常见场景样例) + + [1.禁止特定文件上传](#1.禁止特定文件上传) + + [2.上传文件后删除本地文件](#2.上传文件后删除本地文件) + + [3.下载文件并截断过长的文件名](#3.下载文件并截断过长的文件名) + + [4.上传文件去掉文件名包含的部分字符](#4.上传文件去掉文件名包含的部分字符) + + [5.上传文件时去掉指定目录或者文件](#5.上传文件时去掉指定目录或者文件) + + [6.下载云盘文件到本地后删除云盘对应的文件](#6.下载云盘文件到本地后删除云盘对应的文件) + + [7.Token刷新失败发送外部通知](#7.Token刷新失败发送外部通知) + +# 简介 +本程序支持javascript插件。通过JS插件,你可以按照自己的需要定制上传、下载、同步、删除过程中关键步骤的行为,最大程度满足自己的个性化需求。 +例如: +1. 排除某个特定敏感文件的上传 +2. 上传文件进行改名,但是本地文件不做更改 +3. 上传完成文件,删除本地文件 +4. 上传的文件路径进行更改,但是本地的文件保持不变 +5. 上传文件成功后,通过HTTP通知其他服务 +6. 排除某些网盘文件的下载 +7. 下载的文件进行改名,但是网盘的文件保持不变 +8. 下载的文件路径进行更改,但是网盘的文件保持不变 +9. 下载文件完成后,通过HTTP通知其他服务 +10. 同步备份功能,支持过滤本地文件,或者过滤云盘文件。定制上传或者下载需要同步的文件 + +# 如何使用 +JS插件的样本文件默认存放在程序所在的```plugin/js```文件夹下,分别为: +1. 下载插件(download_handler.js.sample) +2. 上传插件(upload_handler.js.sample) +3. 删除插件(remove_handler.js.sample) +4. 同步备份插件(sync_handler.js.sample) +5. 用户Token插件(token_handler.js.sample) + +建议拷贝一份并将后缀名更改为.js,例如:upload_handler.js,不然插件不会生效。 +你必须具备一定的JS语言基础,然后按照里面的样例根据自己所需进行改动即可。如果你不会JS那也没关系,你可以提issue需求,然后我们开发成员或者网友会给你提供JS脚本代码。 +注意:如果你有通过环境变量```ALIYUNPAN_CONFIG_DIR```设置配置目录,则需要将plugin文件夹拷贝到配置的目录中才可以生效。 + +# JS中内置的函数 +目前开放了如下函数,你可以在你的js脚本中直接调用,以用于增强JS脚本的扩展性、可玩性以及可适用性。 + +## console.log() +打印日志,这个日志需要开启debug日志才会在控制台窗口显示 +```js +console.log("hello world"); +``` +## console.println() +打印日志,这个日志会直接在控制台窗口显示,无需开启debug日志 +```js +console.println("hello world"); +``` + +## PluginUtil.Http.get() +发起HTTP的get请求 +```js + var header = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36", + "Content-Type": "application/json", + "Accept": "application/json" + }; + try { + var r = PluginUtil.Http.get(header, "https://625f528c53a42eaa07f37e13.mockapi.io/files/1"); + console.log(r); + } catch (e) { + if (e !== "Error") { + throw e; + } + } +``` + +## PluginUtil.Http.post() +发起HTTP的post请求 +```js + var header = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36", + "Content-Type": "application/json", + "Accept": "application/json" + }; + try { + var reqDataStr = JSON.stringify({ + "id": "1", + "localFilePath": "/usr/local/src/borders_burundi_producer.gram.aab", + "localFileSize": 1111, + "uploadApproved": false + }); + var r = PluginUtil.Http.post(header, "https://625f528c53a42eaa07f37e13.mockapi.io/files", reqDataStr); + console.log(r); + } catch (e) { + if (e !== "Error") { + throw e; + } + } +``` + +## PluginUtil.LocalFS.deleteFile() +删除本地指定文件,不支持文件夹 +``` +PluginUtil.LocalFS.deleteFile(localFilePath); +其中: +localFilePath - 本地文件的绝对完整路径 +``` +样例 +```js +PluginUtil.LocalFS.deleteFile("/Users/tickstep/Downloads/IMG_0884.HEIC"); +``` + +## PluginUtil.PanFS.deleteFile() +删除云盘指定文件,支持文件、文件夹 +``` +PluginUtil.PanFS.deleteFile(userId, driveId, panFileId); +其中: +userId - 登录的用户ID +driveId - 网盘ID +panFileId - 网盘文件ID +``` +样例 +```js +var userId = context["userId"] +var driveId = params["driveId"] +var driveFileId = params["driveFileId"] +if (PluginUtil.PanFS.deleteFile(userId, driveId, driveFileId)) { + console.println("插件删除云盘文件成功") +} +``` + +## PluginUtil.Email.sendTextMail() +发送文本邮件,定义如下 +``` +PluginUtil.Email.sendTextMail(mailServer, userName, password, to, subject, body) + +其中: +mailServer - smtp服务器+端口 +userName - 发件人邮箱地址 +password - 发件人邮箱密码 +to - 收件人邮箱地址 +subject - 邮件标题 +body - 邮件内容,纯文本 +``` +样例 +```js +PluginUtil.Email.sendTextMail("smtp.qq.com:465", "123xxx@qq.com", "pwdxxxxxx", "12545xxx@qq.com", "文件上传通知", "该文件已经上传完毕"); +``` + +## PluginUtil.Email.sendHtmlMail() +发送HTML富文本邮件,定义如下 +``` +PluginUtil.Email.sendHtmlMail(mailServer, userName, password, to, subject, body) + +其中: +mailServer - smtp服务器+端口 +userName - 发件人邮箱地址 +password - 发件人邮箱密码 +to - 收件人邮箱地址 +subject - 邮件标题 +body - 邮件内容,HTML富文本 +``` +样例 +```js +var html = "" + +"" + +"

文件上传通知

" + +"

该文件已经上传完毕

" + +"" + +""; +PluginUtil.Email.sendHtmlMail("smtp.qq.com:465", "123xxx@qq.com", "pwdxxxxxx", "12545xxx@qq.com", "文件上传通知", html); +``` + +## PluginUtil.KV.putString() +存储键值对,定义如下 +``` +PluginUtil.KV.putString(key, value) + +其中: +key - 键,字符串 +value - 值,字符串 +``` +样例 +```js +PluginUtil.KV.putString("mykey", "1670419352"); +``` + +## PluginUtil.KV.getString() +获取存储的键值,指定对应的键,返回存储的值,如果没有则返回空字符串,定义如下 +``` +PluginUtil.KV.getString(key) + +其中: +key - 键,字符串 +``` +样例 +```js +var value = PluginUtil.KV.getString("mykey"); +``` + +# 常见场景样例 +这里收集了一些常见的需求样例,可以作为插件定制的样例模板。 + +## 1.禁止特定文件上传 +使用JavaScript上传插件中的`uploadFilePrepareCallback`函数。如下所示: +```js +function uploadFilePrepareCallback(context, params) { + console.log(params) + + var result = { + "uploadApproved": "yes", + "driveFilePath": "" + }; + if (params["localFileType"] != "file") { + // do nothing + return result; + } + + // 禁止点号.开头的文件上传 + if (params["localFileName"].indexOf(".") == 0) { + result["uploadApproved"] = "no"; + } + + // 禁止.txt文件上传 + if (params["localFileName"].search(/.txt$/i) >= 0) { + result["uploadApproved"] = "no"; + } + + // 禁止password.key文件上传 + if (params["localFileName"] == "password.key") { + result["uploadApproved"] = "no"; + } + + return result; +} +``` + +## 2.上传文件后删除本地文件 +使用JavaScript上传插件中的`uploadFileFinishCallback`函数。如下所示: +```js +function uploadFileFinishCallback(context, params) { + console.log(params); + if (params["localFileType"] != "file") { + // do nothing + return; + } + if (params["uploadResult"] == "success") { + PluginUtil.LocalFS.deleteFile(params["localFilePath"]); + } +} +``` + +## 3.下载文件并截断过长的文件名 +有些文件的路径或者名称太长,下载的时候可能会由于路径名称过程导致无法下载,这时候可以使用JavaScript下载插件中的`downloadFilePrepareCallback`函数定制下载保存的文件名。 +如下所示: +```js +function downloadFilePrepareCallback(context, params) { + console.log(params) + + var result = { + "downloadApproved": "yes", + "localFilePath": "" + }; + if (params["driveFileType"] != "file") { + return result; + } + + // 下面的代码都是分隔路径,方便后面修改路径时使用 + var filePath = params["localFilePath"]; + filePath = filePath.replace(/\\/g, "/"); + + // 目录完整路径 + var dirPath = ""; + // 文件名,不包括后缀名 + var fileName = ""; + // 文件后缀名 + var fileExt = ""; + + var idx = filePath.lastIndexOf('/'); + if (idx > 0) { + dirPath = filePath.substring(0,idx); + fileName = filePath.substring(idx+1,filePath.length); + } else { + fileName = filePath; + } + idx = fileName.lastIndexOf(".") + if (idx > 0) { + fileExt = fileName.substring(idx,fileName.length); + fileName = fileName.substring(0, fileName.length-fileExt.length) + } + + // 开始按照需要截断太长的文件路径,例如下面的这个例子: + // + // dirPath + "/" ==> 这个的意思是保留前面的文件夹路径,如果不需要就去掉 + // fileName.substr(0,10) ==> 文件名只取前面10个字符,其他的不要了 + // + fileExt ==> 把文件的后缀名补上 + var saveFilePath = dirPath + "/" + fileName.substr(0,10) + fileExt; + + // 返回 + result["localFilePath"] = saveFilePath; + return result; +} +``` +效果如下: +1)假如网盘源路径是:`/亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi` +2)下载后保存本地的路径是:`D:\test\亚马逊书籍合集\亚马逊 kindle ebook 大合集5289册\经济金融 (2)\解密Instagra.mobi` +``` +[1] ---- +文件ID: 630f0623e67c0a1d2e554b1994a29108d089f0d5 +文件名: 解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi +文件类型: 文件 +文件路径: /亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi + +插件修改文件下载保存路径为: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi +[1] 准备下载: /亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagram( 《金融时报》和麦肯锡2020年度商业书籍!社交应用如何改变世界?解锁打造估值千亿美元爆品的核心方法! ) by 莎拉·弗莱尔(z-lib.org).mobi +[1] 将会下载到路径: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi +[1] 下载开始 + +[1] 下载完成, 保存位置: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi +[1] 检验文件有效性成功: D:\test\亚马逊书籍合集/亚马逊 kindle ebook 大合集5289册/经济金融 (2)/解密Instagra.mobi +``` +## 4.上传文件去掉文件名包含的部分字符 +例如本地文件夹名称为```[周杰伦]范特西[mp3]```,上传到网盘希望能更改成名称```[周杰伦]范特西```但保持本地文件夹名称不变,因为文件夹有很多不希望每个手动更改,可以这样实现: +```js +function uploadFilePrepareCallback(context, params) { + var result = { + "uploadApproved": "yes", + "driveFilePath": "" + }; + + // 去掉网盘保存路径中包含的[mp3]字段 + var filePath = params["driveFilePath"]; + filePath = filePath.replace(/\[mp3\]/g, ""); + result["driveFilePath"] = filePath; + + return result; +} +``` +## 5.上传文件时过滤指定目录或者文件路径 +upload命令本身支持exn参数排除上传文件或目录,但是只能指定文件名称,不能指定文件的路径。如果需要排除指定路径则可以通过插件脚本实现,样例如下: +```js +function uploadFilePrepareCallback(context, params) { + //(自行配置)禁止上传的本地【目录列表】,使用绝对路径,可以配置多个路径用逗号分隔 + var forbiddenUploadFolders = ["/Users/tickstep/Downloads/up/target","/Users/tickstep/Downloads/up/.idea"] + //(自行配置)禁止上传的本地【文件列表】,使用绝对路径,可以配置多个路径用逗号分隔 + var forbiddenUploadFiles = ["/Users/tickstep/Downloads/up/pom.xml"] + + // -------------------- 以下代码不要修改 -------------------- + var result = { + "uploadApproved": "yes", + "driveFilePath": "" + }; + + // 下面的代码都是分隔路径,方便后面使用 + var filePath = params["localFilePath"]; + filePath = filePath.replace(/\\/g, "/"); + // 目录完整路径 + var dirPath = ""; + // 文件名,不包括后缀名 + var fileName = ""; + // 文件后缀名 + var fileExt = ""; + var idx = filePath.lastIndexOf('/'); + if (idx > 0) { + dirPath = filePath.substring(0,idx); + fileName = filePath.substring(idx+1,filePath.length); + } else { + fileName = filePath; + } + idx = fileName.lastIndexOf(".") + if (idx > 0) { + fileExt = fileName.substring(idx,fileName.length); + fileName = fileName.substring(0, fileName.length-fileExt.length) + } + + if (params["localFileType"] == "file") { + for (var i = 0; i < forbiddenUploadFiles.length; i++) { + if (forbiddenUploadFiles[i].replace(/\\/g, "/") == params["localFilePath"]) { + result["uploadApproved"] = "no"; // 禁止文件上传 + break + } + } + for (var i = 0; i < forbiddenUploadFolders.length; i++) { + if (forbiddenUploadFolders[i].replace(/\\/g, "/") == dirPath) { + result["uploadApproved"] = "no"; // 禁止文件上传 + break + } + } + } else if (params["localFileType"] == "folder") { + for (var i = 0; i < forbiddenUploadFolders.length; i++) { + if (forbiddenUploadFolders[i].replace(/\\/g, "/") == params["localFilePath"]) { + result["uploadApproved"] = "no"; // 禁止文件上传 + break + } + } + } + + return result; +} +``` +## 6.下载云盘文件到本地后删除云盘对应的文件 +```js +function downloadFileFinishCallback(context, params) { + console.log(params) + // 云盘文件成功下载到本地后,删除云盘的文件 + if (params["downloadResult"] == "success") { + if (params["driveFileType"] == "file") { + // 文件下载成功,删除该云盘文件 + var userId = context["userId"] + var driveId = params["driveId"] + var driveFileId = params["driveFileId"] + if (PluginUtil.PanFS.deleteFile(userId, driveId, driveFileId)) { + console.println("插件删除云盘文件成功:" + params["driveFilePath"]) + } + } + } +} +``` + +## 7.Token刷新失败发送外部通知 +Token刷新失败发送外部通知,例如Server酱 +```js +function userTokenRefreshFinishCallback(context, params) { + var header = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36", + "Content-Type": "application/json", + "Accept": "application/json" + }; + try { + if (params["result"] === "fail") { + // 避免频繁发送 + var ONE_MINUTE = 60 * 1000; + var lastSendEmailTime = PluginUtil.KV.getString("email_last_send_time"); + if (lastSendEmailTime != "") { + if ((Date.now() - lastSendEmailTime) < (10 * ONE_MINUTE)) { + console.log("距离上次发送邮件小于10分钟,先不发送了"); + return + } + } + PluginUtil.KV.putString("email_last_send_time", Date.now()); + + var reqData = { + "text": "Token刷新失败", + "desp": params["message"] + }; + var r = PluginUtil.Http.post(header, "https://sctapi.ftqq.com/xxxxxkeyxxxxxx.send", JSON.stringify(reqData)); + } + } catch (e) { + if (e !== "Error") { + throw e; + } + } +} +``` +或者发送邮件通知 +```js +function userTokenRefreshFinishCallback(context, params) { + try { + if (params["result"] === "fail") { + // 避免频繁发送 + var ONE_MINUTE = 60 * 1000; + var lastSendEmailTime = PluginUtil.KV.getString("email_last_send_time"); + if (lastSendEmailTime != "") { + if ((Date.now() - lastSendEmailTime) < (10 * ONE_MINUTE)) { + console.log("距离上次发送邮件小于10分钟,先不发送了"); + return + } + } + PluginUtil.KV.putString("email_last_send_time", Date.now()); + + // 发送通知邮件,请确保你的发送邮箱具备smtp发送权限,没有的需要找邮箱提供商配置开启 + console.log("发送通知邮件"); + PluginUtil.Email.sendTextMail("smtp.qq.com:465", "123xxx@qq.com", "pwdxxxxxx", "12545xxx@qq.com", "Token刷新失败了", "Token过期了,骚年快去手动恢复吧"); + } + } catch (e) { + if (e !== "Error") { + throw e; + } + } +} +```