mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 16:19:18 +00:00
✨ feat: 新版本适配拉格朗日上传
1. 适配拉格朗日上传文件,解决部分用户使用拉格朗日无法观看视频问题 2. 新增switchers,存储一些开关例如:海外解析、拉格朗日判断等 3. 新增ws用于连接拉格朗日 4. 新增配置文件配置项`lagrangeForwardWebSocket`,用于配置拉格朗日正向地址
This commit is contained in:
parent
012213d871
commit
503ec55dfc
108
apps/switchers.js
Normal file
108
apps/switchers.js
Normal file
@ -0,0 +1,108 @@
|
||||
import config from "../model/index.js";
|
||||
import { REDIS_YUNZAI_ISOVERSEA, REDIS_YUNZAI_LAGRANGE } from "../constants/constant.js";
|
||||
import { deleteFolderRecursive, readCurrentDir } from "../utils/file.js";
|
||||
|
||||
export class switchers extends plugin {
|
||||
constructor() {
|
||||
super({
|
||||
name: "R插件开关类",
|
||||
dsc: "内含一些和Redis相关的开关类",
|
||||
event: "message.group",
|
||||
priority: 300,
|
||||
rule: [
|
||||
{
|
||||
reg: "^#设置海外解析$",
|
||||
fnc: "setOversea",
|
||||
permission: "master",
|
||||
},
|
||||
{
|
||||
reg: "^#设置拉格朗日$",
|
||||
fnc: "setLagrange",
|
||||
permission: "master",
|
||||
},
|
||||
{
|
||||
reg: "^清理data垃圾$",
|
||||
fnc: "clearTrash",
|
||||
permission: "master",
|
||||
},
|
||||
]
|
||||
});
|
||||
// 配置文件
|
||||
this.toolsConfig = config.getConfig("tools");
|
||||
// 视频保存路径
|
||||
this.defaultPath = this.toolsConfig.defaultPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置海外模式
|
||||
* @param e
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async setOversea(e) {
|
||||
// 查看当前设置
|
||||
let os;
|
||||
if ((await redis.exists(REDIS_YUNZAI_ISOVERSEA))) {
|
||||
os = JSON.parse(await redis.get(REDIS_YUNZAI_ISOVERSEA)).os;
|
||||
}
|
||||
// 设置
|
||||
os = ~os
|
||||
await redis.set(
|
||||
REDIS_YUNZAI_ISOVERSEA,
|
||||
JSON.stringify({
|
||||
os: os,
|
||||
}),
|
||||
);
|
||||
e.reply(`当前服务器:${ os ? '海外服务器' : '国内服务器' }`)
|
||||
return true;
|
||||
}
|
||||
|
||||
async setLagrange(e) {
|
||||
// 查看当前设置
|
||||
let driver;
|
||||
if ((await redis.exists(REDIS_YUNZAI_LAGRANGE))) {
|
||||
driver = JSON.parse(await redis.get(REDIS_YUNZAI_LAGRANGE)).driver;
|
||||
}
|
||||
// 设置
|
||||
driver = ~driver
|
||||
await redis.set(
|
||||
REDIS_YUNZAI_LAGRANGE,
|
||||
JSON.stringify({
|
||||
driver: driver,
|
||||
}),
|
||||
);
|
||||
e.reply(`当前驱动:${ driver ? '拉格朗日' : '其他驱动' }`)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理垃圾文件
|
||||
* @param e
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async clearTrash(e) {
|
||||
const dataDirectory = "./data/";
|
||||
|
||||
// 删除Yunzai遗留问题的合成视频垃圾文件
|
||||
try {
|
||||
const files = await readCurrentDir(dataDirectory);
|
||||
let dataClearFileLen = 0;
|
||||
for (const file of files) {
|
||||
// 如果文件名符合规则,执行删除操作
|
||||
if (/^[0-9a-f]{32}$/.test(file)) {
|
||||
await fs.promises.unlink(dataDirectory + file);
|
||||
dataClearFileLen++;
|
||||
}
|
||||
}
|
||||
// 删除R插件临时文件
|
||||
const rTempFileLen = await deleteFolderRecursive(this.defaultPath)
|
||||
e.reply(
|
||||
`数据统计:\n` +
|
||||
`- 当前清理了${ dataDirectory }下总计:${ dataClearFileLen } 个垃圾文件\n` +
|
||||
`- 当前清理了${ this.toolsConfig.defaultPath }下文件夹:${ rTempFileLen } 个群的所有临时文件`
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
await e.reply("清理失败,重试或者手动清理即可");
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import _ from "lodash";
|
||||
import tunnel from "tunnel";
|
||||
import HttpProxyAgent from "https-proxy-agent";
|
||||
import { exec, execSync } from "child_process";
|
||||
import { checkAndRemoveFile, deleteFolderRecursive, mkdirIfNotExists, readCurrentDir } from "../utils/file.js";
|
||||
import { checkAndRemoveFile, mkdirIfNotExists } from "../utils/file.js";
|
||||
import {
|
||||
downloadBFile,
|
||||
getBiliAudio,
|
||||
@ -26,6 +26,7 @@ import {
|
||||
DIVIDING_LINE,
|
||||
douyinTypeMap,
|
||||
REDIS_YUNZAI_ISOVERSEA,
|
||||
REDIS_YUNZAI_LAGRANGE,
|
||||
transMap,
|
||||
TWITTER_BEARER_TOKEN,
|
||||
XHS_NO_WATERMARK_HEADER,
|
||||
@ -65,6 +66,7 @@ import { processTikTokUrl } from "../utils/tiktok.js";
|
||||
import { getDS } from "../utils/mihoyo.js";
|
||||
import GeneralLinkAdapter from "../utils/general-link-adapter.js";
|
||||
import { mid2id } from "../utils/weibo.js";
|
||||
import { LagrangeAdapter } from "../utils/lagrange-adapter.js";
|
||||
|
||||
export class tools extends plugin {
|
||||
/**
|
||||
@ -124,16 +126,6 @@ export class tools extends plugin {
|
||||
reg: "(instagram.com)",
|
||||
fnc: "instagram",
|
||||
},
|
||||
{
|
||||
reg: "^清理data垃圾$",
|
||||
fnc: "clearTrash",
|
||||
permission: "master",
|
||||
},
|
||||
{
|
||||
reg: "^#设置海外解析$",
|
||||
fnc: "setOversea",
|
||||
permission: "master",
|
||||
},
|
||||
{
|
||||
reg: "(h5app.kuwo.cn)",
|
||||
fnc: "bodianMusic",
|
||||
@ -851,34 +843,6 @@ export class tools extends plugin {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 清理垃圾文件
|
||||
async clearTrash(e) {
|
||||
const dataDirectory = "./data/";
|
||||
|
||||
// 删除Yunzai遗留问题的合成视频垃圾文件
|
||||
try {
|
||||
const files = await readCurrentDir(dataDirectory);
|
||||
let dataClearFileLen = 0;
|
||||
for (const file of files) {
|
||||
// 如果文件名符合规则,执行删除操作
|
||||
if (/^[0-9a-f]{32}$/.test(file)) {
|
||||
await fs.promises.unlink(dataDirectory + file);
|
||||
dataClearFileLen++;
|
||||
}
|
||||
}
|
||||
// 删除R插件临时文件
|
||||
const rTempFileLen = await deleteFolderRecursive(this.defaultPath)
|
||||
e.reply(
|
||||
`数据统计:\n` +
|
||||
`- 当前清理了${ dataDirectory }下总计:${ dataClearFileLen } 个垃圾文件\n` +
|
||||
`- 当前清理了${ this.toolsConfig.defaultPath }下文件夹:${ rTempFileLen } 个群的所有临时文件`
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
await e.reply("清理失败,重试或者手动清理即可");
|
||||
}
|
||||
}
|
||||
|
||||
// ins解析
|
||||
async instagram(e) {
|
||||
let suffix = e.msg.match(/(?<=com\/)[\/a-z0-9A-Z].*/)[0];
|
||||
@ -1710,29 +1674,6 @@ export class tools extends plugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置海外模式
|
||||
* @param e
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async setOversea(e) {
|
||||
// 查看当前设置
|
||||
let os;
|
||||
if ((await redis.exists(REDIS_YUNZAI_ISOVERSEA))) {
|
||||
os = JSON.parse(await redis.get(REDIS_YUNZAI_ISOVERSEA)).os;
|
||||
}
|
||||
// 设置
|
||||
os = ~os
|
||||
await redis.set(
|
||||
REDIS_YUNZAI_ISOVERSEA,
|
||||
JSON.stringify({
|
||||
os: os,
|
||||
}),
|
||||
);
|
||||
e.reply(`当前服务器:${ os ? '海外服务器' : '国内服务器' }`)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是海外服务器
|
||||
* @return {Promise<Boolean>}
|
||||
@ -1752,6 +1693,25 @@ export class tools extends plugin {
|
||||
return JSON.parse((await redis.get(REDIS_YUNZAI_ISOVERSEA))).os;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是拉格朗日驱动
|
||||
* @returns {Promise<Boolean>}
|
||||
*/
|
||||
async isLagRangeDriver() {
|
||||
// 如果第一次使用没有值就设置
|
||||
if (!(await redis.exists(REDIS_YUNZAI_LAGRANGE))) {
|
||||
await redis.set(
|
||||
REDIS_YUNZAI_ISOVERSEA,
|
||||
JSON.stringify({
|
||||
driver: false,
|
||||
}),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
// 如果有就取出来
|
||||
return JSON.parse((await redis.get(REDIS_YUNZAI_LAGRANGE))).driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制用户调用
|
||||
* @param e
|
||||
@ -1773,7 +1733,19 @@ export class tools extends plugin {
|
||||
* @param videoSizeLimit 发送转上传视频的大小限制,默认70MB
|
||||
*/
|
||||
async sendVideoToUpload(e, path, videoSizeLimit = 70) {
|
||||
if (!fs.existsSync(path)) return e.reply('视频不存在');
|
||||
// 判断文件是否存在
|
||||
if (!fs.existsSync(path)) {
|
||||
return e.reply('视频不存在');
|
||||
}
|
||||
// 判断是否是拉格朗日
|
||||
if (await this.isLagRangeDriver()) {
|
||||
// 构造拉格朗日适配器
|
||||
const lagrange = new LagrangeAdapter(this.toolsConfig.lagrangeForwardWebSocket);
|
||||
// 上传群文件
|
||||
await lagrange.uploadGroupFile(e.user_id || e.sender.card, e.group_id, path);
|
||||
// 上传完直接返回
|
||||
return;
|
||||
}
|
||||
const stats = fs.statSync(path);
|
||||
const videoSize = (stats.size / (1024 * 1024)).toFixed(2);
|
||||
if (videoSize > videoSizeLimit) {
|
||||
@ -1791,6 +1763,7 @@ export class tools extends plugin {
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async uploadGroupFile(e, path) {
|
||||
// 判断是否是ICQQ
|
||||
if (e.bot?.sendUni) {
|
||||
await e.group.fs.upload(path);
|
||||
} else {
|
||||
|
@ -14,3 +14,5 @@ douyinCookie: '' # douyin's cookie, 格式:odin_tt=xxx;passport_fe_beating_sta
|
||||
queueConcurrency: 1 # 【目前只涉及哔哩哔哩的下载】根据服务器性能设置可以并发下载的个数,如果你的服务器比较强劲,就选择4~12,较弱就一个一个下载,选择1
|
||||
|
||||
videoDownloadConcurrency: 1 # 下载视频是否使用多线程,如果不使用默认是1,如果使用根据服务器进行选择,如果不确定是否可以用4即可,高性能服务器随意4~12都可以,看CPU的实力
|
||||
|
||||
lagrangeForwardWebSocket: 'ws://127.0.0.1:9091/' # 格式:ws://地址:端口/,拉格朗日正向连接地址,用于适配拉格朗日上传群文件,解决部分用户无法查看视频问题
|
@ -1,7 +1,8 @@
|
||||
- {
|
||||
version: 1.6.7,
|
||||
version: 1.7.0,
|
||||
data:
|
||||
[
|
||||
新增<span class="cmd">适配拉格朗日上传</span>功能,
|
||||
新增<span class="cmd">超过文件大小转上传</span>功能,
|
||||
新增<span class="cmd">B站下载</span>功能,
|
||||
新增<span class="cmd">B站扫码</span>功能,
|
||||
|
@ -71,6 +71,12 @@ export const DIVIDING_LINE = "\n------------------{}------------------"
|
||||
*/
|
||||
export const REDIS_YUNZAI_ISOVERSEA = "Yz:rconsole:tools:oversea";
|
||||
|
||||
/**
|
||||
* 保存判断机子是否使用的是拉格朗日
|
||||
* @type {string}
|
||||
*/
|
||||
export const REDIS_YUNZAI_LAGRANGE = "Yz:rconsole:tools:lagrange";
|
||||
|
||||
export const TWITTER_BEARER_TOKEN = "";
|
||||
|
||||
/**
|
||||
|
@ -141,6 +141,17 @@ export function supportGuoba() {
|
||||
placeholder: "不确定用1即可,高性能服务器随意4~12都可以,看CPU的实力",
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "tools.lagrangeForwardWebSocket",
|
||||
label: "拉格朗日正向WebSocket连接地址",
|
||||
bottomHelpMessage:
|
||||
"格式:ws://地址:端口/,拉格朗日正向连接地址,用于适配拉格朗日上传群文件,解决部分用户无法查看视频问题",
|
||||
component: "Input",
|
||||
required: false,
|
||||
componentProps: {
|
||||
placeholder: "请输入拉格朗日正向WebSocket连接地址",
|
||||
},
|
||||
}
|
||||
],
|
||||
getConfigData() {
|
||||
const toolsData = {
|
||||
|
@ -6,6 +6,7 @@
|
||||
"axios": "^1.3.4",
|
||||
"tunnel": "^0.0.6",
|
||||
"qrcode": "^1.5.3",
|
||||
"p-queue": "^8.0.1"
|
||||
"p-queue": "^8.0.1",
|
||||
"ws": "^8.17.0"
|
||||
}
|
||||
}
|
||||
|
138
utils/lagrange-adapter.js
Normal file
138
utils/lagrange-adapter.js
Normal file
@ -0,0 +1,138 @@
|
||||
import { randomUUID } from 'crypto'
|
||||
import path from "path";
|
||||
import fs from 'fs'
|
||||
import { WebSocket } from 'ws'
|
||||
|
||||
export class LagrangeAdapter {
|
||||
/**
|
||||
* 构造拉格朗日适配器
|
||||
* @param wsAddr 形如:ws://127.0.0.1:9091/
|
||||
*/
|
||||
constructor(wsAddr) {
|
||||
this.ws = new WebSocket(wsAddr)
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传群文件
|
||||
* @param bot_id - 云崽机器人id
|
||||
* @param group_id - 群号
|
||||
* @param file - 文件所在位置
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async uploadGroupFile(bot_id, group_id, file) {
|
||||
file = await this.formatFile(file)
|
||||
if (!file.match(/^file:\/\//)) {
|
||||
file = await this.fileToPath(file)
|
||||
file = await this.formatFile(file)
|
||||
}
|
||||
file = file.replace(/^file:\/\//, '')
|
||||
const name = path.basename(file) || Date.now() + path.extname(file)
|
||||
logger.info("[R插件][拉格朗日适配器] 连接到拉格朗日");
|
||||
logger.info(bot_id, group_id, file, name);
|
||||
this.ws.on("open", () => {
|
||||
this.upload_private_file_api(bot_id, group_id, file, name);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传群文件的拉格朗日API
|
||||
* @param {string} id - 机器人QQ 通过e.bot、Bot调用无需传入
|
||||
* @param {number} group_id - 群号
|
||||
* @param {string} file - 本地文件路径
|
||||
* @param {string} name - 储存名称
|
||||
* @param {string} folder - 目标文件夹 默认群文件根目录
|
||||
*/
|
||||
async upload_private_file_api(id, group_id, file, name, folder = '/') {
|
||||
const params = { group_id, file, name, folder }
|
||||
const echo = randomUUID()
|
||||
/** 序列化 */
|
||||
const log = JSON.stringify({ echo, action: "upload_group_file", params })
|
||||
logger.info("[R插件][拉格朗日适配器] 发送视频中...");
|
||||
/** 发送到拉格朗日 */
|
||||
this.ws.send(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理segment中的i||i.file,主要用于一些sb字段,标准化他们
|
||||
* @param {string|object} file - i.file
|
||||
*/
|
||||
async formatFile(file) {
|
||||
const str = function () {
|
||||
if (file.includes('gchat.qpic.cn') && !file.startsWith('https://')) {
|
||||
return `https://${ file }`
|
||||
} else if (file.startsWith('base64://')) {
|
||||
return file
|
||||
} else if (file.startsWith('http://') || file.startsWith('https://')) {
|
||||
return file
|
||||
} else if (fs.existsSync(path.resolve(file.replace(/^file:\/\//, '')))) {
|
||||
return `file://${ path.resolve(file.replace(/^file:\/\//, '')) }`
|
||||
} else if (fs.existsSync(path.resolve(file.replace(/^file:\/\/\//, '')))) {
|
||||
return `file://${ path.resolve(file.replace(/^file:\/\/\//, '')) }`
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
switch (typeof file) {
|
||||
case 'object':
|
||||
/** 这里会有复读这样的直接原样不动把message发过来... */
|
||||
if (file.url) {
|
||||
if (file?.url?.includes('gchat.qpic.cn') && !file?.url?.startsWith('https://')) return `https://${ file.url }`
|
||||
return file.url
|
||||
}
|
||||
|
||||
/** 老插件渲染出来的图有这个字段 */
|
||||
if (file?.type === 'Buffer') return Buffer.from(file?.data)
|
||||
if (Buffer.isBuffer(file) || file instanceof Uint8Array) return file
|
||||
|
||||
/** 流 */
|
||||
if (file instanceof fs.ReadStream) return await Bot.Stream(file, { base: true })
|
||||
|
||||
/** i.file */
|
||||
if (file.file) return str(file.file)
|
||||
return file
|
||||
case 'string':
|
||||
return str(file)
|
||||
default:
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入文件,返回本地路径
|
||||
* 可以是http://、file://、base64://、buffer
|
||||
* @param {file://|base64://|http://|buffer} file
|
||||
* @param {string} _path - 可选,不传默认为图片
|
||||
*/
|
||||
async fileToPath(file, _path) {
|
||||
if (!_path) _path = `./temp/FileToUrl/${ Date.now() }.png`
|
||||
if (Buffer.isBuffer(file) || file instanceof Uint8Array) {
|
||||
fs.writeFileSync(_path, file)
|
||||
return _path
|
||||
} else if (file instanceof fs.ReadStream) {
|
||||
const buffer = await Bot.Stream(file)
|
||||
fs.writeFileSync(_path, buffer)
|
||||
return _path
|
||||
} else if (fs.existsSync(file.replace(/^file:\/\//, ''))) {
|
||||
fs.copyFileSync(file.replace(/^file:\/\//, ''), _path)
|
||||
return _path
|
||||
} else if (fs.existsSync(file.replace(/^file:\/\/\//, ''))) {
|
||||
fs.copyFileSync(file.replace(/^file:\/\/\//, ''), _path)
|
||||
return _path
|
||||
} else if (file.startsWith('base64://')) {
|
||||
const buffer = Buffer.from(file.replace(/^base64:\/\//, ''), 'base64')
|
||||
fs.writeFileSync(_path, buffer)
|
||||
return _path
|
||||
} else if (/^http(s)?:\/\//.test(file)) {
|
||||
const res = await fetch(file)
|
||||
if (!res.ok) {
|
||||
throw new Error(`请求错误!状态码: ${ res.status }`)
|
||||
} else {
|
||||
const buffer = Buffer.from(await res.arrayBuffer())
|
||||
fs.writeFileSync(_path, buffer)
|
||||
return _path
|
||||
}
|
||||
} else {
|
||||
throw new Error('传入的文件类型不符合规则,只接受url、buffer、file://路径或者base64编码的图片')
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user