├── .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 |
{{@SourceCode}}
28 |