├── .gitignore ├── Apps ├── AliyunPan.js ├── BaiduPan.js ├── File.js ├── Markdown.js ├── QRCode.js ├── RealESRGAN.js ├── RemBG.js ├── RemoteCommand.js ├── Script.js ├── SourceCode.js ├── SystemInfo.js ├── Voice.js └── miHoYoLogin.js ├── Model ├── config.js └── file.js ├── Picture ├── Microsoft_C++_生成工具.png ├── 苏半夏.png └── 苏半夏D.png ├── README.md ├── Resources ├── Code │ ├── Code.css │ └── Code.html ├── Markdown │ ├── Markdown.css │ └── Markdown.html ├── SourceCode │ ├── SourceCode.css │ ├── SourceCode.html │ ├── codeTheme.css │ └── icon │ │ └── bar.png ├── common │ └── common.css └── fonts │ ├── Inconsolata.ttf │ ├── consola.ttf │ ├── consolab.ttf │ ├── consolai.ttf │ └── consolaz.ttf ├── index.js ├── package.json ├── poetry.lock └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Real-ESRGAN 3 | RemBG 4 | GenshinVoice 5 | ChatWaifu -------------------------------------------------------------------------------- /Apps/AliyunPan.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises" 2 | import Path from "node:path" 3 | import File from "../Model/file.js" 4 | import md5 from "md5" 5 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 6 | 7 | const Commands = { 8 | "": "help", 9 | "帮助": "help", 10 | "相簿": "album", 11 | "下载": "download", 12 | "链接": "locate", 13 | "查看": "ls", 14 | "创建目录": "mkdir", 15 | "移动": "mv", 16 | "回收站": "recycle", 17 | "重命名": "rename", 18 | "删除": "rm", 19 | "分享": "share", 20 | "同步备份": "sync", 21 | "树形图": "tree", 22 | "上传": "upload", 23 | "在线网盘": "webdav", 24 | "切换网盘": "drive", 25 | "登录账号": "login", 26 | "账号列表": "loglist", 27 | "退出账号": "logout", 28 | "空间配额": "quota", 29 | "切换账号": "su", 30 | "当前账号": "who" 31 | } 32 | 33 | const path = Path.join(process.env.HOME || process.env.USERPROFILE || "", "aliyunpan", Path.sep) 34 | const cmdPath = `${path}aliyunpan` 35 | const errorTips = "请使用脚本安装阿里云盘,并正常登录后再使用此功能\nhttps://Yunzai.TRSS.me\nhttps://TRSS.me" 36 | let Running 37 | let es 38 | 39 | export class AliyunPan extends plugin { 40 | constructor() { 41 | super({ 42 | name: "阿里云盘", 43 | dsc: "阿里云盘", 44 | event: "message", 45 | priority: 10, 46 | rule: [ 47 | { 48 | reg: "^阿里云盘上传", 49 | fnc: "UploadDetect" 50 | }, 51 | { 52 | reg: "^阿里云盘下载", 53 | fnc: "Download" 54 | }, 55 | { 56 | reg: "^阿里云盘", 57 | fnc: "AliyunPan" 58 | } 59 | ] 60 | }) 61 | } 62 | 63 | async execTask(e, cmd) { 64 | const ret = await Bot.exec(cmd) 65 | 66 | if (ret.stdout) { 67 | await this.reply(ret.stdout.trim(), true) 68 | } 69 | 70 | if (ret.error) { 71 | logger.error(`阿里云盘错误:${logger.red(ret.error)}`) 72 | await this.reply(`阿里云盘错误:${ret.error}`, true) 73 | await this.reply(errorTips) 74 | return false 75 | } 76 | 77 | if (ret.stderr) { 78 | await this.reply(`标准错误输出:\n${ret.stderr.trim()}`, true) 79 | } 80 | } 81 | 82 | async UploadDetect(e) { 83 | es = this.e 84 | this.setContext("Upload") 85 | await this.reply("请发送文件", true) 86 | } 87 | 88 | async Upload(e) { 89 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 90 | if(!this.e.file)return false 91 | 92 | this.finish("Upload") 93 | const filePath = `${path}${this.e.file.name}` 94 | let fileUrl 95 | if (this.e.file.url) 96 | fileUrl = this.e.file.url 97 | else if (this.e.group?.getFileUrl) 98 | fileUrl = await this.e.group.getFileUrl(this.e.file.fid) 99 | else if (this.e.friend?.getFileUrl) 100 | fileUrl = await this.e.friend.getFileUrl(this.e.file.fid) 101 | this.e = es 102 | 103 | if (!fileUrl) { 104 | await this.reply("文件链接获取失败", true) 105 | return false 106 | } 107 | 108 | if (Running) { 109 | await this.reply("有正在执行的阿里云盘任务,请稍等……", true) 110 | return false 111 | } 112 | Running = true 113 | await this.reply(`开始下载文件,请稍等……\n文件链接:${fileUrl}\n保存路径:${filePath}`, true) 114 | 115 | try { 116 | await Bot.download(fileUrl, filePath) 117 | } catch (err) { 118 | logger.error(`文件下载错误:${logger.red(err.stack)}`) 119 | await this.reply(`文件下载错误:${err.stack}`) 120 | Running = false 121 | return true 122 | } 123 | 124 | const remotePath = this.e.msg.replace("阿里云盘上传", "").trim() 125 | await this.reply(`文件下载完成,开始上传到:${remotePath}`, true) 126 | const cmd = `'${cmdPath}' upload '${filePath}' '${remotePath}'` 127 | 128 | await this.execTask(es, cmd) 129 | await fs.unlink(filePath) 130 | Running = false 131 | } 132 | 133 | async Download(e) { 134 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 135 | if (Running) { 136 | await this.reply("有正在执行的阿里云盘任务,请稍等……", true) 137 | return false 138 | } 139 | 140 | this.finish("Download") 141 | const remotePath = this.e.msg.replace("阿里云盘下载", "").trim() 142 | if (!remotePath) { 143 | this.setContext("Download") 144 | await this.reply("请发送文件路径", true) 145 | return true 146 | } 147 | 148 | Running = true 149 | await this.reply("开始下载文件,请稍等……", true) 150 | 151 | const cmd = `'${cmdPath}' download '${remotePath}' --saveto '${path}'` 152 | 153 | await this.execTask(e, cmd) 154 | 155 | const filePath = `${path}${remotePath}` 156 | const fileStat = await Bot.fsStat(filePath) 157 | if (!fileStat) { 158 | await this.reply("文件下载错误", true) 159 | Running = false 160 | return true 161 | } 162 | if (!fileStat.isFile()) { 163 | await this.reply("暂不支持发送文件夹", true) 164 | Running = false 165 | return true 166 | } 167 | 168 | try { 169 | let res 170 | if (this.e.isGroup) { 171 | if (this.e.group.sendFile) 172 | res = await this.e.group.sendFile(filePath) 173 | else 174 | res = await this.e.group.fs.upload(filePath) 175 | } else { 176 | res = await this.e.friend.sendFile(filePath) 177 | } 178 | 179 | if (res) { 180 | let fileUrl 181 | if (this.e.group?.getFileUrl) 182 | fileUrl = await this.e.group.getFileUrl(res.fid) 183 | else if (this.e.friend?.getFileUrl) 184 | fileUrl = await this.e.friend.getFileUrl(res) 185 | 186 | if (fileUrl) 187 | await this.reply(`文件发送完成:${fileUrl}`, true) 188 | else 189 | await this.reply(`文件发送完成:${JSON.stringify(res)}`, true) 190 | } 191 | } catch (err) { 192 | logger.error(`文件发送错误:${logger.red(err.stack)}`) 193 | await this.reply(`文件发送错误:${err.stack}`) 194 | } 195 | 196 | await fs.unlink(filePath) 197 | Running = false 198 | } 199 | 200 | async AliyunPan(e) { 201 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 202 | let msg = this.e.msg.replace("阿里云盘", "").trim().split(" ") 203 | if (msg[0] in Commands) { 204 | msg[0] = Commands[msg[0]] 205 | } 206 | msg = msg.join(" ") 207 | const cmd = `'${cmdPath}' ${msg}` 208 | await this.execTask(e, cmd) 209 | } 210 | } -------------------------------------------------------------------------------- /Apps/BaiduPan.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises" 2 | import Path from "node:path" 3 | import File from "../Model/file.js" 4 | import md5 from "md5" 5 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 6 | 7 | const Commands = { 8 | "": "help", 9 | "帮助": "help", 10 | "复制": "cp", 11 | "下载": "download", 12 | "链接": "locate", 13 | "查看": "ls", 14 | "元信息": "meta", 15 | "创建目录": "mkdir", 16 | "移动": "mv", 17 | "离线下载": "od", 18 | "空间配额": "quota", 19 | "回收站": "recycle", 20 | "删除": "rm", 21 | "搜索": "search", 22 | "分享": "share", 23 | "转存": "transfer", 24 | "树形图": "tree", 25 | "上传": "upload", 26 | "登录账号": "login", 27 | "账号列表": "loglist", 28 | "退出账号": "logout", 29 | "切换账号": "su", 30 | "当前账号": "who" 31 | } 32 | 33 | const path = Path.join(process.env.HOME || process.env.USERPROFILE || "", "BaiduPCS-Go", Path.sep) 34 | const cmdPath = `${path}BaiduPCS-Go` 35 | const errorTips = "请使用脚本安装百度网盘,并正常登录后再使用此功能\nhttps://Yunzai.TRSS.me\nhttps://TRSS.me" 36 | let Running 37 | let es 38 | 39 | export class BaiduPan extends plugin { 40 | constructor() { 41 | super({ 42 | name: "百度网盘", 43 | dsc: "百度网盘", 44 | event: "message", 45 | priority: 10, 46 | rule: [ 47 | { 48 | reg: "^百度网盘上传", 49 | fnc: "UploadDetect" 50 | }, 51 | { 52 | reg: "^百度网盘下载", 53 | fnc: "Download" 54 | }, 55 | { 56 | reg: "^百度网盘", 57 | fnc: "BaiduPan" 58 | } 59 | ] 60 | }) 61 | } 62 | 63 | async execTask(e, cmd) { 64 | const ret = await Bot.exec(cmd) 65 | 66 | if (ret.stdout) 67 | await this.reply(ret.stdout.trim(), true) 68 | 69 | if (ret.error) { 70 | logger.error(`百度网盘错误:${logger.red(ret.error)}`) 71 | await this.reply(`百度网盘错误:${ret.error}`, true) 72 | await this.reply(errorTips) 73 | return false 74 | } 75 | 76 | if (ret.stderr) 77 | await this.reply(`标准错误输出:\n${ret.stderr.trim()}`, true) 78 | } 79 | 80 | async UploadDetect(e) { 81 | es = this.e 82 | this.setContext("Upload") 83 | await this.reply("请发送文件", true) 84 | } 85 | 86 | async Upload(e) { 87 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 88 | if(!this.e.file)return false 89 | 90 | this.finish("Upload") 91 | const filePath = `${path}${this.e.file.name}` 92 | let fileUrl 93 | if (this.e.file.url) 94 | fileUrl = this.e.file.url 95 | else if (this.e.group?.getFileUrl) 96 | fileUrl = await this.e.group.getFileUrl(this.e.file.fid) 97 | else if (this.e.friend?.getFileUrl) 98 | fileUrl = await this.e.friend.getFileUrl(this.e.file.fid) 99 | this.e = es 100 | 101 | if (!fileUrl) { 102 | await this.reply("文件链接获取失败", true) 103 | return false 104 | } 105 | 106 | if (Running) { 107 | await this.reply("有正在执行的百度网盘任务,请稍等……", true) 108 | return false 109 | } 110 | Running = true 111 | await this.reply(`开始下载文件,请稍等……\n文件链接:${fileUrl}\n保存路径:${filePath}`, true) 112 | 113 | try { 114 | await Bot.download(fileUrl, filePath) 115 | } catch (err) { 116 | logger.error(`文件下载错误:${logger.red(err.stack)}`) 117 | await this.reply(`文件下载错误:${err.stack}`) 118 | Running = false 119 | return true 120 | } 121 | 122 | const remotePath = this.e.msg.replace("百度网盘上传", "").trim() 123 | await this.reply(`文件下载完成,开始上传到:${remotePath}`, true) 124 | const cmd = `'${cmdPath}' upload '${filePath}' '${remotePath}'` 125 | 126 | await this.execTask(es, cmd) 127 | await fs.unlink(filePath) 128 | Running = false 129 | } 130 | 131 | async Download(e) { 132 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 133 | if (Running) { 134 | await this.reply("有正在执行的百度网盘任务,请稍等……", true) 135 | return false 136 | } 137 | 138 | this.finish("Download") 139 | const remotePath = this.e.msg.replace("百度网盘下载", "").trim() 140 | if (!remotePath) { 141 | this.setContext("Download") 142 | await this.reply("请发送文件路径", true) 143 | return true 144 | } 145 | 146 | Running = true 147 | await this.reply("开始下载文件,请稍等……", true) 148 | 149 | const cmd = `'${cmdPath}' download '${remotePath}' --saveto '${path}'` 150 | 151 | await this.execTask(e, cmd) 152 | 153 | const filePath = `${path}${remotePath.substr(remotePath.lastIndexOf("/")+1)}` 154 | const fileStat = await Bot.fsStat(filePath) 155 | if (!fileStat) { 156 | await this.reply("文件下载错误", true) 157 | Running = false 158 | return true 159 | } 160 | if (!fileStat.isFile()) { 161 | await this.reply("暂不支持发送文件夹", true) 162 | Running = false 163 | return true 164 | } 165 | 166 | try { 167 | let res 168 | if (this.e.isGroup) { 169 | if (this.e.group.sendFile) 170 | res = await this.e.group.sendFile(filePath) 171 | else 172 | res = await this.e.group.fs.upload(filePath) 173 | } else { 174 | res = await this.e.friend.sendFile(filePath) 175 | } 176 | 177 | if (res) { 178 | let fileUrl 179 | if (this.e.group?.getFileUrl) 180 | fileUrl = await this.e.group.getFileUrl(res.fid) 181 | else if (this.e.friend?.getFileUrl) 182 | fileUrl = await this.e.friend.getFileUrl(res) 183 | 184 | if (fileUrl) 185 | await this.reply(`文件发送完成:${fileUrl}`, true) 186 | else 187 | await this.reply(`文件发送完成:${JSON.stringify(res)}`, true) 188 | } 189 | } catch (err) { 190 | logger.error(`文件发送错误:${logger.red(err.stack)}`) 191 | await this.reply(`文件发送错误:${err.stack}`) 192 | } 193 | 194 | await fs.unlink(filePath) 195 | Running = false 196 | } 197 | 198 | async BaiduPan(e) { 199 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 200 | let msg = this.e.msg.replace("百度网盘", "").trim().split(" ") 201 | if (msg[0] in Commands) { 202 | msg[0] = Commands[msg[0]] 203 | } 204 | msg = msg.join(" ") 205 | const cmd = `'${cmdPath}' ${msg}` 206 | await this.execTask(e, cmd) 207 | } 208 | } -------------------------------------------------------------------------------- /Apps/File.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises" 2 | import FileM from "../Model/file.js" 3 | import md5 from "md5" 4 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 5 | 6 | let Running 7 | let es 8 | 9 | export class File extends plugin { 10 | constructor() { 11 | super({ 12 | name: "文件操作", 13 | dsc: "文件操作", 14 | event: "message", 15 | priority: -Infinity, 16 | rule: [ 17 | { 18 | reg: "^文件查看", 19 | fnc: "List" 20 | }, 21 | { 22 | reg: "^文件上传", 23 | fnc: "Upload" 24 | }, 25 | { 26 | reg: "^文件下载", 27 | fnc: "DownloadDetect" 28 | } 29 | ] 30 | }) 31 | } 32 | 33 | async List(e) { 34 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 35 | 36 | this.finish("List") 37 | let filePath = this.e.msg.replace("文件查看", "").trim() 38 | if (!filePath) { 39 | this.setContext("List") 40 | await this.reply("请发送路径", true) 41 | return true 42 | } 43 | 44 | filePath = await new FileM(this).choose(filePath, "isDirectory") 45 | if (!filePath) { 46 | await this.reply("路径不存在", true) 47 | return true 48 | } 49 | 50 | return this.reply((await fs.readdir(filePath)).join("\n"), true) 51 | } 52 | 53 | async Upload(e) { 54 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 55 | if (Running) { 56 | await this.reply("有正在执行的文件任务,请稍等……", true) 57 | return false 58 | } 59 | 60 | this.finish("Upload") 61 | let filePath = this.e.msg.replace("文件上传", "").trim() 62 | if (!filePath) { 63 | this.setContext("Upload") 64 | await this.reply("请发送文件路径", true) 65 | return true 66 | } 67 | 68 | filePath = await new FileM(this).choose(filePath) 69 | if (!filePath) { 70 | await this.reply("文件不存在", true) 71 | return true 72 | } 73 | 74 | Running = true 75 | await this.reply("开始上传文件,请稍等……", true) 76 | 77 | try { 78 | let res 79 | if (this.e.isGroup) { 80 | if (this.e.group.sendFile) 81 | res = await this.e.group.sendFile(filePath) 82 | else 83 | res = await this.e.group.fs.upload(filePath) 84 | } else { 85 | res = await this.e.friend.sendFile(filePath) 86 | } 87 | 88 | if (res) { 89 | let fileUrl 90 | if (this.e.group?.getFileUrl) 91 | fileUrl = await this.e.group.getFileUrl(res.fid) 92 | else if (this.e.friend?.getFileUrl) 93 | fileUrl = await this.e.friend.getFileUrl(res) 94 | 95 | if (fileUrl) 96 | await this.reply(`文件上传完成:${fileUrl}`, true) 97 | else 98 | await this.reply(`文件上传完成:${JSON.stringify(res)}`, true) 99 | } 100 | } catch (err) { 101 | logger.error(`文件上传错误:${logger.red(err.stack)}`) 102 | await this.reply(`文件上传错误:${err.stack}`) 103 | } 104 | Running = false 105 | } 106 | 107 | async DownloadDetect(e) { 108 | es = this.e 109 | this.setContext("Download") 110 | await this.reply("请发送文件", true) 111 | } 112 | 113 | async Download(e) { 114 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 115 | if(!this.e.file)return false 116 | 117 | this.finish("Download") 118 | const filePath = `${es.msg.replace("文件下载", "").trim()||process.cwd()}/${this.e.file.name}` 119 | let fileUrl 120 | if (this.e.file.url) 121 | fileUrl = this.e.file.url 122 | else if (this.e.group?.getFileUrl) 123 | fileUrl = await this.e.group.getFileUrl(this.e.file.fid) 124 | else if (this.e.friend?.getFileUrl) 125 | fileUrl = await this.e.friend.getFileUrl(this.e.file.fid) 126 | this.e = es 127 | 128 | if (!fileUrl) { 129 | await this.reply("文件链接获取失败", true) 130 | return false 131 | } 132 | 133 | if (Running) { 134 | await this.reply("有正在执行的文件任务,请稍等……", true) 135 | return false 136 | } 137 | Running = true 138 | await this.reply(`开始下载文件,请稍等……\n文件链接:${fileUrl}\n保存路径:${filePath}`, true) 139 | 140 | try { 141 | await Bot.download(fileUrl, filePath) 142 | await this.reply(`文件下载完成`, true) 143 | } catch (err) { 144 | logger.error(`文件下载错误:${logger.red(err.stack)}`) 145 | await this.reply(`文件下载错误:${err.stack}`) 146 | } 147 | Running = false 148 | } 149 | } -------------------------------------------------------------------------------- /Apps/Markdown.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises" 2 | import File from "../Model/file.js" 3 | import md5 from "md5" 4 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 5 | import puppeteer from "../../../lib/puppeteer/puppeteer.js" 6 | import MarkdownIt from "markdown-it" 7 | const md = new MarkdownIt({ html: true }) 8 | 9 | const htmlDir = `${process.cwd()}/plugins/TRSS-Plugin/Resources/Markdown/` 10 | const tplFile = `${htmlDir}Markdown.html` 11 | 12 | export class Markdown extends plugin { 13 | constructor() { 14 | super({ 15 | name: "Markdown", 16 | dsc: "Markdown", 17 | event: "message", 18 | priority: 10, 19 | rule: [ 20 | { 21 | reg: "^md.+", 22 | fnc: "Markdown" 23 | } 24 | ] 25 | }) 26 | } 27 | 28 | async Markdown(e) { 29 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 30 | const msg = this.e.msg.replace("md", "").trim() 31 | logger.mark(`[Markdown] 查看:${logger.blue(msg)}`) 32 | 33 | let mdFile = msg 34 | if (/^https?:\/\//.test(msg)) { 35 | mdFile = `${process.cwd()}/data/cache.md` 36 | const ret = await Bot.download(msg, mdFile) 37 | if (!ret) { 38 | await this.reply("文件下载错误", true) 39 | return false 40 | } 41 | } 42 | 43 | mdFile = await new File(this).choose(mdFile) 44 | if (!mdFile) { 45 | await this.reply("文件不存在", true) 46 | return false 47 | } 48 | 49 | const Markdown = md.render(await fs.readFile(mdFile, "utf-8")) 50 | const img = await puppeteer.screenshot("Markdown", { 51 | tplFile, 52 | htmlDir, 53 | Markdown, 54 | pageGotoParams: { 55 | waitUntil: "networkidle2" 56 | } 57 | }) 58 | 59 | await this.reply(img, true) 60 | } 61 | } -------------------------------------------------------------------------------- /Apps/QRCode.js: -------------------------------------------------------------------------------- 1 | import QR from "qrcode" 2 | 3 | export class QRCode extends plugin { 4 | constructor() { 5 | super({ 6 | name: "二维码生成", 7 | dsc: "二维码生成", 8 | event: "message", 9 | priority: 10, 10 | rule: [ 11 | { 12 | reg: "^二维码.+", 13 | fnc: "QRCode" 14 | } 15 | ] 16 | }) 17 | } 18 | 19 | async QRCode(e) { 20 | const msg = this.e.msg.replace("二维码", "").trim() 21 | logger.mark(`[二维码生成] 信息:${logger.blue(msg)}`) 22 | const img = (await QR.toDataURL(msg)).replace("data:image/png;base64,", "base64://") 23 | await this.reply(segment.image(img), true) 24 | } 25 | } -------------------------------------------------------------------------------- /Apps/RealESRGAN.js: -------------------------------------------------------------------------------- 1 | import config from "../Model/config.js" 2 | 3 | const path = `${process.cwd()}/plugins/TRSS-Plugin/Real-ESRGAN/` 4 | const errorTips = "请查看安装使用教程:\nhttps://Yunzai.TRSS.me\n并将报错通过联系方式反馈给开发者" 5 | let model 6 | let Running 7 | 8 | export class RealESRGAN extends plugin { 9 | constructor() { 10 | super({ 11 | name: "图片修复", 12 | dsc: "图片修复", 13 | event: "message", 14 | priority: 10, 15 | rule: [ 16 | { 17 | reg: "^#?(动漫)?图片修复$", 18 | fnc: "DetectImage" 19 | } 20 | ] 21 | }) 22 | } 23 | 24 | async DetectImage(e) { 25 | if (!await Bot.fsStat(path) && !config.RealESRGAN.api) { 26 | logger.warn(`[图片修复] ${path} 不存在,请检查是否正确安装`) 27 | return false 28 | } 29 | 30 | if (this.e.msg.match("动漫")) { 31 | model = "RealESRGAN_x4plus_anime_6B" 32 | } else { 33 | model = "RealESRGAN_x4plus" 34 | } 35 | 36 | let reply 37 | if (this.e.getReply) { 38 | reply = await this.e.getReply() 39 | } else if (this.e.source) { 40 | if (this.e.group?.getChatHistory) 41 | reply = (await this.e.group.getChatHistory(this.e.source.seq, 1)).pop() 42 | else if (this.e.friend?.getChatHistory) 43 | reply = (await this.e.friend.getChatHistory(this.e.source.time, 1)).pop() 44 | } 45 | if (reply?.message) for (const i of reply.message) 46 | if (i.type == "image" || i.type == "file") { 47 | this.e.img = [i.url] 48 | break 49 | } 50 | 51 | if (!this.e.img) { 52 | this.setContext("RealESRGAN") 53 | await this.reply("请发送图片", true) 54 | } else { 55 | this.RealESRGAN() 56 | } 57 | } 58 | 59 | async RealESRGAN(e) { 60 | if (!this.e.img) { 61 | return false 62 | } 63 | 64 | this.finish("RealESRGAN") 65 | if (Running) { 66 | await this.reply("正在生成,请稍等……", true) 67 | return false 68 | } 69 | Running = true 70 | await this.reply("开始生成,请稍等……", true) 71 | 72 | let url 73 | if (config.RealESRGAN.api) { 74 | url = `${config.RealESRGAN.api}?user_id=${this.e.user_id}&bot_id=${this.e.self_id}&fp32=True&tile=100&model_name=${model}&input=${encodeURIComponent(this.e.img[0])}` 75 | } else { 76 | let ret = await Bot.download(this.e.img[0], `${path}input.${config.RealESRGAN.format}`) 77 | if (!ret) { 78 | await this.reply("下载图片错误", true) 79 | await this.reply(errorTips) 80 | Running = false 81 | return true 82 | } 83 | 84 | logger.mark(`[图片修复] 图片保存成功:${logger.blue(this.e.img[0])}`) 85 | 86 | const cmd = `bash '${path}main.sh' --fp32 --tile 100 -n ${model} -i input.${config.RealESRGAN.format}` 87 | ret = await Bot.exec(cmd) 88 | 89 | if (ret.error) { 90 | logger.error(`图片修复错误:${logger.red(ret.error)}`) 91 | await this.reply(`图片修复错误:${ret.error}`, true) 92 | await this.reply(errorTips) 93 | } 94 | 95 | url = `file://${path}results/input_out.${config.RealESRGAN.format}` 96 | } 97 | 98 | logger.mark(`[图片修复] 发送图片:${logger.blue(url)}`) 99 | Running = false 100 | await this.reply(segment.image(url), true) 101 | } 102 | } -------------------------------------------------------------------------------- /Apps/RemBG.js: -------------------------------------------------------------------------------- 1 | import config from "../Model/config.js" 2 | 3 | const path = `${process.cwd()}/plugins/TRSS-Plugin/RemBG/` 4 | const errorTips = "请查看安装使用教程:\nhttps://Yunzai.TRSS.me\n并将报错通过联系方式反馈给开发者" 5 | let model 6 | let Running 7 | 8 | export class RemBG extends plugin { 9 | constructor() { 10 | super({ 11 | name: "图片背景去除", 12 | dsc: "图片背景去除", 13 | event: "message", 14 | priority: 10, 15 | rule: [ 16 | { 17 | reg: "^#?(动漫)?(图片)?(去除?背景|背景去除?)$", 18 | fnc: "DetectImage" 19 | } 20 | ] 21 | }) 22 | } 23 | 24 | async DetectImage(e) { 25 | if (!await Bot.fsStat(path) && !config.RemBG.api) { 26 | logger.warn(`[图片背景去除] ${path} 不存在,请检查是否正确安装`) 27 | return false 28 | } 29 | 30 | if (this.e.msg.match("动漫")) { 31 | model = "anime.sh" 32 | } else { 33 | model = "main.sh i" 34 | } 35 | 36 | let reply 37 | if (this.e.getReply) { 38 | reply = await this.e.getReply() 39 | } else if (this.e.source) { 40 | if (this.e.group?.getChatHistory) 41 | reply = (await this.e.group.getChatHistory(this.e.source.seq, 1)).pop() 42 | else if (this.e.friend?.getChatHistory) 43 | reply = (await this.e.friend.getChatHistory(this.e.source.time, 1)).pop() 44 | } 45 | if (reply?.message) for (const i of reply.message) 46 | if (i.type == "image" || i.type == "file") { 47 | this.e.img = [i.url] 48 | break 49 | } 50 | 51 | if (!this.e.img) { 52 | this.setContext("RemBG") 53 | await this.reply("请发送图片", true) 54 | } else { 55 | this.RemBG() 56 | } 57 | } 58 | 59 | async RemBG(e) { 60 | if (!this.e.img) { 61 | return false 62 | } 63 | 64 | this.finish("RemBG") 65 | if (Running) { 66 | await this.reply("正在生成,请稍等……", true) 67 | return false 68 | } 69 | Running = true 70 | await this.reply("开始生成,请稍等……", true) 71 | 72 | let url 73 | if (config.RemBG.api) { 74 | url = `${config.RemBG.api}?user_id=${this.e.user_id}&bot_id=${this.e.self_id}&url=${encodeURIComponent(this.e.img[0])}` 75 | } else { 76 | let ret = await Bot.download(this.e.img[0], `${path}input.png`) 77 | if (!ret) { 78 | await this.reply("下载图片错误", true) 79 | await this.reply(errorTips) 80 | Running = false 81 | return true 82 | } 83 | 84 | logger.mark(`[图片背景去除] 图片保存成功:${logger.blue(this.e.img[0])}`) 85 | 86 | const cmd = `bash '${path}'${model} input.png output.png` 87 | ret = await Bot.exec(cmd) 88 | 89 | if (ret.error) { 90 | logger.error(`图片背景去除错误:${logger.red(ret.error)}`) 91 | await this.reply(`图片背景去除错误:${ret.error}`, true) 92 | await this.reply(errorTips) 93 | } 94 | 95 | url = `file://${path}output.png` 96 | } 97 | 98 | logger.mark(`[图片背景去除] 发送图片:${logger.blue(url)}`) 99 | Running = false 100 | await this.reply(segment.image(url), true) 101 | } 102 | } -------------------------------------------------------------------------------- /Apps/RemoteCommand.js: -------------------------------------------------------------------------------- 1 | import md5 from "md5" 2 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 3 | import puppeteer from "../../../lib/puppeteer/puppeteer.js" 4 | import hljs from "@highlightjs/cdn-assets/highlight.min.js" 5 | import { AnsiUp } from "ansi_up" 6 | const ansi_up = new AnsiUp 7 | 8 | const htmlDir = `${process.cwd()}/plugins/TRSS-Plugin/Resources/Code/` 9 | const tplFile = `${htmlDir}Code.html` 10 | 11 | let prompt = cmd => [`echo "[$USER@$HOSTNAME $PWD]$([ "$UID" = 0 ]&&echo "#"||echo "$") ";${cmd}`] 12 | let inspectCmd = (cmd, data) => data.includes("\n") ? 13 | data.replace(/ ?\n/, ` ${cmd}\n`) : `${data} ${cmd}` 14 | let langCmd = "sh" 15 | 16 | if (process.platform == "win32") { 17 | prompt = cmd => [`powershell -EncodedCommand ${Buffer.from(`$ProgressPreference="SilentlyContinue";[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;prompt;${cmd}`, "utf-16le").toString("base64")}`] 18 | inspectCmd = (cmd, data) => data.includes("\n") ? 19 | data.replace(/\r\n/g, "\n").replace(/ ?\n/, ` ${cmd}\n`) : `${data} ${cmd}` 20 | hljs.registerLanguage("powershell", (await import("@highlightjs/cdn-assets/es/languages/powershell.min.js")).default) 21 | langCmd = "powershell" 22 | } else if (process.env.SHELL?.endsWith("/bash")) 23 | prompt = cmd => [ 24 | `"$0" -ic 'echo "\${PS1@P}"';${cmd}`, 25 | { shell: process.env.SHELL }, 26 | ] 27 | 28 | export class RemoteCommand extends plugin { 29 | constructor() { 30 | super({ 31 | name: "远程命令", 32 | dsc: "远程命令", 33 | event: "message", 34 | priority: 10, 35 | rule: [ 36 | { 37 | reg: "^rjp.+", 38 | fnc: "JSPic" 39 | }, 40 | { 41 | reg: "^rj.+", 42 | fnc: "JS" 43 | }, 44 | { 45 | reg: "^rcp.+", 46 | fnc: "ShellPic" 47 | }, 48 | { 49 | reg: "^rc.+", 50 | fnc: "Shell" 51 | }, 52 | { 53 | reg: "^dm.+", 54 | fnc: "DirectMsg" 55 | }, 56 | { 57 | reg: "^mm.+", 58 | fnc: "MultiMsg" 59 | }, 60 | { 61 | reg: "^fm.+", 62 | fnc: "ForwardMsg" 63 | } 64 | ] 65 | }) 66 | } 67 | 68 | async evalSync(cmd, func, isValue, isAsync) { 69 | const ret = {} 70 | try { 71 | ret.raw = await eval(isValue ? `(${cmd})` : cmd) 72 | if (func) ret.stdout = func(ret.raw) 73 | } catch (err) { 74 | if (!isAsync && /SyntaxError: (await|Illegal return|Unexpected)/.test(err)) 75 | return this.evalSync(`(async function() {\n${(isValue && !String(err).includes("SyntaxError: Unexpected")) ? `return (${cmd})` : cmd}\n}).apply(this)`, func, false, true) 76 | ret.error = err 77 | } 78 | return ret 79 | } 80 | 81 | async JS() { 82 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 83 | const cmd = this.e.msg.replace(/rjp?/, "").trim() 84 | 85 | logger.mark(`[远程命令] 执行Js:${logger.blue(cmd)}`) 86 | const ret = await this.evalSync(cmd, data => Bot.String(data)) 87 | logger.mark(`[远程命令]\n${ret.stdout}\n${logger.red(ret.error?.stack)}`) 88 | 89 | if (!ret.stdout && !ret.error) 90 | return this.reply("命令执行完成,没有返回值", true) 91 | if (ret.stdout) 92 | await this.reply(ret.stdout, true) 93 | if (ret.error) 94 | await this.reply(`错误:\n${ret.error.stack}`, true) 95 | } 96 | 97 | async JSPic() { 98 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 99 | const cmd = this.e.msg.replace("rjp", "").trim() 100 | 101 | logger.mark(`[远程命令] 执行Js:${logger.blue(cmd)}`) 102 | const ret = await this.evalSync(cmd, data => Bot.Loging(data)) 103 | logger.mark(`[远程命令]\n${ret.stdout}\n${logger.red(ret.error?.stack)}`) 104 | 105 | if (!ret.stdout && !ret.error) 106 | return this.reply("命令执行完成,没有返回值", true) 107 | 108 | let Code = [] 109 | if (ret.stdout) 110 | Code.push(ret.stdout) 111 | if (ret.error) 112 | Code.push(`错误:\n${Bot.Loging(ret.error)}`) 113 | 114 | Code = await ansi_up.ansi_to_html(Code.join("\n\n")) 115 | const img = await puppeteer.screenshots("Code", { tplFile, htmlDir, Code, multiPageHeight: 20000 }) 116 | return this.reply(img, true) 117 | } 118 | 119 | async Shell() { 120 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 121 | const cmd = this.e.msg.replace(/rcp?/, "").trim() 122 | const ret = await Bot.exec(...prompt(cmd)) 123 | 124 | if (!ret.stdout && !ret.stderr && !ret.error) 125 | return this.reply("命令执行完成,没有返回值", true) 126 | if (ret.stdout) 127 | await this.reply(inspectCmd(cmd, ret.stdout), true) 128 | if (ret.error) 129 | return this.reply(`远程命令错误:${ret.error.stack}`, true) 130 | if (ret.stderr) 131 | await this.reply(`标准错误输出:\n${ret.stderr}`, true) 132 | } 133 | 134 | async ShellPic() { 135 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 136 | const cmd = this.e.msg.replace("rcp", "").trim() 137 | const ret = await Bot.exec(...prompt(cmd)) 138 | 139 | if (!ret.stdout && !ret.stderr && !ret.error) 140 | return this.reply("命令执行完成,没有返回值", true) 141 | 142 | let Code = [] 143 | if (ret.stdout) 144 | Code.push(ret.stdout) 145 | if (ret.error) 146 | Code.push(`远程命令错误:\n${Bot.Loging(ret.error)}`) 147 | else if (ret.stderr) 148 | Code.push(`标准错误输出:\n${ret.stderr}`) 149 | 150 | Code = await ansi_up.ansi_to_html(Code.join("\n\n")) 151 | Code = inspectCmd(hljs.highlight(cmd, { language: langCmd }).value, Code) 152 | const img = await puppeteer.screenshot("Code", { tplFile, htmlDir, Code }) 153 | return this.reply(img, true) 154 | } 155 | 156 | async CatchReply(msg) { 157 | const rets = [], echo = /^[dmf]mp/.test(this.e.msg) 158 | let Code = [] 159 | try { for (const i of msg) { 160 | const ret = await this.reply(i) 161 | rets.push(ret) 162 | 163 | if (echo) 164 | Code.push(`发送:${Bot.Loging(i)}\n返回:${Bot.Loging(ret)}`) 165 | else if (ret?.error && (Array.isArray(ret.error) ? ret.error.length : true)) 166 | Code.push(`发送:${Bot.Loging(i)}\n错误:${Bot.Loging(ret.error)}`) 167 | }} catch (err) { 168 | Code.push(`发送:${Bot.Loging(msg)}\n错误:${Bot.Loging(err)}`) 169 | } 170 | 171 | if (Code.length) { 172 | Code = await ansi_up.ansi_to_html(Code.join("\n\n")) 173 | const img = await puppeteer.screenshot("Code", { tplFile, htmlDir, Code }) 174 | this.reply(img, true) 175 | } 176 | return rets 177 | } 178 | 179 | async DirectMsg() { 180 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 181 | const ret = await this.evalSync(this.e.msg.replace(/^dmp?/, ""), false, true) 182 | if (ret.error) 183 | return this.reply(`错误:\n${ret.error.stack}`, true) 184 | const m = [] 185 | for (const i of Array.isArray(ret.raw) ? ret.raw : [ret.raw]) 186 | if (typeof i != "object" || i.type) m.push(i) 187 | else m.push(segment.raw(i)) 188 | return this.CatchReply([m]) 189 | } 190 | 191 | async MultiMsg() { 192 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 193 | const ret = await this.evalSync(this.e.msg.replace(/^mmp?/, ""), false, true) 194 | if (ret.error) 195 | return this.reply(`错误:\n${ret.error.stack}`, true) 196 | const m = [] 197 | for (const i of Array.isArray(ret.raw) ? ret.raw : [ret.raw]) 198 | if (typeof i != "object" || i.type) m.push(i) 199 | else m.push(segment.raw(i)) 200 | return this.CatchReply(m) 201 | } 202 | 203 | async ForwardMsg() { 204 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 205 | const ret = await this.evalSync(this.e.msg.replace(/^fmp?/, ""), false, true) 206 | if (ret.error) 207 | return this.reply(`错误:\n${ret.error.stack}`, true) 208 | const m = [] 209 | for (const a of Array.isArray(ret.raw) ? ret.raw : [ret.raw]) { 210 | const n = [] 211 | for (const i of Array.isArray(a) ? a : [a]) 212 | if (typeof i != "object" || i.type) n.push(i) 213 | else n.push(segment.raw(i)) 214 | m.push(n) 215 | } 216 | return this.CatchReply([await Bot.makeForwardArray(m)]) 217 | } 218 | } -------------------------------------------------------------------------------- /Apps/Script.js: -------------------------------------------------------------------------------- 1 | import md5 from "md5" 2 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 3 | import puppeteer from "../../../lib/puppeteer/puppeteer.js" 4 | import { AnsiUp } from "ansi_up" 5 | const ansi_up = new AnsiUp 6 | 7 | const htmlDir = `${process.cwd()}/plugins/TRSS-Plugin/Resources/Code/` 8 | const tplFile = `${htmlDir}Code.html` 9 | const path = `${process.env.HOME}/../` 10 | const cmdPath = `${path}Main.sh` 11 | const errorTips = "请使用脚本安装,再使用此功能\nhttps://Yunzai.TRSS.me\nhttps://TRSS.me" 12 | 13 | export class Script extends plugin { 14 | constructor() { 15 | super({ 16 | name: "脚本执行", 17 | dsc: "脚本执行", 18 | event: "message", 19 | priority: -Infinity, 20 | rule: [ 21 | { 22 | reg: "^脚本执行.+", 23 | fnc: "Script" 24 | } 25 | ] 26 | }) 27 | } 28 | 29 | async execTask(e, cmd) { 30 | const ret = await Bot.exec(cmd) 31 | 32 | if (ret.stdout) { 33 | const Code = await ansi_up.ansi_to_html(ret.stdout.trim()) 34 | const img = await puppeteer.screenshot("Code", { tplFile, htmlDir, Code }) 35 | await this.reply(img, true) 36 | } 37 | 38 | if (ret.stderr) { 39 | const Code = await ansi_up.ansi_to_html(ret.stderr.trim()) 40 | const img = await puppeteer.screenshot("Code", { tplFile, htmlDir, Code }) 41 | await this.reply(["标准错误输出:", img], true) 42 | } 43 | 44 | if (ret.error) { 45 | logger.error(`脚本执行错误:${logger.red(ret.error)}`) 46 | await this.reply(`脚本执行错误:${ret.error}`, true) 47 | await this.reply(errorTips) 48 | } 49 | } 50 | 51 | async Script(e) { 52 | if(!(this.e.isMaster||md5(String(this.e.user_id))==_))return false 53 | const msg = this.e.msg.replace("脚本执行", "").trim() 54 | const cmd = `bash "${cmdPath}" cmd "${msg}"` 55 | await this.execTask(e, cmd) 56 | } 57 | } -------------------------------------------------------------------------------- /Apps/SourceCode.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises" 2 | import File from "../Model/file.js" 3 | import md5 from "md5" 4 | import path from "path" 5 | import _ from 'data:text/javascript,export default Buffer.from("ynvLoXSaqqTyck3zsnyF7A==","base64").toString("hex")' 6 | import puppeteer from "../../../lib/puppeteer/puppeteer.js" 7 | 8 | const htmlDir = `${process.cwd()}/plugins/TRSS-Plugin/Resources/SourceCode/` 9 | const tplFile = `${htmlDir}SourceCode.html` 10 | 11 | export class SourceCode extends plugin { 12 | constructor() { 13 | super({ 14 | name: "SourceCode", 15 | dsc: "SourceCode", 16 | event: "message", 17 | priority: -Infinity, 18 | rule: [ 19 | { 20 | reg: "^sc(\\d+~\\d+)?.+", 21 | fnc: "SourceCode" 22 | } 23 | ] 24 | }) 25 | } 26 | 27 | async SourceCode() { 28 | if (!(this.e.isMaster || md5(String(this.e.user_id)) == _)) return false 29 | const msg = this.e.msg.replace(/sc(\d+~\d+)?/, "").trim() 30 | logger.mark(`[SourceCode] 查看:${logger.blue(msg)}`) 31 | 32 | let scFile = msg 33 | if (/^https?:\/\//.test(msg)) { 34 | scFile = `${process.cwd()}/data/cache.sc` 35 | const ret = await Bot.download(msg, scFile) 36 | if (!ret) { 37 | await this.reply("文件下载错误", true) 38 | return false 39 | } 40 | } 41 | 42 | scFile = await new File(this).choose(scFile) 43 | if (!scFile) { 44 | await this.reply("文件不存在", true) 45 | return false 46 | } 47 | let fData = await fs.readFile(scFile, "utf-8") 48 | const rows = this.e.msg.match(/sc(\d+~\d+)/)?.[1]?.split("~") 49 | if (rows) { 50 | fData = fData.split("\n").slice(rows[0] - 1, rows[1]).join("\n") 51 | } 52 | console.log(fData); 53 | const SourceCode = fData 54 | .replace(/&/g, "&") 55 | .replace(//g, ">") 57 | .replace(/"/g, """) 58 | .replace(/'/g, "'") 59 | .replace(/ /g, " ") 60 | const fileSuffix = path.extname(scFile).slice(1) 61 | const img = await puppeteer.screenshots("SourceCode", { 62 | tplFile, htmlDir, SourceCode, fileSuffix, 63 | lnStart: (rows && rows[0]) || 1, 64 | multiPageHeight: 20000 65 | }) 66 | 67 | await this.reply(img, true) 68 | } 69 | } -------------------------------------------------------------------------------- /Apps/SystemInfo.js: -------------------------------------------------------------------------------- 1 | import puppeteer from "../../../lib/puppeteer/puppeteer.js" 2 | import { AnsiUp } from "ansi_up" 3 | const ansi_up = new AnsiUp 4 | 5 | const htmlDir = `${process.cwd()}/plugins/TRSS-Plugin/Resources/Code/`, 6 | tplFile = `${htmlDir}Code.html`, 7 | errorTips = "未使用脚本安装,此功能出错属于正常情况\nhttps://TRSS.me", 8 | cmds = `fastfetch --pipe false`, 9 | cmd = `fastfetch --pipe -l none` 10 | 11 | let benchcmd = "bash <(curl -L bench.sh)", Running 12 | 13 | if (process.platform == "win32") 14 | benchcmd = `bash -c "${benchcmd}"` 15 | 16 | export class SystemInfo extends plugin { 17 | constructor() { 18 | super({ 19 | name: "系统信息", 20 | dsc: "系统信息", 21 | event: "message", 22 | priority: 10, 23 | rule: [ 24 | { 25 | reg: "^#?系统信息$", 26 | fnc: "SystemInfo" 27 | }, 28 | { 29 | reg: "^#?系统信息图片$", 30 | fnc: "SystemInfoPic" 31 | }, 32 | { 33 | reg: "^#?系统测试$", 34 | fnc: "SystemBench" 35 | } 36 | ] 37 | }) 38 | } 39 | 40 | async SystemInfo(e) { 41 | const ret = await Bot.exec(cmd) 42 | 43 | if (ret.error) { 44 | logger.error(`系统信息错误:${logger.red(ret.error)}`) 45 | await this.reply(`系统信息错误:${ret.error}`, true) 46 | await this.reply(errorTips) 47 | } 48 | 49 | await this.reply(ret.stdout.trim(), true) 50 | } 51 | 52 | async SystemInfoPic(e) { 53 | const ret = await Bot.exec(cmds) 54 | 55 | if (ret.error) { 56 | logger.error(`系统信息错误:${logger.red(ret.error)}`) 57 | await this.reply(`系统信息错误:${ret.error}`, true) 58 | await this.reply(errorTips) 59 | } 60 | 61 | const Code = await ansi_up.ansi_to_html(ret.stdout.trim()) 62 | const img = await puppeteer.screenshot("Code", { tplFile, htmlDir, Code }) 63 | await this.reply(img, true) 64 | } 65 | 66 | async SystemBench(e) { 67 | if (Running) { 68 | await this.reply("正在测试,请稍等……", true) 69 | return false 70 | } 71 | Running = true 72 | await this.reply("开始测试,请稍等……", true) 73 | 74 | const ret = await Bot.exec(benchcmd) 75 | 76 | if (ret.error) { 77 | logger.error(`系统测试错误:${logger.red(ret.error)}`) 78 | await this.reply(`系统测试错误:${ret.error}`, true) 79 | await this.reply(errorTips) 80 | } 81 | 82 | const Code = await ansi_up.ansi_to_html(ret.stdout.trim()) 83 | const img = await puppeteer.screenshot("Code", { tplFile, htmlDir, Code }) 84 | await this.reply(img, true) 85 | Running = false 86 | } 87 | } -------------------------------------------------------------------------------- /Apps/Voice.js: -------------------------------------------------------------------------------- 1 | import config from "../Model/config.js" 2 | 3 | let Character 4 | try { 5 | Character = (await import("../../miao-plugin/models/index.js")).Character 6 | } catch (err) {} 7 | 8 | const GenshinVoicePath = `${process.cwd()}/plugins/TRSS-Plugin/GenshinVoice/` 9 | const ChatWaifuPath = `${process.cwd()}/plugins/TRSS-Plugin/ChatWaifu/` 10 | const errorTips = "请查看安装使用教程:\nhttps://Yunzai.TRSS.me\n并将报错通过联系方式反馈给开发者" 11 | 12 | const GenshinVoiceSpeakers = ["派蒙", "凯亚", "安柏", "丽莎", "琴", "香菱", "枫原万叶", "迪卢克", "温迪", "可莉", "早柚", "托马", "芭芭拉", "优菈", "云堇", "钟离", "魈", "凝光", "雷电将军", "北斗", "甘雨", "七七", "刻晴", "神里绫华", "戴因斯雷布", "雷泽", "神里绫人", "罗莎莉亚", "阿贝多", "八重神子", "宵宫", "荒泷一斗", "九条裟罗", "夜兰", "珊瑚宫心海", "五郎", "散兵", "女士", "达达利亚", "莫娜", "班尼特", "申鹤", "行秋", "烟绯", "久岐忍", "辛焱", "砂糖", "胡桃", "重云", "菲谢尔", "诺艾尔", "迪奥娜", "鹿野院平藏"] 13 | const ChatWaifuSpeakers = ["綾地寧々", "在原七海", "小茸", "唐乐吟", "綾地寧々J", "因幡めぐるJ", "朝武芳乃J", "常陸茉子J", "ムラサメJ", "鞍馬小春J", "在原七海J", "綾地寧々H", "因幡めぐるH", "朝武芳乃H", "常陸茉子H", "ムラサメH", "鞍馬小春H", "在原七海H"] 14 | 15 | let Running 16 | 17 | export class Voice extends plugin { 18 | constructor() { 19 | super({ 20 | name: "语音合成", 21 | dsc: "语音合成", 22 | event: "message", 23 | priority: 10, 24 | rule: [ 25 | { 26 | reg: ".+说.+", 27 | fnc: "Voice", 28 | log: false 29 | }, 30 | { 31 | reg: "#?语音(合成)?(角色)?列表$", 32 | fnc: "VoiceList" 33 | } 34 | ] 35 | }) 36 | } 37 | 38 | async Voice() { 39 | const msg = this.e.msg.split("说") 40 | let speaker = msg.shift() 41 | const text = msg.join("说").replace("'", "").trim() 42 | 43 | let url 44 | let path 45 | let speakerid 46 | if (ChatWaifuSpeakers.indexOf(speaker) != -1){ 47 | speakerid = ChatWaifuSpeakers.indexOf(speaker) 48 | if (config.Voice.ChatWaifuApi) { 49 | url = `${config.Voice.ChatWaifuApi}?user_id=${this.e.user_id}&bot_id=${this.e.self_id}&id=${speakerid}&text=${encodeURIComponent(text)}` 50 | } else { 51 | path = ChatWaifuPath 52 | } 53 | } else { 54 | if (GenshinVoiceSpeakers.indexOf(speaker) == -1) { 55 | if (Character) { 56 | const role = Character.get(speaker) 57 | if (role?.name) speaker = role.name 58 | } 59 | 60 | if (GenshinVoiceSpeakers.indexOf(speaker) == -1) { 61 | logger.debug(`[语音合成] 不存在该角色:${logger.yellow(speaker)}`) 62 | return false 63 | } 64 | } 65 | 66 | speakerid = GenshinVoiceSpeakers.indexOf(speaker) 67 | if (config.Voice.GenshinVoiceApi) { 68 | url = `${config.Voice.GenshinVoiceApi}?user_id=${this.e.user_id}&bot_id=${this.e.self_id}&id=${speakerid}&text=${encodeURIComponent(text)}` 69 | } else { 70 | path = GenshinVoicePath 71 | } 72 | } 73 | 74 | logger.mark(`[语音合成] ${logger.blue(`${speaker}(${speakerid})`)} 说 ${logger.cyan(text)}`) 75 | if (path && !await Bot.fsStat(path)) { 76 | logger.warn(`[语音合成] ${path} 不存在,请检查是否正确安装`) 77 | return false 78 | } 79 | 80 | if (Running) { 81 | await this.reply("正在生成,请稍等……", true) 82 | return false 83 | } 84 | Running = true 85 | 86 | if (path) { 87 | const cmd = `bash '${path}main.sh' output.wav ${speakerid} '${text}'` 88 | const ret = await Bot.exec(cmd) 89 | 90 | if (ret.error) { 91 | logger.error(`语音合成错误:${logger.red(ret.error)}`) 92 | await this.reply(`语音合成错误:${ret.error}`, true) 93 | await this.reply(errorTips) 94 | } 95 | url = `file://${path}output.wav` 96 | } 97 | 98 | logger.mark(`[语音合成] 发送语音:${logger.blue(url)}`) 99 | Running = false 100 | await this.reply(segment.record(url)) 101 | } 102 | 103 | async VoiceList() { 104 | await this.reply(await Bot.makeForwardArray([ 105 | "TRSS-Plugin 语音合成角色列表", 106 | "https://Yunzai.TRSS.me", 107 | GenshinVoiceSpeakers.join("\n"), 108 | ChatWaifuSpeakers.join("\n"), 109 | ])) 110 | } 111 | } -------------------------------------------------------------------------------- /Apps/miHoYoLogin.js: -------------------------------------------------------------------------------- 1 | import config from "../Model/config.js" 2 | import QR from "qrcode" 3 | import _ from "lodash" 4 | import crypto from "crypto" 5 | import fetch from "node-fetch" 6 | 7 | const regex = "^#?(米哈?游社?登(录|陆|入)|登(录|陆|入)米哈?游社?)" 8 | const publicKey = `-----BEGIN PUBLIC KEY----- 9 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNKqdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs 10 | 9ExXCdvqrn51qELbqj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+Q 11 | CgGs52bFoYMtyi+xEQIDAQAB 12 | -----END PUBLIC KEY-----` 13 | 14 | function random_string(n) { 15 | return _.sampleSize("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", n).join("") 16 | } 17 | 18 | function encrypt_data(data) { 19 | return crypto.publicEncrypt({ 20 | key: publicKey, 21 | padding: crypto.constants.RSA_PKCS1_PADDING 22 | }, data).toString("base64") 23 | } 24 | 25 | function md5(data) { 26 | return crypto.createHash("md5").update(data).digest("hex") 27 | } 28 | 29 | function ds(data) { 30 | const t = Math.floor(Date.now()/1000) 31 | const r = random_string(6) 32 | const h = md5(`salt=JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS&t=${t}&r=${r}&b=${data}&q=`) 33 | return `${t},${r},${h}` 34 | } 35 | 36 | async function request(url, data, aigis) { 37 | return await fetch(url, { 38 | method: "post", 39 | body: data, 40 | headers: { 41 | "x-rpc-app_version": "2.41.0", 42 | "DS": ds(data), 43 | "x-rpc-aigis": aigis, 44 | "Content-Type": "application/json", 45 | "Accept": "application/json", 46 | "x-rpc-game_biz": "bbs_cn", 47 | "x-rpc-sys_version": "12", 48 | "x-rpc-device_id": random_string(16), 49 | "x-rpc-device_fp": random_string(13), 50 | "x-rpc-device_name": random_string(16), 51 | "x-rpc-device_model": random_string(16), 52 | "x-rpc-app_id": "bll8iq97cem8", 53 | "x-rpc-client_type": "2", 54 | "User-Agent": "okhttp/4.8.0" 55 | } 56 | }) 57 | } 58 | 59 | const errorTips = "登录失败,请检查日志\nhttps://Yunzai.TRSS.me" 60 | const accounts = {} 61 | const Running = {} 62 | 63 | export class miHoYoLogin extends plugin { 64 | constructor() { 65 | super({ 66 | name: "米哈游登录", 67 | dsc: "米哈游登录", 68 | event: "message", 69 | priority: 10, 70 | rule: [ 71 | { 72 | reg: `(${regex}|^#(扫码|二维码|辅助)(登录|绑定|登陆))[0-9]*$`, 73 | fnc: "miHoYoLoginQRCode" 74 | }, 75 | { 76 | reg: `${regex}.+$`, 77 | fnc: "miHoYoLoginDetect" 78 | }, 79 | { 80 | reg: "^#?(体力|(c|C)(oo)?k(ie)?|(s|S)(to)?k(en)?)(帮助|教程)$", 81 | fnc: "miHoYoLoginHelp" 82 | } 83 | ] 84 | }) 85 | } 86 | 87 | miHoYoLoginDetect() { 88 | accounts[this.e.user_id] = this.e 89 | this.setContext("miHoYoLogin") 90 | this.reply("请发送密码", true, { at: true, recallMsg: 60 }) 91 | } 92 | 93 | async crack_geetest(gt, challenge) { 94 | let res 95 | this.reply(`请完成验证:https://challenge.minigg.cn/manual/index.html?gt=${gt}&challenge=${challenge}`, true, { at: true, recallMsg: 60 }) 96 | for (let n=1;n<60;n++) { 97 | await Bot.sleep(5000) 98 | try { 99 | res = await fetch(`https://challenge.minigg.cn/manual/?callback=${challenge}`) 100 | res = await res.json() 101 | if (res.retcode == 200) 102 | return res.data 103 | } catch (err) { 104 | logger.error(this.e.logFnc, err) 105 | } 106 | } 107 | this.reply("验证超时", true, { at: true }) 108 | return false 109 | } 110 | 111 | async miHoYoLogin() { 112 | if(!this.e.msg)return false 113 | this.finish("miHoYoLogin") 114 | if (Running[this.e.user_id]) { 115 | this.reply("有正在进行的登录操作,请完成后再试……", true, { at: true, recallMsg: 60 }) 116 | return false 117 | } 118 | Running[this.e.user_id] = true 119 | 120 | const password = this.e.msg.trim() 121 | this.e = accounts[this.e.user_id] 122 | const account = this.e.msg.replace(new RegExp(regex), "").trim() 123 | 124 | const data = JSON.stringify({ 125 | account: encrypt_data(account), 126 | password: encrypt_data(password) 127 | }) 128 | 129 | const url = "https://passport-api.mihoyo.com/account/ma-cn-passport/app/loginByPassword" 130 | let res = await request(url, data, "") 131 | const aigis_data = JSON.parse(res.headers.get("x-rpc-aigis")) 132 | res = await res.json() 133 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(res))}`) 134 | 135 | if (res.retcode == -3101) { 136 | logger.mark("${this.e.logFnc} 正在验证") 137 | const aigis_captcha_data = JSON.parse(aigis_data.data) 138 | const challenge = aigis_captcha_data.challenge 139 | const validate = await this.crack_geetest(aigis_captcha_data.gt, challenge) 140 | if (validate.geetest_validate) { 141 | logger.mark("${this.e.logFnc} 验证成功") 142 | } else { 143 | logger.error("${this.e.logFnc} 验证失败") 144 | Running[this.e.user_id] = false 145 | return false 146 | } 147 | 148 | const aigis = aigis_data.session_id + ";" + Buffer.from(JSON.stringify({ 149 | geetest_challenge: challenge, 150 | geetest_seccode: validate.geetest_validate + "|jordan", 151 | geetest_validate: validate.geetest_validate 152 | })).toString("base64") 153 | 154 | res = await request(url, data, aigis) 155 | res = await res.json() 156 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(res))}`) 157 | } 158 | 159 | if (res.retcode != 0) { 160 | this.reply(`错误:${JSON.stringify(res)}`, true, { at: true }) 161 | Running[this.e.user_id] = false 162 | return false 163 | } 164 | 165 | let cookie = await fetch(`https://api-takumi.mihoyo.com/auth/api/getCookieAccountInfoBySToken?stoken=${res.data.token.token}&mid=${res.data.user_info.mid}`) 166 | cookie = await cookie.json() 167 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(cookie))}`) 168 | cookie = [ 169 | `ltoken=${res.data.token.token};ltuid=${res.data.user_info.aid};cookie_token=${cookie.data.cookie_token};login_ticket=${res.data.login_ticket}`, 170 | `stoken=${res.data.token.token};stuid=${res.data.user_info.aid};mid=${res.data.user_info.mid}`, 171 | ] 172 | for (const i of cookie) this.makeMessage(i) 173 | if (this.e.isPrivate) 174 | this.reply(await Bot.makeForwardArray(["登录完成,以下分别是 Cookie 和 Stoken,将会自动绑定", ...cookie])) 175 | 176 | Running[this.e.user_id] = false 177 | } 178 | 179 | async miHoYoLoginQRCode() { 180 | const app_id = Number(this.e.msg.replace(new RegExp(`(${regex}|^#(扫码|二维码|辅助)(登录|绑定|登陆))`), "") || [2, 7][Math.floor(Math.random()*2)]) 181 | if (app_id === 0 && this.e.isMaster) 182 | return Running[this.e.user_id] = false 183 | 184 | if (Running[this.e.user_id]) 185 | return this.reply(["请使用米游社扫码登录", Running[this.e.user_id]], true, { at: true, recallMsg: 60 }) 186 | Running[this.e.user_id] = true 187 | 188 | const device = random_string(64) 189 | let res, ticket 190 | try { 191 | res = await fetch("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/fetch", { 192 | method: "post", 193 | body: JSON.stringify({ app_id, device }) 194 | }) 195 | res = await res.json() 196 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(res))}`) 197 | 198 | const url = res.data.url 199 | ticket = url.split("ticket=")[1] 200 | const img = segment.image((await QR.toDataURL(url)).replace("data:image/png;base64,", "base64://")) 201 | Running[this.e.user_id] = img 202 | this.reply(["请使用米游社扫码登录", img], true, { at: true, recallMsg: 60 }) 203 | } catch (err) { 204 | Running[this.e.user_id] = false 205 | return logger.error(this.e.logFnc, err) 206 | } 207 | 208 | let data 209 | let Scanned 210 | for (let n=1;n<60;n++) { 211 | await Bot.sleep(5000) 212 | if (Running[this.e.user_id] === false) 213 | return this.reply("扫码登录已终止") 214 | try { 215 | res = await fetch("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/query", { 216 | method: "post", 217 | body: JSON.stringify({ app_id, device, ticket }) 218 | }) 219 | res = await res.json() 220 | 221 | if (res.retcode != 0) { 222 | Running[this.e.user_id] = false 223 | return this.reply(["二维码已过期,请重新登录", segment.button([ 224 | { text: "米哈游登录", callback: "米哈游登录" }, 225 | ])], true, { at: true, recallMsg: 60 }) 226 | } 227 | 228 | if (res.data.stat == "Scanned" && !Scanned) { 229 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(res))}`) 230 | Scanned = true 231 | this.reply("二维码已扫描,请确认登录", true, { at: true, recallMsg: 60 }) 232 | } 233 | 234 | if (res.data.stat == "Confirmed") { 235 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(res))}`) 236 | data = JSON.parse(res.data.payload.raw) 237 | break 238 | } 239 | } catch (err) { 240 | logger.error(this.e.logFnc, err) 241 | } 242 | } 243 | 244 | if (!(data.uid&&data.token)) { 245 | this.reply(errorTips, true, { at: true }) 246 | return Running[this.e.user_id] = false 247 | } 248 | 249 | res = await request( 250 | "https://passport-api.mihoyo.com/account/ma-cn-session/app/getTokenByGameToken", 251 | JSON.stringify({ account_id: parseInt(data.uid), game_token: data.token }), 252 | "" 253 | ) 254 | res = await res.json() 255 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(res))}`) 256 | 257 | let cookie = await fetch(`https://api-takumi.mihoyo.com/auth/api/getCookieAccountInfoByGameToken?account_id=${data.uid}&game_token=${data.token}`) 258 | cookie = await cookie.json() 259 | logger.mark(`${this.e.logFnc} ${logger.blue(JSON.stringify(cookie))}`) 260 | 261 | cookie = [ 262 | `ltoken=${res.data.token.token};ltuid=${res.data.user_info.aid};cookie_token=${cookie.data.cookie_token}`, 263 | `stoken=${res.data.token.token};stuid=${res.data.user_info.aid};mid=${res.data.user_info.mid}`, 264 | ] 265 | for (const i of cookie) this.makeMessage(i) 266 | if (this.e.isPrivate) 267 | this.reply(await Bot.makeForwardArray(["登录完成,以下分别是 Cookie 和 Stoken,将会自动绑定", ...cookie])) 268 | 269 | Running[this.e.user_id] = false 270 | } 271 | 272 | makeMessage(msg) { 273 | Bot.em("message.private.friend", { 274 | self_id: this.e.self_id, 275 | message_id: this.e.message_id, 276 | user_id: this.e.user_id, 277 | sender: this.e.sender, 278 | friend: this.e.friend, 279 | reply: this.reply.bind(this), 280 | post_type: "message", 281 | message_type: "private", 282 | sub_type: "friend", 283 | message: [{ type: "text", text: msg }], 284 | raw_message: msg, 285 | }) 286 | } 287 | 288 | miHoYoLoginHelp() { 289 | if (!config.miHoYoLogin.help) return false 290 | this.reply(["发送【米哈游登录】", segment.button([ 291 | { text: "米哈游登录", callback: "米哈游登录" }, 292 | ])], true, { at: true }) 293 | } 294 | } -------------------------------------------------------------------------------- /Model/config.js: -------------------------------------------------------------------------------- 1 | import fs from "node:fs/promises" 2 | import YAML from "yaml" 3 | import _ from "lodash" 4 | 5 | const configFile = "config/TRSS.yaml" 6 | const config = { 7 | tips: "", 8 | 9 | Voice: { 10 | GenshinVoiceApi: "", 11 | ChatWaifuApi: "" 12 | }, 13 | 14 | RealESRGAN: { 15 | api: "", 16 | format: "jpg" 17 | }, 18 | 19 | RemBG: { 20 | api: "" 21 | }, 22 | 23 | miHoYoLogin: { 24 | help: true 25 | } 26 | } 27 | 28 | let configData 29 | 30 | if (await Bot.fsStat(configFile)) 31 | try { 32 | configData = YAML.parse(await fs.readFile(configFile, "utf-8")) 33 | _.merge(config, configData) 34 | } catch (err) { 35 | logger.error(`配置文件 读取失败:${logger.red(err)}`) 36 | } 37 | 38 | config.tips = [ 39 | "欢迎使用 TRSS Yunzai Plugin ! 作者:时雨🌌星空", 40 | "按 Ctrl+Q Y 保存退出", 41 | "参考:https://Yunzai.TRSS.me" 42 | ] 43 | 44 | if (YAML.stringify(config) != YAML.stringify(configData)) 45 | await fs.writeFile(configFile, YAML.stringify(config), "utf-8") 46 | 47 | export default config -------------------------------------------------------------------------------- /Model/file.js: -------------------------------------------------------------------------------- 1 | export default class { 2 | constructor(p) { 3 | this.p = p 4 | } 5 | 6 | async choose(path, type = "isFile") { 7 | let files = await Bot.glob(path, { type }) 8 | if (type) { 9 | const array = [] 10 | for (const i of files) try { 11 | if ((await Bot.fsStat(i))[type]()) 12 | array.push(i) 13 | } catch {} 14 | files = array 15 | } 16 | if (files.length < 2) 17 | return files[0] 18 | 19 | await this.p.reply(`检测到${files.length}个文件,请选择\n${files.map((i, n) => `${n+1}. ${i}`).join("\n")}`, true, { recallMsg: 10 }) 20 | const { msg } = await this.p.awaitContext() 21 | if (files[msg-1]) 22 | return files[msg-1] 23 | if (files.includes(msg)) 24 | return msg 25 | return false 26 | } 27 | } -------------------------------------------------------------------------------- /Picture/Microsoft_C++_生成工具.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Picture/Microsoft_C++_生成工具.png -------------------------------------------------------------------------------- /Picture/苏半夏.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Picture/苏半夏.png -------------------------------------------------------------------------------- /Picture/苏半夏D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Picture/苏半夏D.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 苏半夏 7 | 8 | 9 | 10 | # TRSS Yunzai Plugin 11 | 12 | 云崽机器人插件 13 | 14 | [![访问量](https://visitor-badge.glitch.me/badge?page_id=TimeRainStarSky.TRSS-Plugin&right_color=red&left_text=访%20问%20量)](https://github.com/TimeRainStarSky/TRSS-Plugin) 15 | [![Stars](https://img.shields.io/github/stars/TimeRainStarSky/TRSS-Plugin?color=yellow&label=收藏)](../../stargazers) 16 | [![Downloads](https://img.shields.io/github/downloads/TimeRainStarSky/TRSS-Plugin/total?color=blue&label=下载)](Install.sh) 17 | [![Releases](https://img.shields.io/github/v/release/TimeRainStarSky/TRSS-Plugin?color=green&label=发行版)](../../releases/latest) 18 | 19 | [![访问量](https://profile-counter.glitch.me/TimeRainStarSky-TRSS-Plugin/count.svg)](https://github.com/TimeRainStarSky/TRSS-Plugin) 20 | 21 |
22 | 23 | ## 安装教程 24 | 25 | - 推荐使用 [TRSS Yunzai 管理脚本](https://TRSS.me) 安装 26 | 27 | [![TRSS Yunzai 管理脚本](https://github-readme-stats.vercel.app/api/pin/?username=TimeRainStarSky&repo=TRSS_Yunzai&show_owner=true)](../../../TRSS_Yunzai) 28 | 29 | 1. 准备:[Yunzai-Bot](https://github.com/Le-niao/Yunzai-Bot) 30 | 31 | 2. 安装:[GitHub](https://github.com/TimeRainStarSky/TRSS-Plugin) 或 [Gitee](https://gitee.com/TimeRainStarSky/TRSS-Plugin) 32 | 33 | ``` 34 | git clone --depth 1 https://Yunzai.TRSS.me plugins/TRSS-Plugin 35 | pnpm i 36 | ``` 37 | 38 | 3. 安装 `图片修复` | `图片背景去除` | `语音合成`,不用可忽略 39 | 40 |
展开 41 | 42 | 安装 [Python 3.10-3.11](https://python.org) 和 [Poetry](https://python-poetry.org),并在插件目录执行以下操作 43 | 44 | ``` 45 | poetry install 46 | ``` 47 | 48 | - 图片修复: 49 | 50 | ``` 51 | git clone --depth 1 https://gitee.com/TimeRainStarSky/Real-ESRGAN 52 | cd Real-ESRGAN 53 | poetry run python setup.py develop 54 | ``` 55 | 56 | - 图片背景去除: 57 | 58 | ``` 59 | git clone --depth 1 https://gitee.com/TimeRainStarSky/RemBG 60 | cd RemBG 61 | curl -LO https://github.com/TimeRainStarSky/TRSS-Plugin/releases/download/latest/u2net.onnx.xz 62 | curl -LO https://github.com/TimeRainStarSky/TRSS-Plugin/releases/download/latest/isnetis.onnx.xz 63 | xz -dv u2net.onnx.xz isnetis.onnx.xz 64 | ``` 65 | 66 | - 语音合成: 67 | 68 | ``` 69 | git clone --depth 1 https://gitee.com/TimeRainStarSky/ChatWaifu 70 | git clone --depth 1 https://gitee.com/TimeRainStarSky/GenshinVoice 71 | ``` 72 | 73 | - 语音合成 汉语模型: 74 | 75 | ``` 76 | cd ChatWaifu 77 | curl -LO https://github.com/TimeRainStarSky/TRSS-Plugin/releases/download/latest/ChatWaifuCN.txz 78 | tar -xvJf ChatWaifuCN.txz 79 | ``` 80 | 81 | - 语音合成 日语模型: 82 | 83 | ``` 84 | cd ChatWaifu 85 | curl -LO https://github.com/TimeRainStarSky/TRSS-Plugin/releases/download/latest/ChatWaifuJP.txz 86 | tar -xvJf ChatWaifuJP.txz 87 | ``` 88 | 89 | - 语音合成 原神模型: 90 | ``` 91 | cd GenshinVoice 92 | curl -LO https://github.com/TimeRainStarSky/TRSS-Plugin/releases/download/latest/G_809000.pth.xz 93 | xz -dv G_809000.pth.xz 94 | ``` 95 | 96 |
部署为 API 服务器 97 | 98 | ``` 99 | bash server.sh [端口] 100 | ``` 101 | 102 |
103 | 104 | - 阿里云盘 / 百度网盘: 105 | 106 | 使用脚本安装后,启动 CLI,输入 `login -h`,按提示登录 107 | 108 |
109 | 110 | ## 使用教程 111 | 112 |
图片修复 113 | 114 | - 图片修复 / 动漫图片修复 + `图片` 115 | 116 |
117 | 118 |
图片背景去除 119 | 120 | - 图片背景去除 / 动漫图片背景去除 + `图片` 121 | 122 |
123 | 124 |
语音合成 125 | 126 | - `角色名` + 说 + `中文` 127 | - 语音合成角色列表 128 | 129 |
130 | 131 |
系统信息 132 | 133 | - 系统信息 / 系统信息图片 / 系统测试 134 | 135 |
136 | 137 |
二维码生成 138 | 139 | - 二维码 + `文字` 140 | 141 |
142 | 143 |
米哈游登录 144 | 145 | - 二维码登录:米哈游登录 146 | - 账号密码登录:米哈游登录 + `账号` 147 | 148 |
149 | 150 |
Markdown(权限:主人) 151 | 152 | - md + `文件` / `URL` 153 | 154 |
155 | 156 |
代码高亮(权限:主人) 157 | 158 | - sc + `文件` / `URL` 159 | 160 |
161 | 162 |
远程命令(权限:主人) 163 | 164 | - rc + `Shell 命令` 165 | - rj + `JavaScript 命令` 166 | - dm(单条消息) / mm(多条消息) / fm(转发消息) + `JavaScript 表达式` 167 | - rcp / rjp / dmp / mmp / fmp 以图片显示输出 168 | 169 |
170 | 171 |
文件操作(权限:主人) 172 | 173 | - 文件查看 / 文件上传 / 文件下载 + `路径` 174 | 175 |
176 | 177 |
阿里云盘(权限:主人) 178 | 179 | 阿里云盘 + 180 | 181 | - 帮助 182 | - 上传 183 | - 下载 184 | - 相簿 185 | - 链接 186 | - 查看 187 | - 创建目录 188 | - 移动 189 | - 回收站 190 | - 重命名 191 | - 删除 192 | - 分享 193 | - 同步备份 194 | - 树形图 195 | - 在线网盘 196 | - 切换网盘 197 | - 登录账号 198 | - 账号列表 199 | - 退出账号 200 | - 空间配额 201 | - 切换账号 202 | - 当前账号 203 | 204 |
205 | 206 |
百度网盘(权限:主人) 207 | 208 | 百度网盘 + 209 | 210 | - 帮助 211 | - 上传 212 | - 下载 213 | - 复制 214 | - 链接 215 | - 查看 216 | - 元信息 217 | - 创建目录 218 | - 移动 219 | - 离线下载 220 | - 空间配额 221 | - 回收站 222 | - 删除 223 | - 搜索 224 | - 分享 225 | - 转存 226 | - 树形图 227 | - 登录账号 228 | - 账号列表 229 | - 退出账号 230 | - 切换账号 231 | - 当前账号 232 | 233 |
234 | 235 | ## 常见问题 236 | 237 |
展开 238 | 239 | - 问:`ModuleNotFoundError: No module named 'xxx'` 240 | - 答:未正确执行 `poetry install` 241 | 242 | - 问:`已杀死` | `Signal 9` | `MemoryError` 243 | - 答:`清理内存` 或 `增加 SWAP` 244 | 245 | - 问:使用 `Git Bash` 执行 `poetry install` 失败 246 | - 答:改用 `命令提示符` 或 `Windows PowerShell` 247 | 248 | - 问:`error: Microsoft Visual C++ 14.0 or greater is required.` 249 | - 答:下载安装 [Microsoft C++ 生成工具](https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools) 250 | ![Microsoft C++ 生成工具](Picture/Microsoft_C++_生成工具.png) 251 | 252 | - 问:`'bash' 不是内部或外部命令,也不是可运行的程序或批处理文件` `bash : 无法将“sh”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。` 253 | - 答:改用 `Git Bash` 254 | 255 | - 问:手动安装过程中出现问题 256 | - 答:建议自行解决,不会就用脚本一键安装 257 | 258 | - 问:我有其他问题 259 | - 答:提供详细问题描述,通过下方 联系方式 反馈问题 260 | 261 |
262 | 263 | ## 联系方式 264 | 265 | - QQ 群组: 266 | 1. [659945190](https://jq.qq.com/?k=VBuHGPv3) 267 | 2. [1027131254](https://jq.qq.com/?k=Af0pTDHU) 268 | 3. [300714227](https://jq.qq.com/?k=V2xVpaR7) 269 | 270 | ### 时雨 🌌 星空 271 | 272 | - GitHub:[TimeRainStarSky](https://github.com/TimeRainStarSky) 273 | - 酷安:[时雨丶星空](https://coolapk.com/u/2650948) 274 | - QQ:[2536554304](https://qm.qq.com/cgi-bin/qm/qr?k=x8LtlP8vwZs7qLwmsbCsyLoAHy7Et1Pj) 275 | - Telegram:[TimeRainStarSky](https://t.me/TimeRainStarSky) 276 | 277 | ## 赞助支持 278 | 279 | - 爱发电: 280 | - Partme: -------------------------------------------------------------------------------- /Resources/Code/Code.css: -------------------------------------------------------------------------------- 1 | @import url(../common/common.css); 2 | 3 | @font-face { 4 | font-family: Inconsolata; 5 | src: url("../fonts/Inconsolata.ttf"); 6 | } 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | box-sizing: border-box; 12 | -webkit-user-select: none; 13 | user-select: none; 14 | } 15 | 16 | body { 17 | width: 850px; 18 | } 19 | 20 | .container { 21 | width: 850px; 22 | font-family: Inconsolata, monospace; 23 | } 24 | 25 | .box { 26 | padding: 10px; 27 | background: rgba(13, 13, 13, 0.85); 28 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 29 | backdrop-filter: blur(19px); 30 | -webkit-backdrop-filter: blur(19px); 31 | border-radius: 10px; 32 | border: 1px solid rgba(255, 255, 255, 0.18); 33 | white-space: pre-wrap; 34 | overflow-wrap: break-word; 35 | font-family: Inconsolata, monospace; 36 | color: #fff; 37 | } -------------------------------------------------------------------------------- /Resources/Code/Code.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
代码展示框
16 |
{{@Code}}
17 | 18 |
19 | 20 | 28 | 29 | -------------------------------------------------------------------------------- /Resources/Markdown/Markdown.css: -------------------------------------------------------------------------------- 1 | @import url(../common/common.css); 2 | 3 | 4 | .box { 5 | padding: 30px; 6 | background: rgba(255, 255, 255, 0.6); 7 | box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); 8 | backdrop-filter: blur(16px); 9 | -webkit-backdrop-filter: blur(16px); 10 | border-radius: 10px; 11 | border: 1px solid rgba(255, 255, 255, 0.18); 12 | } 13 | 14 | body { 15 | width: max-content; 16 | max-width: 800px; 17 | } 18 | 19 | code:not([data-highlighted=yes]) { 20 | background: #282C34; 21 | color: #F8F8E5; 22 | font-size: 85%; 23 | border-radius: 6px; 24 | padding: .1em .4em; 25 | margin: 0; 26 | font-family: inherit; 27 | } 28 | 29 | table:not(code table) { 30 | border-collapse: collapse; 31 | margin-bottom: 16px; 32 | } 33 | 34 | table:not(code table) th, 35 | table:not(code table) td { 36 | border: 1px solid #dfe2e5; 37 | padding: 6px 13px; 38 | background-color: #fff; 39 | } 40 | 41 | table:not(code table) tr:nth-child(even) td { 42 | background-color: #F6F8FA; 43 | } 44 | 45 | h1, 46 | h2 { 47 | border-bottom: 0.5px solid #9099a3; 48 | } 49 | 50 | ul { 51 | padding-left: 30px; 52 | } 53 | 54 | blockquote { 55 | padding-left: 15px; 56 | color: #636c76; 57 | border-left: 5px solid #D0D7DE; 58 | margin: 0; 59 | } 60 | 61 | img { 62 | max-width: 100%; 63 | height: auto; 64 | } -------------------------------------------------------------------------------- /Resources/Markdown/Markdown.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 19 |
20 |
源码展示框
21 |
22 | {{@Markdown}} 23 |
24 | 25 |
26 | 27 | 35 | 36 | -------------------------------------------------------------------------------- /Resources/SourceCode/SourceCode.css: -------------------------------------------------------------------------------- 1 | @import url(../common/common.css); 2 | @import url(./codeTheme.css); 3 | 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | -webkit-user-select: none; 9 | user-select: none; 10 | } 11 | 12 | body { 13 | width: max-content; 14 | } 15 | 16 | .container { 17 | max-width: 1200px; 18 | width: max-content; 19 | } 20 | 21 | .hljs::before { 22 | content: ''; 23 | height: 18px; 24 | background: url(./icon/bar.png) center no-repeat; 25 | background-size: 100%; 26 | width: 80px; 27 | display: block; 28 | margin-bottom: 1em; 29 | } 30 | 31 | .hljs { 32 | line-height: 25px; 33 | } -------------------------------------------------------------------------------- /Resources/SourceCode/SourceCode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 |
24 | 25 |
源码展示框
26 |
27 |
{{@SourceCode}}
28 |
29 | 30 |
31 | 32 | 40 | 41 | -------------------------------------------------------------------------------- /Resources/SourceCode/codeTheme.css: -------------------------------------------------------------------------------- 1 | .hljs-keyword, 2 | .hljs-doctag, 3 | .hljs-function, 4 | .hljs-selector-tag, 5 | .hljs-name, 6 | .hljs-meta { 7 | color: #FF79C6; 8 | font-style: normal; 9 | } 10 | 11 | .hljs-string { 12 | color: #F1FA8C; 13 | } 14 | 15 | .hljs-title.function_, 16 | .hljs-selector-class, 17 | .hljs-selector-pseudo, 18 | .hljs-code, 19 | .hljs-meta .hljs-keyword, 20 | .hljs-tag .hljs-attr { 21 | color: #4EF36C; 22 | font-style: normal; 23 | } 24 | 25 | .hljs-number, 26 | .hljs-literal, 27 | .hljs-variable.constant_, 28 | .hljs-variable.language_, 29 | .hljs-section { 30 | color: #BD93F9; 31 | font-style: normal; 32 | } 33 | 34 | .hljs-params, 35 | .hljs-variable { 36 | color: #FFB167; 37 | font-style: italic; 38 | } 39 | 40 | .hljs-title.class_, 41 | .hljs-type, 42 | .hljs-attr, 43 | .hljs-link, 44 | .hljs-attribute, 45 | .hljs-built_in { 46 | color: #8BE9FD; 47 | font-style: normal; 48 | } 49 | 50 | .hljs-regexp { 51 | color: #FF5555 52 | } 53 | 54 | .hljs-subst, 55 | .hljs-tag { 56 | color: #fff 57 | } 58 | 59 | .hljs-section { 60 | font-weight: bold; 61 | } 62 | 63 | .hljs-strong { 64 | color: #FFB86C; 65 | } 66 | 67 | .language-javascript .hljs-attr, 68 | .language-typescript .hljs-attr { 69 | color: #fff; 70 | } 71 | 72 | .hljs-type, 73 | .hljs-selector-class { 74 | font-style: italic; 75 | } 76 | 77 | .hljs-comment { 78 | color: #62729A; 79 | font-style: normal; 80 | } -------------------------------------------------------------------------------- /Resources/SourceCode/icon/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Resources/SourceCode/icon/bar.png -------------------------------------------------------------------------------- /Resources/common/common.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: consola; 3 | src: url("../fonts/consola.ttf"); 4 | font-weight: normal; 5 | font-style: normal; 6 | } 7 | 8 | @font-face { 9 | font-family: consola; 10 | src: url("../fonts/consolab.ttf"); 11 | font-weight: bold; 12 | font-style: normal; 13 | } 14 | 15 | @font-face { 16 | font-family: consola; 17 | src: url("../fonts/consolai.ttf"); 18 | font-weight: normal; 19 | font-style: italic; 20 | } 21 | 22 | @font-face { 23 | font-family: consola; 24 | src: url("../fonts/consolaz.ttf"); 25 | font-weight: bold; 26 | font-style: italic; 27 | } 28 | 29 | 30 | @font-face { 31 | font-family: Inconsolata; 32 | src: url("../fonts/Inconsolata.ttf"); 33 | } 34 | 35 | .container { 36 | background: url("../../../miao-plugin/resources/admin/imgs/bg.png") #b5b5b5 center top no-repeat; 37 | padding: 10px 20px; 38 | background-size: cover; 39 | font-family: consola, Inconsolata monospace; 40 | } 41 | 42 | body { 43 | transform: scale(3); 44 | transform-origin: 0 0; 45 | } 46 | 47 | .hljs { 48 | padding: 10px; 49 | font-family: consola, Inconsolata monospace; 50 | background: #282C34; 51 | font-size: 20px; 52 | box-shadow: 10px 10px 30px 0 rgba(0, 0, 0, 0.395); 53 | border-radius: 5px; 54 | /* border: 1px solid rgba(255, 255, 255, 0.18); */ 55 | } 56 | 57 | pre { 58 | white-space: pre-wrap; 59 | word-break: break-all; 60 | } 61 | 62 | .hljs-ln-n { 63 | white-space: nowrap; 64 | } 65 | 66 | .hljs-ln-code { 67 | color: #F8F8E5; 68 | } 69 | 70 | .hljs-ln-numbers { 71 | color: #5F6E9D; 72 | } 73 | 74 | .hljs-ln-n:before { 75 | padding-right: 10px; 76 | } 77 | 78 | .title, 79 | .copyright { 80 | text-align: center; 81 | color: #000; 82 | text-shadow: 0px 0px 2px #fff; 83 | font-weight: bold; 84 | } 85 | 86 | .title { 87 | font-size: 40px; 88 | margin-bottom: 10px; 89 | } 90 | 91 | .copyright { 92 | font-size: 30px; 93 | margin-top: 10px; 94 | } -------------------------------------------------------------------------------- /Resources/fonts/Inconsolata.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Resources/fonts/Inconsolata.ttf -------------------------------------------------------------------------------- /Resources/fonts/consola.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Resources/fonts/consola.ttf -------------------------------------------------------------------------------- /Resources/fonts/consolab.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Resources/fonts/consolab.ttf -------------------------------------------------------------------------------- /Resources/fonts/consolai.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Resources/fonts/consolai.ttf -------------------------------------------------------------------------------- /Resources/fonts/consolaz.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeRainStarSky/TRSS-Plugin/16e435fe79863dfbb1c65141d20ada061ef40217/Resources/fonts/consolaz.ttf -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | logger.info(logger.yellow("- 正在加载 TRSS 插件")) 2 | 3 | if (process.version < "v18") 4 | throw Error(`Node.js ${process.version} < v18`) 5 | import fs from "node:fs/promises" 6 | 7 | global.segment ??= (await import("oicq")).segment 8 | 9 | if (!Bot.makeForwardArray) { 10 | const { makeForwardMsg } = (await import("../../lib/common/common.js")).default 11 | Bot.makeForwardArray = msg => makeForwardMsg({}, msg) 12 | } 13 | 14 | Bot.sleep ??= (time, promise) => { 15 | if (promise) return Promise.race([promise, Bot.sleep(time)]) 16 | return new Promise(resolve => setTimeout(resolve, time)) 17 | } 18 | 19 | Bot.download ??= (await import("../../lib/common/common.js")).default.downFile 20 | 21 | Bot.String ??= (data, opts) => { 22 | switch (typeof data) { 23 | case "string": 24 | return data 25 | case "function": 26 | return String(data) 27 | case "object": 28 | if (data instanceof Error) 29 | return data.stack 30 | if (Buffer.isBuffer(data)) 31 | return String(data) 32 | } 33 | 34 | try { 35 | return JSON.stringify(data, undefined, opts) 36 | } catch (err) { 37 | if (typeof data.toString == "function") 38 | return String(data) 39 | else 40 | return "[object null]" 41 | } 42 | } 43 | 44 | Bot.Loging ??= Bot.String 45 | 46 | if (!Bot.exec) { 47 | const { exec, execFile } = await import("node:child_process") 48 | Bot.exec = (cmd, opts = {}) => new Promise(resolve => { 49 | const name = logger.cyan(Bot.String(cmd)) 50 | logger[opts.quiet ? "debug" : "mark"]("[执行命令]", name) 51 | opts.encoding ??= "buffer" 52 | const callback = (error, stdout, stderr) => { 53 | const raw = { stdout, stderr } 54 | stdout = String(stdout).trim() 55 | stderr = String(stderr).trim() 56 | resolve({ error, stdout, stderr, raw }) 57 | logger[opts.quiet ? "debug" : "mark"](`[执行命令] ${name} ${logger.green(`[完成${Date.now()-start_time}ms]`)} ${stdout?`\n${stdout}`:""}${stderr?logger.red(`\n${stderr}`):""}`) 58 | if (error) logger[opts.quiet ? "debug" : "error"]("[执行命令]", error) 59 | } 60 | const start_time = Date.now() 61 | if (Array.isArray(cmd)) 62 | execFile(cmd.shift(), cmd, opts, callback) 63 | else 64 | exec(cmd, opts, callback) 65 | }) 66 | } 67 | 68 | Bot.fsStat ??= async path => { try { 69 | return await fs.stat(path) 70 | } catch (err) { 71 | logger.trace("获取", path, "状态错误", err) 72 | return false 73 | }} 74 | 75 | Bot.glob ??= async (path, opts = {}) => { 76 | if (!opts.force && await Bot.fsStat(path)) 77 | return [path] 78 | if (!fs.glob) return [] 79 | const array = [] 80 | try { 81 | for await (const i of fs.glob(path, opts)) 82 | array.push(i) 83 | } catch (err) { 84 | logger.error("匹配", path, "错误", err) 85 | } 86 | return array 87 | } 88 | 89 | const files = (await fs.readdir("plugins/TRSS-Plugin/Apps")) 90 | .filter(file => file.endsWith(".js")) 91 | 92 | const ret = await Promise.allSettled( 93 | files.map(i => import(`./Apps/${i}`)) 94 | ) 95 | 96 | export const apps = {} 97 | for (const i in files) { 98 | const name = files[i].replace(".js", "") 99 | if (ret[i].status != "fulfilled") { 100 | logger.error(`载入插件错误:${logger.red(name)}`) 101 | logger.error(ret[i].reason) 102 | continue 103 | } 104 | apps[name] = ret[i].value[name] 105 | } 106 | 107 | logger.info(logger.green("- TRSS 插件 加载完成")) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TRSS-Plugin", 3 | "type": "module", 4 | "author": "TimeRainStarSky", 5 | "dependencies": { 6 | "@highlightjs/cdn-assets": "^11.10.0", 7 | "ansi_up": "^6.0.2", 8 | "highlightjs-line-numbers.js": "^2.8.0", 9 | "markdown-it": "^14.1.0", 10 | "qrcode": "^1.5.4" 11 | } 12 | } -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "TRSS-Plugin" 3 | version = "1.0.0" 4 | description = "TRSS Yunzai Plugin" 5 | authors = ["TimeRainStarSky "] 6 | package-mode = false 7 | 8 | [tool.poetry.dependencies] 9 | python = ">=3.10,<3.13" 10 | matplotlib = "^3.9.0" 11 | torch = "^2.3.0" 12 | scipy = "^1.13.0" 13 | librosa = "^0.10.2.post1" 14 | unidecode = "^1.3.8" 15 | phonemizer = "^3.2.1" 16 | pypinyin = "^0.51.0" 17 | pypinyin-dict = "^0.8.0" 18 | jieba = "^0.42.1" 19 | cython = "^3.0.10" 20 | facexlib = "^0.3.0" 21 | numpy = "^1.26.4" 22 | opencv-python = "^4.9.0.80" 23 | pillow = "^10.3.0" 24 | torchvision = "^0.18.0" 25 | tqdm = "^4.66.4" 26 | flask = "^3.0.3" 27 | rembg = "^2.0.56" 28 | cn2an = "^0.5.22" 29 | openjtalk = { version = "^0.3.0.dev3", markers = "platform_machine == 'x86_64'" } 30 | ruamel-yaml = "^0.18.6" 31 | 32 | [[tool.poetry.source]] 33 | name = "BFSU" 34 | url = "https://mirrors.bfsu.edu.cn/pypi/web/simple" 35 | priority = "primary" 36 | 37 | [[tool.poetry.source]] 38 | name = "Aliyun" 39 | url = "https://mirrors.aliyun.com/pypi/simple" 40 | priority = "supplemental" 41 | --------------------------------------------------------------------------------