mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 08:09:19 +00:00
🎈 pref: 优化代码结构和功能表现
- 移除`checkCommandExists`函数,由`checkToolInCurEnv`全面替代,以适应不同操作系统的命令检测需求 - 简化`generateRandomStr`函数,采用数组方法生成随机字符串,提高代码简洁性 - 优化`downloadImg`和相关函数,统一使用axios进行图片下载,移除冗余的fetch调用 - 精简和统一文件路径拼接操作,避免硬编码路径分隔符,增强代码的可移植性 - 优化正则表达式和字符串处理,提高代码的执行效率和可读性 - 优化代理设置和错误处理逻辑,确保在各种网络环境下的稳定性和可靠性 - 优化视频下载逻辑,通过`aria2c`和`axel`命令增加下载效率和稳定性 - 优化图片和视频文件命名逻辑,避免文件覆盖并提高文件管理效率 - 优化代码中注释的使用,提供更清晰的功能说明和维护文档
This commit is contained in:
parent
747e22453f
commit
d0082372b0
294
apps/tools.js
294
apps/tools.js
@ -69,7 +69,6 @@ import {
|
||||
import { getWbi } from "../utils/biliWbi.js";
|
||||
import { getBodianAudio, getBodianMusicInfo, getBodianMv } from "../utils/bodian.js";
|
||||
import {
|
||||
checkCommandExists,
|
||||
checkToolInCurEnv,
|
||||
cleanFilename,
|
||||
downloadAudio,
|
||||
@ -125,7 +124,7 @@ export class tools extends plugin {
|
||||
priority: 300,
|
||||
rule: [
|
||||
{
|
||||
reg: `^(翻|trans)[${ tools.Constants.existsTransKey }]`,
|
||||
reg: `^(翻|trans)[${tools.Constants.existsTransKey}]`,
|
||||
fnc: "trans",
|
||||
},
|
||||
{
|
||||
@ -228,7 +227,7 @@ export class tools extends plugin {
|
||||
// 魔法接口
|
||||
this.proxyAddr = this.toolsConfig.proxyAddr;
|
||||
this.proxyPort = this.toolsConfig.proxyPort;
|
||||
this.myProxy = `http://${ this.proxyAddr }:${ this.proxyPort }`;
|
||||
this.myProxy = `http://${this.proxyAddr}:${this.proxyPort}`;
|
||||
// 加载识别前缀
|
||||
this.identifyPrefix = this.toolsConfig.identifyPrefix;
|
||||
// 加载哔哩哔哩配置
|
||||
@ -300,7 +299,7 @@ export class tools extends plugin {
|
||||
const res = await this.douyinRequest(douUrl);
|
||||
// 当前版本需要填入cookie
|
||||
if (_.isEmpty(this.douyinCookie)) {
|
||||
e.reply(`检测到没有Cookie,无法解析抖音${ HELP_DOC }`);
|
||||
e.reply(`检测到没有Cookie,无法解析抖音${HELP_DOC}`);
|
||||
return;
|
||||
}
|
||||
const douId = /note\/(\d+)/g.exec(res)?.[1] || /video\/(\d+)/g.exec(res)?.[1];
|
||||
@ -321,8 +320,8 @@ export class tools extends plugin {
|
||||
headers["User-Agent"],
|
||||
);
|
||||
// const param = resp.data.result[0].paramsencode;
|
||||
const resDyApi = `${ dyApi }&a_bogus=${ abParam }`;
|
||||
headers['Referer'] = `https://www.douyin.com/video/${ douId }`
|
||||
const resDyApi = `${dyApi}&a_bogus=${abParam}`;
|
||||
headers['Referer'] = `https://www.douyin.com/video/${douId}`
|
||||
// 定义一个dy请求
|
||||
const dyResponse = () => axios.get(resDyApi, {
|
||||
headers,
|
||||
@ -349,20 +348,20 @@ export class tools extends plugin {
|
||||
const dyDuration = Math.trunc(duration / 1000);
|
||||
const durationThreshold = this.biliDuration;
|
||||
// 一些共同发送内容
|
||||
let dySendContent = `${ this.identifyPrefix } 识别:抖音,${ item.author.nickname }\n📝 简介:${ item.desc }`;
|
||||
let dySendContent = `${this.identifyPrefix} 识别:抖音,${item.author.nickname}\n📝 简介:${item.desc}`;
|
||||
if (dyDuration >= durationThreshold) {
|
||||
// 超过阈值,不发送的情况
|
||||
// 封面
|
||||
const dyCover = cover.url_list?.pop();
|
||||
// logger.info(cover.url_list);
|
||||
dySendContent += `\n
|
||||
${ DIVIDING_LINE.replace('{}', '限制说明') }\n当前视频时长约:${ Math.trunc(dyDuration / 60) }分钟,\n大于管理员设置的最大时长 ${ durationThreshold / 60 } 分钟!`;
|
||||
${DIVIDING_LINE.replace('{}', '限制说明')}\n当前视频时长约:${Math.trunc(dyDuration / 60)}分钟,\n大于管理员设置的最大时长 ${durationThreshold / 60} 分钟!`;
|
||||
e.reply([segment.image(dyCover), dySendContent]);
|
||||
// 如果开启评论的就调用
|
||||
await this.douyinComment(e, douId, headers);
|
||||
return;
|
||||
}
|
||||
e.reply(`${ dySendContent }`);
|
||||
e.reply(`${dySendContent}`);
|
||||
// 分辨率判断是否压缩
|
||||
const resolution = this.douyinCompression ? "720p" : "1080p";
|
||||
// 使用今日头条 CDN 进一步加快解析速度
|
||||
@ -381,7 +380,7 @@ export class tools extends plugin {
|
||||
}*/
|
||||
|
||||
// logger.info(resUrl);
|
||||
const path = `${ this.getCurDownloadPath(e) }/temp.mp4`;
|
||||
const path = `${this.getCurDownloadPath(e)}/temp.mp4`;
|
||||
// 加入队列
|
||||
this.queue.add(async () => {
|
||||
await this.downloadVideo(resUrl).then(() => {
|
||||
@ -390,7 +389,7 @@ export class tools extends plugin {
|
||||
})
|
||||
} else if (urlType === "image") {
|
||||
// 发送描述
|
||||
e.reply(`${ this.identifyPrefix } 识别:抖音, ${ item.desc }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:抖音, ${item.desc}`);
|
||||
// 无水印图片列表
|
||||
let no_watermark_image_list = [];
|
||||
// 有水印图片列表
|
||||
@ -413,7 +412,7 @@ export class tools extends plugin {
|
||||
await this.douyinComment(e, douId, headers);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
logger.mark(`Cookie 过期或者 Cookie 没有填写,请参考\n${ HELP_DOC }\n尝试无效后可以到官方QQ群[575663150]提出 bug 等待解决`)
|
||||
logger.mark(`Cookie 过期或者 Cookie 没有填写,请参考\n${HELP_DOC}\n尝试无效后可以到官方QQ群[575663150]提出 bug 等待解决`)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -433,7 +432,7 @@ export class tools extends plugin {
|
||||
new URLSearchParams(new URL(dyCommentUrl).search).toString(),
|
||||
headers["User-Agent"],
|
||||
);
|
||||
const commentsResp = await axios.get(`${ dyCommentUrl }&a_bogus=${ abParam }`, {
|
||||
const commentsResp = await axios.get(`${dyCommentUrl}&a_bogus=${abParam}`, {
|
||||
headers
|
||||
})
|
||||
// logger.info(headers)
|
||||
@ -468,9 +467,9 @@ export class tools extends plugin {
|
||||
const path = this.getCurDownloadPath(e);
|
||||
await checkAndRemoveFile(path + "/temp.mp4");
|
||||
const title = ytDlpGetTilt(url, isOversea, this.myProxy);
|
||||
e.reply(`${ this.identifyPrefix } 识别:TikTok,视频下载中请耐心等待 \n${ title }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:TikTok,视频下载中请耐心等待 \n${title}`);
|
||||
await ytDlpHelper(path, cleanedTiktokUrl, isOversea, this.myProxy);
|
||||
await this.sendVideoToUpload(e, `${ path }/temp.mp4`);
|
||||
await this.sendVideoToUpload(e, `${path}/temp.mp4`);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -485,7 +484,7 @@ export class tools extends plugin {
|
||||
// 检查路径是否存在文件夹
|
||||
await mkdirIfNotExists(this.defaultPath);
|
||||
// 发送请求
|
||||
const saveCodePath = `${ this.defaultPath }qrcode.png`;
|
||||
const saveCodePath = `${this.defaultPath}qrcode.png`;
|
||||
|
||||
const { SESSDATA, refresh_token } = await getScanCodeData(saveCodePath, 8, () => imgSendHook(e, saveCodePath))
|
||||
|
||||
@ -502,7 +501,7 @@ export class tools extends plugin {
|
||||
let url = e.msg === undefined ? e.message.shift().data.replaceAll("\\", "") : e.msg.trim().replaceAll("\\", "");
|
||||
// 直接发送BV号的处理
|
||||
if (/^BV[1-9a-zA-Z]{10}$/.exec(url)?.[0]) {
|
||||
url = `https://www.bilibili.com/video/${ url }`;
|
||||
url = `https://www.bilibili.com/video/${url}`;
|
||||
logger.info(url)
|
||||
}
|
||||
// 短号处理
|
||||
@ -538,7 +537,7 @@ export class tools extends plugin {
|
||||
e.reply([
|
||||
segment.image(user_cover),
|
||||
segment.image(keyframe),
|
||||
`${ this.identifyPrefix } 识别:哔哩哔哩直播,${ title }${ description ? `\n\n简述:${ description }\n` : '' }${ tags ? `标签:${ tags }\n` : '' }`
|
||||
`${this.identifyPrefix} 识别:哔哩哔哩直播,${title}${description ? `\n\n简述:${description}\n` : ''}${tags ? `标签:${tags}\n` : ''}`
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
@ -557,7 +556,7 @@ export class tools extends plugin {
|
||||
return true;
|
||||
}
|
||||
// 创建文件,如果不存在,
|
||||
const path = `${ this.getCurDownloadPath(e) }/`;
|
||||
const path = `${this.getCurDownloadPath(e)}/`;
|
||||
await mkdirIfNotExists(path);
|
||||
// 处理番剧
|
||||
if (url.includes("play\/ep") || url.includes("play\/ss")) {
|
||||
@ -567,7 +566,7 @@ export class tools extends plugin {
|
||||
// 加入队列
|
||||
this.queue.add(async () => {
|
||||
// 下载文件
|
||||
await this.biliDownloadStrategy(e, `https://www.bilibili.com/bangumi/play/ep${ ep }`, path);
|
||||
await this.biliDownloadStrategy(e, `https://www.bilibili.com/bangumi/play/ep${ep}`, path);
|
||||
})
|
||||
}
|
||||
return true;
|
||||
@ -598,8 +597,8 @@ export class tools extends plugin {
|
||||
// 拼接在线人数
|
||||
const onlineTotal = await this.biliOnlineTotal(bvid, cid);
|
||||
// 格式化数据
|
||||
const combineContent = `\n${ formatBiliInfo(dataProcessMap) }\n📝 简介:${ truncateString(filteredDesc, this.toolsConfig.biliIntroLenLimit || BILI_DEFAULT_INTRO_LEN_LIMIT) }\n🏄♂️️ 当前视频有 ${ onlineTotal.total } 人在观看,其中 ${ onlineTotal.count } 人在网页端观看`;
|
||||
let biliInfo = [`${ this.identifyPrefix } 识别:哔哩哔哩:${ title }`, combineContent]
|
||||
const combineContent = `\n${formatBiliInfo(dataProcessMap)}\n📝 简介:${truncateString(filteredDesc, this.toolsConfig.biliIntroLenLimit || BILI_DEFAULT_INTRO_LEN_LIMIT)}\n🏄♂️️ 当前视频有 ${onlineTotal.total} 人在观看,其中 ${onlineTotal.count} 人在网页端观看`;
|
||||
let biliInfo = [`${this.identifyPrefix} 识别:哔哩哔哩:${title}`, combineContent]
|
||||
// 加入图片
|
||||
biliInfo.unshift(segment.image(pic));
|
||||
// 总结
|
||||
@ -612,7 +611,7 @@ export class tools extends plugin {
|
||||
// 限制视频解析
|
||||
if (isLimitDuration) {
|
||||
const durationInMinutes = (curDuration / 60).toFixed(0);
|
||||
biliInfo.push(`${ DIVIDING_LINE.replace('{}', '限制说明') }\n当前视频时长约:${ durationInMinutes }分钟,\n大于管理员设置的最大时长 ${ this.biliDuration / 60 } 分钟!`);
|
||||
biliInfo.push(`${DIVIDING_LINE.replace('{}', '限制说明')}\n当前视频时长约:${durationInMinutes}分钟,\n大于管理员设置的最大时长 ${this.biliDuration / 60} 分钟!`);
|
||||
Msg && (await e.reply(Msg));
|
||||
e.reply(biliInfo);
|
||||
return true;
|
||||
@ -670,10 +669,10 @@ export class tools extends plugin {
|
||||
const { shortLink, shortLink2 } = await this.biliAnimeCacheDetect(result.title, result.cover);
|
||||
e.reply([
|
||||
segment.image(resp.result.cover),
|
||||
`${ this.identifyPrefix }识别:哔哩哔哩番剧,${ result.title }\n🎯 评分: ${ result?.rating?.score ?? '-' } / ${ result?.rating?.count ?? '-' }\n📺 ${ result.new_ep.desc }, ${ result.seasons[0].new_ep.index_show }\n`,
|
||||
`${ formatBiliInfo(dataProcessMap) }`,
|
||||
`\n\n🪶 在线观看: ${ await urlTransformShortLink(ANIME_SERIES_SEARCH_LINK + title) }`,
|
||||
`\n🌸 在线观看: ${ await urlTransformShortLink(ANIME_SERIES_SEARCH_LINK2 + title) }`
|
||||
`${this.identifyPrefix}识别:哔哩哔哩番剧,${result.title}\n🎯 评分: ${result?.rating?.score ?? '-'} / ${result?.rating?.count ?? '-'}\n📺 ${result.new_ep.desc}, ${result.seasons[0].new_ep.index_show}\n`,
|
||||
`${formatBiliInfo(dataProcessMap)}`,
|
||||
`\n\n🪶 在线观看: ${await urlTransformShortLink(ANIME_SERIES_SEARCH_LINK + title)}`,
|
||||
`\n🌸 在线观看: ${await urlTransformShortLink(ANIME_SERIES_SEARCH_LINK2 + title)}`
|
||||
], true);
|
||||
return ep;
|
||||
}
|
||||
@ -688,7 +687,7 @@ export class tools extends plugin {
|
||||
async biliDownloadStrategy(e, url, path) {
|
||||
// =================以下是调用BBDown的逻辑=====================
|
||||
// 下载视频和音频
|
||||
const tempPath = `${ path }temp`;
|
||||
const tempPath = `${path}temp`;
|
||||
// 检测是否开启BBDown
|
||||
if (this.biliUseBBDown) {
|
||||
// 检测环境的 BBDown
|
||||
@ -696,7 +695,7 @@ export class tools extends plugin {
|
||||
// 存在 BBDown
|
||||
if (isExistBBDown) {
|
||||
// 删除之前的文件
|
||||
await checkAndRemoveFile(`${ tempPath }.mp4`);
|
||||
await checkAndRemoveFile(`${tempPath}.mp4`);
|
||||
// 下载视频
|
||||
await startBBDown(url, path, {
|
||||
biliSessData: this.biliSessData,
|
||||
@ -704,7 +703,7 @@ export class tools extends plugin {
|
||||
biliCDN: BILI_CDN_SELECT_LIST.find(item => item.value === this.biliCDN)?.sign,
|
||||
});
|
||||
// 发送视频
|
||||
return this.sendVideoToUpload(e, `${ tempPath }.mp4`);
|
||||
return this.sendVideoToUpload(e, `${tempPath}.mp4`);
|
||||
}
|
||||
e.reply("🚧 R插件提醒你:开启但未检测到当前环境有【BBDown】,即将使用默认下载方式 ( ◡̀_◡́)ᕤ");
|
||||
}
|
||||
@ -716,7 +715,7 @@ export class tools extends plugin {
|
||||
await this.downBili(tempPath, data.videoUrl, data.audioUrl);
|
||||
|
||||
// 上传视频
|
||||
return this.sendVideoToUpload(e, `${ tempPath }.mp4`);
|
||||
return this.sendVideoToUpload(e, `${tempPath}.mp4`);
|
||||
} catch (err) {
|
||||
// 错误处理
|
||||
logger.error('[R插件][哔哩哔哩视频发送]下载错误,具体原因为:', err);
|
||||
@ -764,7 +763,7 @@ export class tools extends plugin {
|
||||
const dynamicId = /[^/]+(?!.*\/)/.exec(url)[0];
|
||||
getDynamic(dynamicId, session).then(async resp => {
|
||||
if (resp.dynamicSrc.length > 0) {
|
||||
e.reply(`${ this.identifyPrefix } 识别:哔哩哔哩动态, ${ resp.dynamicDesc }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:哔哩哔哩动态, ${resp.dynamicDesc}`);
|
||||
let dynamicSrcMsg = [];
|
||||
resp.dynamicSrc.forEach(item => {
|
||||
dynamicSrcMsg.push({
|
||||
@ -797,7 +796,7 @@ export class tools extends plugin {
|
||||
wbi = await getWbi({ bvid, cid, up_mid }, this.biliSessData);
|
||||
}
|
||||
// 构造API
|
||||
const summaryUrl = `${ BILI_SUMMARY }?${ wbi }`;
|
||||
const summaryUrl = `${BILI_SUMMARY}?${wbi}`;
|
||||
logger.info(summaryUrl)
|
||||
// 构造结果:https://api.bilibili.com/x/web-interface/view/conclusion/get?bvid=BV1L94y1H7CV&cid=1335073288&up_mid=297242063&wts=1701546363&w_rid=1073871926b3ccd99bd790f0162af634
|
||||
return axios.get(summaryUrl)
|
||||
@ -809,7 +808,7 @@ export class tools extends plugin {
|
||||
let resReply = "";
|
||||
// 总体总结
|
||||
if (summary) {
|
||||
resReply = `\n摘要:${ summary }\n`
|
||||
resReply = `\n摘要:${summary}\n`
|
||||
}
|
||||
// 分段总结
|
||||
if (outline) {
|
||||
@ -820,9 +819,9 @@ export class tools extends plugin {
|
||||
const specificContent = keyPoint.map(point => {
|
||||
const { timestamp, content } = point
|
||||
const specificTime = secondsToTime(timestamp)
|
||||
return `${ specificTime } ${ content }\n`;
|
||||
return `${specificTime} ${content}\n`;
|
||||
}).join("");
|
||||
return `- ${ smallTitle }\n${ specificContent }\n`;
|
||||
return `- ${smallTitle}\n${specificContent}\n`;
|
||||
});
|
||||
resReply += specificTimeSummary.join("");
|
||||
}
|
||||
@ -836,15 +835,18 @@ export class tools extends plugin {
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
async getBiliStream(liveId) {
|
||||
return axios.get(`${ BILI_STREAM_INFO }?room_id=${ liveId }`, {
|
||||
return axios.get(`${BILI_STREAM_INFO}?room_id=${liveId}`, {
|
||||
headers: {
|
||||
'User-Agent': COMMON_USER_AGENT,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 例子:https://twitter.com/chonkyanimalx/status/1595834168000204800
|
||||
/**
|
||||
* @deprecated Use newFunction instead.
|
||||
*/
|
||||
async twitter(e) {
|
||||
console.warn('警告: 函数已弃用,将在未来版本中移除');
|
||||
// 配置参数及解析
|
||||
const reg = /https?:\/\/twitter.com\/[0-9-a-zA-Z_]{1,20}\/status\/([0-9]*)/;
|
||||
const twitterUrl = reg.exec(e.msg);
|
||||
@ -862,14 +864,14 @@ export class tools extends plugin {
|
||||
await fetch(TWITTER_TWEET_INFO.replace("{}", id), {
|
||||
headers: {
|
||||
"User-Agent": "v2TweetLookupJS",
|
||||
"authorization": `Bearer ${ Buffer.from(TWITTER_BEARER_TOKEN, "base64").toString() }`
|
||||
"authorization": `Bearer ${Buffer.from(TWITTER_BEARER_TOKEN, "base64").toString()}`
|
||||
},
|
||||
...params,
|
||||
agent: !isOversea ? '' : new HttpsProxyAgent(this.myProxy),
|
||||
}).then(async resp => {
|
||||
logger.info(resp)
|
||||
e.reply(`${ this.identifyPrefix } 识别:小蓝鸟学习版,${ resp.data.text }`);
|
||||
const downloadPath = `${ this.getCurDownloadPath(e) }`;
|
||||
e.reply(`${this.identifyPrefix} 识别:小蓝鸟学习版,${resp.data.text}`);
|
||||
const downloadPath = `${this.getCurDownloadPath(e)}`;
|
||||
// 创建文件夹(如果没有过这个群)
|
||||
if (!fs.existsSync(downloadPath)) {
|
||||
mkdirsSync(downloadPath);
|
||||
@ -884,7 +886,7 @@ export class tools extends plugin {
|
||||
// 视频
|
||||
await this.downloadVideo(resp.includes.media[0].variants[0].url, true).then(
|
||||
_ => {
|
||||
e.reply(segment.video(`${ downloadPath }/temp.mp4`));
|
||||
e.reply(segment.video(`${downloadPath}/temp.mp4`));
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -934,7 +936,7 @@ export class tools extends plugin {
|
||||
}
|
||||
// 提取视频
|
||||
let videoUrl = GENERAL_REQ_LINK.link.replace("{}", twitterUrl);
|
||||
e.reply(`${ this.identifyPrefix } 识别:小蓝鸟学习版`);
|
||||
e.reply(`${this.identifyPrefix} 识别:小蓝鸟学习版`);
|
||||
const config = {
|
||||
headers: {
|
||||
'Accept': 'ext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
@ -984,21 +986,21 @@ export class tools extends plugin {
|
||||
|
||||
// acfun解析
|
||||
async acfun(e) {
|
||||
const path = `${ this.getCurDownloadPath(e) }/temp/`;
|
||||
const path = `${this.getCurDownloadPath(e)}/temp/`;
|
||||
await mkdirIfNotExists(path);
|
||||
|
||||
let inputMsg = e.msg;
|
||||
// 适配手机分享:https://m.acfun.cn/v/?ac=32838812&sid=d2b0991bd6ad9c09
|
||||
if (inputMsg.includes("m.acfun.cn")) {
|
||||
inputMsg = `https://www.acfun.cn/v/ac${ /ac=([^&?]*)/.exec(inputMsg)[1] }`;
|
||||
inputMsg = `https://www.acfun.cn/v/ac${/ac=([^&?]*)/.exec(inputMsg)[1]}`;
|
||||
}
|
||||
|
||||
parseUrl(inputMsg).then(res => {
|
||||
e.reply(`${ this.identifyPrefix } 识别:猴山,${ res.videoName }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:猴山,${res.videoName}`);
|
||||
parseM3u8(res.urlM3u8s[res.urlM3u8s.length - 1]).then(res2 => {
|
||||
downloadM3u8Videos(res2.m3u8FullUrls, path).then(_ => {
|
||||
mergeAcFileToMp4(res2.tsNames, path, `${ path }out.mp4`).then(_ => {
|
||||
this.sendVideoToUpload(e, `${ path }out.mp4`)
|
||||
mergeAcFileToMp4(res2.tsNames, path, `${path}out.mp4`).then(_ => {
|
||||
this.sendVideoToUpload(e, `${path}out.mp4`)
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1037,14 +1039,14 @@ export class tools extends plugin {
|
||||
} else {
|
||||
id = /explore\/(\w+)/.exec(msgUrl)?.[1] || /discovery\/item\/(\w+)/.exec(msgUrl)?.[1];
|
||||
}
|
||||
const downloadPath = `${ this.getCurDownloadPath(e) }`;
|
||||
const downloadPath = `${this.getCurDownloadPath(e)}`;
|
||||
// 检测没有 cookie 则退出
|
||||
if (_.isEmpty(this.xiaohongshuCookie)) {
|
||||
e.reply(`2024-8-2后反馈必须使用ck,不然无法解析请填写相关ck\n${ HELP_DOC }`);
|
||||
e.reply(`2024-8-2后反馈必须使用ck,不然无法解析请填写相关ck\n${HELP_DOC}`);
|
||||
return;
|
||||
}
|
||||
// 获取信息
|
||||
fetch(`${ XHS_REQ_LINK }${ id }`, {
|
||||
fetch(`${XHS_REQ_LINK}${id}`, {
|
||||
headers: XHS_NO_WATERMARK_HEADER,
|
||||
}).then(async resp => {
|
||||
const xhsHtml = await resp.text();
|
||||
@ -1056,7 +1058,7 @@ export class tools extends plugin {
|
||||
if (type === "video") {
|
||||
// 封面
|
||||
const cover = noteData.imageList?.[0].urlDefault;
|
||||
e.reply([segment.image(cover), `${ this.identifyPrefix } 识别:小红书, ${ title }\n${ desc }`]);
|
||||
e.reply([segment.image(cover), `${this.identifyPrefix} 识别:小红书, ${title}\n${desc}`]);
|
||||
// ⚠️ (暂时废弃)构造xhs视频链接(有水印)
|
||||
const xhsVideoUrl = noteData.video.media.stream.h264?.[0]?.masterUrl;
|
||||
|
||||
@ -1066,20 +1068,20 @@ export class tools extends plugin {
|
||||
this.downloadVideo(xhsVideoUrl).then(path => {
|
||||
if (path === undefined) {
|
||||
// 创建文件,如果不存在
|
||||
path = `${ this.getCurDownloadPath(e) }/`;
|
||||
path = `${this.getCurDownloadPath(e)}/`;
|
||||
}
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
return true;
|
||||
} else if (type === "normal") {
|
||||
e.reply(`${ this.identifyPrefix } 识别:小红书, ${ title }\n${ desc }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:小红书, ${title}\n${desc}`);
|
||||
const imagePromises = [];
|
||||
// 使用 for..of 循环处理异步下载操作
|
||||
for (let [index, item] of noteData.imageList.entries()) {
|
||||
imagePromises.push(downloadImg({
|
||||
img: item.urlDefault,
|
||||
dir: downloadPath,
|
||||
fileName: `${ index }.png`,
|
||||
fileName: `${index}.png`,
|
||||
downloadMethod: this.biliDownloadMethod,
|
||||
}));
|
||||
}
|
||||
@ -1114,14 +1116,14 @@ export class tools extends plugin {
|
||||
/(?=mvId).*?(?=&)/.exec(e.msg.trim())?.[0].replace("mvId=", "");
|
||||
const { name, album, artist, albumPic120, categorys } = await getBodianMusicInfo(id);
|
||||
e.reply([
|
||||
`${ this.identifyPrefix } 识别:波点音乐,${ name }-${ album }-${ artist }\n标签:${ categorys
|
||||
`${this.identifyPrefix} 识别:波点音乐,${name}-${album}-${artist}\n标签:${categorys
|
||||
.map(item => item.name)
|
||||
.join(" | ") }`,
|
||||
.join(" | ")}`,
|
||||
segment.image(albumPic120),
|
||||
]);
|
||||
if (e.msg.includes("musicId")) {
|
||||
const path = `${ this.getCurDownloadPath(e) }`;
|
||||
await getBodianAudio(id, path, `${ name }-${ artist }`).then(sendPath => {
|
||||
const path = `${this.getCurDownloadPath(e)}`;
|
||||
await getBodianAudio(id, path, `${name}-${artist}`).then(sendPath => {
|
||||
// 发送语音
|
||||
e.reply(segment.record(sendPath));
|
||||
// 上传群文件
|
||||
@ -1189,11 +1191,11 @@ export class tools extends plugin {
|
||||
})
|
||||
]);
|
||||
const { name: mvName, artistName: mvArtist, cover: mvCover } = mvDetailData.data?.data;
|
||||
e.reply([segment.image(mvCover), `${ this.identifyPrefix } 识别:网易云MV,${ mvName } - ${ mvArtist }`]);
|
||||
e.reply([segment.image(mvCover), `${this.identifyPrefix} 识别:网易云MV,${mvName} - ${mvArtist}`]);
|
||||
// logger.info(mvUrlData.data)
|
||||
const { url: mvUrl } = mvUrlData.data?.data;
|
||||
this.downloadVideo(mvUrl).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -1212,14 +1214,14 @@ export class tools extends plugin {
|
||||
// 获取歌曲信息
|
||||
let title = await axios.get(AUTO_NETEASE_SONG_DETAIL.replace("{}", id)).then(res => {
|
||||
const song = res.data.songs[0];
|
||||
return `${ song?.name }-${ song?.ar?.[0].name }`.replace(/[\/\?<>\\:\*\|".… ]/g, "");
|
||||
return `${song?.name}-${song?.ar?.[0].name}`.replace(/[\/\?<>\\:\*\|".… ]/g, "");
|
||||
});
|
||||
// 一般这个情况是VIP歌曲 (如果没有url或者是国内, 国内全走临时接口,后续如果不要删除逻辑'!isOversea ||')
|
||||
if (!isOversea || url == null) {
|
||||
url = await this.musicTempApi(e, title, "网易云音乐");
|
||||
} else {
|
||||
// 不是VIP歌曲,直接识别完就下一步
|
||||
e.reply(`${ this.identifyPrefix } 识别:网易云音乐,${ title }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:网易云音乐,${title}`);
|
||||
}
|
||||
// 动态判断后缀名
|
||||
const extensionPattern = /\.([a-zA-Z0-9]+)$/;
|
||||
@ -1233,7 +1235,7 @@ export class tools extends plugin {
|
||||
// 删除文件
|
||||
await checkAndRemoveFile(path);
|
||||
}).catch(err => {
|
||||
logger.error(`下载音乐失败,错误信息为: ${ err.message }`);
|
||||
logger.error(`下载音乐失败,错误信息为: ${err.message}`);
|
||||
});
|
||||
});
|
||||
return true;
|
||||
@ -1257,7 +1259,7 @@ export class tools extends plugin {
|
||||
// ??后的内容是适配`QQ_MUSIC_TEMP_API`、最后是汽水
|
||||
const url = vipMusicData.data?.mp3 ?? vipMusicData.data?.data?.url ?? vipMusicData.data?.music;
|
||||
const cover = vipMusicData.data?.img ?? vipMusicData.data?.data?.cover ?? vipMusicData.data?.cover;
|
||||
await e.reply([segment.image(cover), `${ this.identifyPrefix } 识别:${ musicType },${ messageTitle }`]);
|
||||
await e.reply([segment.image(cover), `${this.identifyPrefix} 识别:${musicType},${messageTitle}`]);
|
||||
return url;
|
||||
}
|
||||
|
||||
@ -1293,7 +1295,7 @@ export class tools extends plugin {
|
||||
.then(async resp => {
|
||||
const wbData = resp.data.data;
|
||||
const { text, status_title, source, region_name, pics, page_info } = wbData;
|
||||
e.reply(`${ this.identifyPrefix } 识别:微博,${ text.replace(/<[^>]+>/g, '') }\n${ status_title }\n${ source }\t${ region_name ?? '' }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:微博,${text.replace(/<[^>]+>/g, '')}\n${status_title}\n${source}\t${region_name ?? ''}`);
|
||||
if (pics) {
|
||||
// 下载图片并格式化消息
|
||||
const imagesPromise = pics.map(item => {
|
||||
@ -1337,7 +1339,7 @@ export class tools extends plugin {
|
||||
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
|
||||
"referer": "https://weibo.com/",
|
||||
}, 1).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
} catch (err) {
|
||||
e.reply("视频资源获取失败");
|
||||
@ -1356,7 +1358,7 @@ export class tools extends plugin {
|
||||
async general(e) {
|
||||
try {
|
||||
const adapter = await GeneralLinkAdapter.create(e.msg);
|
||||
e.reply(`${ this.identifyPrefix } 识别:${ adapter.name }${ adapter.desc ? `, ${ adapter.desc }` : '' }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:${adapter.name}${adapter.desc ? `, ${adapter.desc}` : ''}`);
|
||||
logger.mark(adapter);
|
||||
if (adapter.images && adapter.images.length > 0) {
|
||||
const images = adapter.images.map(item => {
|
||||
@ -1371,7 +1373,7 @@ export class tools extends plugin {
|
||||
// 视频:https://www.kuaishou.com/short-video/3xhjgcmir24m4nm
|
||||
const url = adapter.video;
|
||||
this.downloadVideo(url).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
} else {
|
||||
e.reply("解析失败:无法获取到资源");
|
||||
@ -1402,9 +1404,9 @@ export class tools extends plugin {
|
||||
const path = this.getCurDownloadPath(e);
|
||||
await checkAndRemoveFile(path + "/temp.mp4")
|
||||
const title = await ytDlpGetTilt(url, isOversea, this.myProxy);
|
||||
e.reply(`${ this.identifyPrefix } 识别:油管,视频下载中请耐心等待 \n${ title }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:油管,视频下载中请耐心等待 \n${title}`);
|
||||
await ytDlpHelper(path, url, isOversea, this.myProxy, true);
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`);
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw error; // Rethrow the error so it can be handled by the caller
|
||||
@ -1450,7 +1452,7 @@ export class tools extends plugin {
|
||||
} catch (e) {
|
||||
realContent = content;
|
||||
}
|
||||
const normalMsg = `${ this.identifyPrefix } 识别:米游社,${ subject }\n${ realContent?.describe || "" }`;
|
||||
const normalMsg = `${this.identifyPrefix} 识别:米游社,${subject}\n${realContent?.describe || ""}`;
|
||||
const replyMsg = cover ? [segment.image(cover), normalMsg] : normalMsg;
|
||||
e.reply(replyMsg);
|
||||
// 图片
|
||||
@ -1474,7 +1476,7 @@ export class tools extends plugin {
|
||||
// 暂时选取分辨率较低的video进行解析
|
||||
const videoUrl = resolutions[i].url;
|
||||
this.downloadVideo(videoUrl).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -1519,10 +1521,10 @@ export class tools extends plugin {
|
||||
const cover = firstFeed.images[0].url;
|
||||
const noWatermarkDownloadUrl = firstFeed.video_url;
|
||||
|
||||
e.reply([segment.image(cover), `${ this.identifyPrefix } 识别:微视,${ title }`]);
|
||||
e.reply([segment.image(cover), `${this.identifyPrefix} 识别:微视,${title}`]);
|
||||
|
||||
this.downloadVideo(noWatermarkDownloadUrl).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
@ -1571,7 +1573,7 @@ export class tools extends plugin {
|
||||
images,
|
||||
};
|
||||
|
||||
e.reply(`${ this.identifyPrefix } 识别:最右,${ shortVideoInfo.authorName }\n${ shortVideoInfo.title }`)
|
||||
e.reply(`${this.identifyPrefix} 识别:最右,${shortVideoInfo.authorName}\n${shortVideoInfo.title}`)
|
||||
|
||||
if (shortVideoInfo.images.length > 0) {
|
||||
const replyImages = shortVideoInfo.images.map(item => {
|
||||
@ -1585,7 +1587,7 @@ export class tools extends plugin {
|
||||
}
|
||||
if (shortVideoInfo.noWatermarkDownloadUrl) {
|
||||
this.downloadVideo(shortVideoInfo.noWatermarkDownloadUrl).then(path => {
|
||||
this.sendVideoToUpload(e, `${ path }/temp.mp4`)
|
||||
this.sendVideoToUpload(e, `${path}/temp.mp4`)
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -1605,19 +1607,19 @@ export class tools extends plugin {
|
||||
// 如果没有文件夹就创建一个
|
||||
await mkdirIfNotExists(currentWorkingDirectory + "/am")
|
||||
// 检测是否存在框架
|
||||
const isExistFreyr = await checkCommandExists("freyr");
|
||||
const isExistFreyr = await checkToolInCurEnv("freyr");
|
||||
if (!isExistFreyr) {
|
||||
e.reply(`检测到没有${ freyrName }需要的环境,无法解析!${ HELP_DOC }`);
|
||||
e.reply(`检测到没有${freyrName}需要的环境,无法解析!${HELP_DOC}`);
|
||||
return;
|
||||
}
|
||||
// 执行命令
|
||||
const result = await execSync(`freyr -d ${ currentWorkingDirectory + "/am/" } get ${ message }`);
|
||||
const result = await execSync(`freyr -d ${currentWorkingDirectory + "/am/"} get ${message}`);
|
||||
logger.info(result.toString());
|
||||
// 获取信息
|
||||
let { title, album, artist } = await this.parseFreyrLog(result.toString());
|
||||
// 兜底策略
|
||||
if (freyrName === "Apple Music" && (title === "N/A" || album === "N/A" || artist === "N/A")) {
|
||||
const data = await axios.get(`https://api.fabdl.com/apple-music/get?url=${ message }`, {
|
||||
const data = await axios.get(`https://api.fabdl.com/apple-music/get?url=${message}`, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
"Referer": "https://apple-music-downloader.com/",
|
||||
@ -1634,7 +1636,7 @@ export class tools extends plugin {
|
||||
// 国内服务器解决方案
|
||||
if (!isOversea) {
|
||||
// 临时接口
|
||||
const url = await this.musicTempApi(e, `${ title } ${ artist }`, freyrName);
|
||||
const url = await this.musicTempApi(e, `${title} ${artist}`, freyrName);
|
||||
// 下载音乐
|
||||
downloadAudio(url, this.getCurDownloadPath(e), title, 'follow').then(async path => {
|
||||
// 发送语音
|
||||
@ -1643,22 +1645,22 @@ export class tools extends plugin {
|
||||
await this.uploadGroupFile(e, path);
|
||||
await checkAndRemoveFile(path);
|
||||
}).catch(err => {
|
||||
logger.error(`下载音乐失败,错误信息为: ${ err.message }`);
|
||||
logger.error(`下载音乐失败,错误信息为: ${err.message}`);
|
||||
});
|
||||
} else {
|
||||
// freyr 逻辑
|
||||
e.reply(`${ this.identifyPrefix } 识别:${ freyrName },${ title }--${ artist }`);
|
||||
e.reply(`${this.identifyPrefix} 识别:${freyrName},${title}--${artist}`);
|
||||
// 检查目录是否存在
|
||||
const musicPath = currentWorkingDirectory + "/am/" + artist + "/" + album;
|
||||
// 找到音频文件
|
||||
const mediaFiles = await getMediaFilesAndOthers(musicPath);
|
||||
for (let other of mediaFiles.others) {
|
||||
await this.uploadGroupFile(e, `${ musicPath }/${ other }`);
|
||||
await this.uploadGroupFile(e, `${musicPath}/${other}`);
|
||||
}
|
||||
}
|
||||
// 计数
|
||||
tools.#amCount += 1;
|
||||
logger.info(`当前${ freyrName }已经下载了:${ tools.#amCount }次`);
|
||||
logger.info(`当前${freyrName}已经下载了:${tools.#amCount}次`);
|
||||
// 定时清理
|
||||
if (tools.#amCount >= 5) {
|
||||
await deleteFolderRecursive(currentWorkingDirectory + "/am");
|
||||
@ -1709,13 +1711,13 @@ export class tools extends plugin {
|
||||
.setModel(this.aiModel)
|
||||
.setPrompt(SUMMARY_PROMPT)
|
||||
.build();
|
||||
e.reply(`${ this.identifyPrefix } 识别:${ name },正在为您总结,请稍等...`, true, { recallMsg: MESSAGE_RECALL_TIME });
|
||||
e.reply(`${this.identifyPrefix} 识别:${name},正在为您总结,请稍等...`, true, { recallMsg: MESSAGE_RECALL_TIME });
|
||||
const { ans: kimiAns, model } = await builder.kimi(summaryLink);
|
||||
// 计算阅读时间
|
||||
const stats = estimateReadingTime(kimiAns);
|
||||
const titleMatch = kimiAns.match(/(Title|标题)([::])\s*(.*?)\n/)?.[3];
|
||||
e.reply(`《${ titleMatch }》 预计阅读时间: ${ stats.minutes } 分钟,总字数: ${ stats.words }`)
|
||||
const Msg = await Bot.makeForwardMsg(textArrayToMakeForward(e, [`「R插件 x ${ model }」联合为您总结内容:`, kimiAns]));
|
||||
e.reply(`《${titleMatch}》 预计阅读时间: ${stats.minutes} 分钟,总字数: ${stats.words}`)
|
||||
const Msg = await Bot.makeForwardMsg(textArrayToMakeForward(e, [`「R插件 x ${model}」联合为您总结内容:`, kimiAns]));
|
||||
await e.reply(Msg);
|
||||
return true;
|
||||
}
|
||||
@ -1730,13 +1732,13 @@ export class tools extends plugin {
|
||||
async tempSummary(name, summaryLink, e) {
|
||||
const content = await llmRead(summaryLink);
|
||||
const titleMatch = content.match(/Title:\s*(.*?)\n/)?.[1];
|
||||
e.reply(`${ this.identifyPrefix } 识别:${ name } - ${ titleMatch },正在为您总结,请稍等...`, true);
|
||||
e.reply(`${this.identifyPrefix} 识别:${name} - ${titleMatch},正在为您总结,请稍等...`, true);
|
||||
const summary = await deepSeekChat(content, SUMMARY_PROMPT);
|
||||
const Msg = await Bot.makeForwardMsg(textArrayToMakeForward(e, [`「R插件 x DeepSeek」联合为您总结内容:`, summary]));
|
||||
await e.reply(Msg);
|
||||
}
|
||||
|
||||
// q q m u s i c 解析
|
||||
// q q m u s i c 解析
|
||||
async qqMusic(e) {
|
||||
// case1: Taylor Swift/Bleachers《Anti-Hero (Feat. Bleachers) (Explicit)》 https://c6.y.qq.com/base/fcgi-bin/u?__=lg19lFgQerbo @QQ音乐
|
||||
/** case 2:
|
||||
@ -1754,7 +1756,7 @@ export class tools extends plugin {
|
||||
musicInfo = prompt + "-" + desc;
|
||||
// 空判定
|
||||
if (musicInfo.trim() === "-" || prompt === undefined || desc === undefined) {
|
||||
logger.info(`没有识别到QQ音乐小程序,帮助文档如下:${ HELP_DOC }`)
|
||||
logger.info(`没有识别到QQ音乐小程序,帮助文档如下:${HELP_DOC}`)
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@ -1764,7 +1766,7 @@ export class tools extends plugin {
|
||||
}
|
||||
// 删除特殊字符
|
||||
musicInfo = cleanFilename(musicInfo);
|
||||
logger.info(`[R插件][qqMusic] 识别音乐为:${ musicInfo }`);
|
||||
logger.info(`[R插件][qqMusic] 识别音乐为:${musicInfo}`);
|
||||
// 使用临时接口下载
|
||||
const url = await this.musicTempApi(e, musicInfo, "QQ音乐");
|
||||
// 下载音乐
|
||||
@ -1775,7 +1777,7 @@ export class tools extends plugin {
|
||||
await this.uploadGroupFile(e, path);
|
||||
await checkAndRemoveFile(path);
|
||||
}).catch(err => {
|
||||
logger.error(`下载音乐失败,错误信息为: ${ err.message }`);
|
||||
logger.error(`下载音乐失败,错误信息为: ${err.message}`);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@ -1784,7 +1786,7 @@ export class tools extends plugin {
|
||||
async qishuiMusic(e) {
|
||||
const normalRegex = /^(.*?)\s*https?:\/\//;
|
||||
const musicInfo = normalRegex.exec(e.msg)?.[1].trim().replace("@汽水音乐", "");
|
||||
logger.info(`[R插件][qishuiMusic] 识别音乐为:${ musicInfo }`);
|
||||
logger.info(`[R插件][qishuiMusic] 识别音乐为:${musicInfo}`);
|
||||
// 使用临时接口下载
|
||||
const url = await this.musicTempApi(e, musicInfo, "汽水音乐");
|
||||
// 下载音乐
|
||||
@ -1795,7 +1797,7 @@ export class tools extends plugin {
|
||||
await this.uploadGroupFile(e, path);
|
||||
await checkAndRemoveFile(path);
|
||||
}).catch(err => {
|
||||
logger.error(`下载音乐失败,错误信息为: ${ err.message }`);
|
||||
logger.error(`下载音乐失败,错误信息为: ${err.message}`);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@ -1815,7 +1817,7 @@ export class tools extends plugin {
|
||||
// 检查当前环境
|
||||
const isExistTdl = await checkToolInCurEnv("tdl");
|
||||
if (!isExistTdl) {
|
||||
e.reply(`未检测到必要的环境,无法解析小飞机${ HELP_DOC }`);
|
||||
e.reply(`未检测到必要的环境,无法解析小飞机${HELP_DOC}`);
|
||||
return;
|
||||
}
|
||||
const url = urlRex.exec(e.msg)[0];
|
||||
@ -1825,8 +1827,8 @@ export class tools extends plugin {
|
||||
e.reply("文件已保存到 Save Messages!");
|
||||
return true;
|
||||
}
|
||||
e.reply(`${ this.identifyPrefix } 识别:小飞机(学习版)`);
|
||||
const tgSavePath = `${ this.getCurDownloadPath(e) }/tg`;
|
||||
e.reply(`${this.identifyPrefix} 识别:小飞机(学习版)`);
|
||||
const tgSavePath = `${this.getCurDownloadPath(e)}/tg`;
|
||||
// 如果没有文件夹则创建
|
||||
await mkdirIfNotExists(tgSavePath);
|
||||
// 删除之前的文件
|
||||
@ -1836,7 +1838,7 @@ export class tools extends plugin {
|
||||
const mediaFiles = await getMediaFilesAndOthers(tgSavePath);
|
||||
if (mediaFiles.images.length > 0) {
|
||||
const imagesData = mediaFiles.images.map(item => {
|
||||
const fileContent = fs.readFileSync(`${ tgSavePath }/${ item }`);
|
||||
const fileContent = fs.readFileSync(`${tgSavePath}/${item}`);
|
||||
return {
|
||||
message: segment.image(fileContent),
|
||||
nickname: e.sender.card || e.user_id,
|
||||
@ -1846,11 +1848,11 @@ export class tools extends plugin {
|
||||
e.reply(await Bot.makeForwardMsg(imagesData), true, { recallMsg: MESSAGE_RECALL_TIME });
|
||||
} else if (mediaFiles.videos.length > 0) {
|
||||
for (const item of mediaFiles.videos) {
|
||||
await this.sendVideoToUpload(e, `${ tgSavePath }/${ item }`);
|
||||
await this.sendVideoToUpload(e, `${tgSavePath}/${item}`);
|
||||
}
|
||||
} else {
|
||||
for (let other of mediaFiles.others) {
|
||||
await this.uploadGroupFile(e, `${ tgSavePath }/${ other }`);
|
||||
await this.uploadGroupFile(e, `${tgSavePath}/${other}`);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -1862,7 +1864,7 @@ export class tools extends plugin {
|
||||
const msg = /https:\/\/tieba\.baidu\.com\/p\/[A-Za-z0-9]+/.exec(e.msg)?.[0];
|
||||
const id = /\/p\/([A-Za-z0-9]+)/.exec(msg)?.[1];
|
||||
// 获取帖子详情
|
||||
const hibi = HIBI_API_SERVICE + `/tieba/post_detail?tid=${ id }`;
|
||||
const hibi = HIBI_API_SERVICE + `/tieba/post_detail?tid=${id}`;
|
||||
const hibiResp = await fetch(hibi, {
|
||||
headers: {
|
||||
"User-Agent": COMMON_USER_AGENT,
|
||||
@ -1873,7 +1875,7 @@ export class tools extends plugin {
|
||||
const top = postList[0];
|
||||
// 提取标题和内容
|
||||
const { title, content } = top;
|
||||
let sendContent = `${ this.identifyPrefix }识别:贴吧,${ title }`
|
||||
let sendContent = `${this.identifyPrefix}识别:贴吧,${title}`
|
||||
let extractImages = [];
|
||||
// 如果内容中有图片、文本或视频,它会将它们添加到 sendContent 消息中
|
||||
if (content && content.length > 0) {
|
||||
@ -1885,13 +1887,13 @@ export class tools extends plugin {
|
||||
if (cdn_src) extractImages.push(segment.image(cdn_src));
|
||||
|
||||
// 处理文本
|
||||
if (text) sendContent.push(`\n\n📝 简介:${ text }`);
|
||||
if (text) sendContent.push(`\n\n📝 简介:${text}`);
|
||||
|
||||
// 处理视频
|
||||
if (link) {
|
||||
this.queue.add(async () => {
|
||||
const filePath = await this.downloadVideo(link);
|
||||
this.sendVideoToUpload(e, `${ filePath }/temp.mp4`);
|
||||
this.sendVideoToUpload(e, `${filePath}/temp.mp4`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1970,7 +1972,7 @@ export class tools extends plugin {
|
||||
this.videoDownloadConcurrency
|
||||
),
|
||||
]).then(data => {
|
||||
return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, `${ title }.mp4`);
|
||||
return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, `${title}.mp4`);
|
||||
});
|
||||
}
|
||||
|
||||
@ -2008,7 +2010,7 @@ export class tools extends plugin {
|
||||
* @returns {string}
|
||||
*/
|
||||
getCurDownloadPath(e) {
|
||||
return `${ this.defaultPath }${ e.group_id || e.user_id }`
|
||||
return `${this.defaultPath}${e.group_id || e.user_id}`
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2016,8 +2018,8 @@ export class tools extends plugin {
|
||||
* @returns {{groupPath: string, target: string}}
|
||||
*/
|
||||
getGroupPathAndTarget() {
|
||||
const groupPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`;
|
||||
const target = `${ groupPath }/temp.mp4`;
|
||||
const groupPath = `${this.defaultPath}${this.e.group_id || this.e.user_id}`;
|
||||
const target = `${groupPath}/temp.mp4`;
|
||||
return { groupPath, target };
|
||||
}
|
||||
|
||||
@ -2039,7 +2041,7 @@ export class tools extends plugin {
|
||||
// 构造代理参数
|
||||
const proxyOption = {
|
||||
...(isProxy && {
|
||||
httpAgent: new HttpsProxyAgent(`http://${ this.proxyAddr }:${ this.proxyPort }`),
|
||||
httpAgent: new HttpsProxyAgent(`http://${this.proxyAddr}:${this.proxyPort}`),
|
||||
}),
|
||||
}
|
||||
|
||||
@ -2104,7 +2106,7 @@ export class tools extends plugin {
|
||||
const partAxiosConfig = {
|
||||
headers: {
|
||||
"User-Agent": userAgent,
|
||||
"Range": `bytes=${ start }-${ end }`
|
||||
"Range": `bytes=${start}-${end}`
|
||||
},
|
||||
responseType: "stream",
|
||||
...proxyOption
|
||||
@ -2112,12 +2114,12 @@ export class tools extends plugin {
|
||||
|
||||
promises.push(axios.get(url, partAxiosConfig).then(res => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const partPath = `${ target }.part${ i }`;
|
||||
logger.mark(`[R插件][视频下载引擎] 正在下载 part${ i }`)
|
||||
const partPath = `${target}.part${i}`;
|
||||
logger.mark(`[R插件][视频下载引擎] 正在下载 part${i}`)
|
||||
const writer = fs.createWriteStream(partPath);
|
||||
res.data.pipe(writer);
|
||||
writer.on("finish", () => {
|
||||
logger.mark(`[R插件][视频下载引擎] part${ i + 1 } 下载完成`); // 记录线程下载完成
|
||||
logger.mark(`[R插件][视频下载引擎] part${i + 1} 下载完成`); // 记录线程下载完成
|
||||
resolve(partPath);
|
||||
});
|
||||
writer.on("error", reject);
|
||||
@ -2147,7 +2149,7 @@ export class tools extends plugin {
|
||||
|
||||
return groupPath;
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ err }`);
|
||||
logger.error(`下载视频发生错误!\ninfo:${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2162,39 +2164,39 @@ export class tools extends plugin {
|
||||
|
||||
// 构造aria2c命令参数
|
||||
const aria2cArgs = [
|
||||
`"${ url }"`,
|
||||
`"${url}"`,
|
||||
`--out="temp.mp4"`,
|
||||
`--dir="${ groupPath }"`,
|
||||
`--user-agent="${ userAgent }"`,
|
||||
`--max-connection-per-server=${ numThreads }`, // 每个服务器的最大连接数
|
||||
`--split=${ numThreads }`, // 分成 6 个部分进行下载
|
||||
`--dir="${groupPath}"`,
|
||||
`--user-agent="${userAgent}"`,
|
||||
`--max-connection-per-server=${numThreads}`, // 每个服务器的最大连接数
|
||||
`--split=${numThreads}`, // 分成 6 个部分进行下载
|
||||
];
|
||||
|
||||
// 如果有自定义头信息
|
||||
if (headers) {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
aria2cArgs.push(`--header="${ key }: ${ value }"`);
|
||||
aria2cArgs.push(`--header="${key}: ${value}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果使用代理
|
||||
if (proxyOption && proxyOption.httpAgent) {
|
||||
const proxyUrl = proxyOption.httpAgent.proxy.href;
|
||||
aria2cArgs.push(`--all-proxy="${ proxyUrl }"`);
|
||||
aria2cArgs.push(`--all-proxy="${proxyUrl}"`);
|
||||
}
|
||||
|
||||
try {
|
||||
await checkAndRemoveFile(target);
|
||||
logger.mark(`开始下载: ${ url }`);
|
||||
logger.mark(`开始下载: ${url}`);
|
||||
|
||||
// 执行aria2c命令
|
||||
const command = `aria2c ${ aria2cArgs.join(' ') }`;
|
||||
const command = `aria2c ${aria2cArgs.join(' ')}`;
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ stderr }`);
|
||||
logger.error(`下载视频发生错误!\ninfo:${stderr}`);
|
||||
throw error;
|
||||
} else {
|
||||
logger.mark(`下载完成: ${ url }`);
|
||||
logger.mark(`下载完成: ${url}`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -2202,7 +2204,7 @@ export class tools extends plugin {
|
||||
let count = 0;
|
||||
return new Promise((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
logger.info(logger.red(`[R插件][Aria2] 没有检测到文件!重试第${ count + 1 }次`));
|
||||
logger.info(logger.red(`[R插件][Aria2] 没有检测到文件!重试第${count + 1}次`));
|
||||
count += 1;
|
||||
if (fs.existsSync(target)) {
|
||||
logger.info("[R插件][Aria2] 检测到文件!");
|
||||
@ -2217,7 +2219,7 @@ export class tools extends plugin {
|
||||
}, DOWNLOAD_WAIT_DETECT_FILE_TIME);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ err }`);
|
||||
logger.error(`下载视频发生错误!\ninfo:${err}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@ -2233,38 +2235,38 @@ export class tools extends plugin {
|
||||
|
||||
// 构造axel命令参数
|
||||
const axelArgs = [
|
||||
`-n ${ numThreads }`,
|
||||
`-o "${ target }"`,
|
||||
`-U "${ userAgent }"`,
|
||||
`-n ${numThreads}`,
|
||||
`-o "${target}"`,
|
||||
`-U "${userAgent}"`,
|
||||
url
|
||||
];
|
||||
|
||||
// 如果有自定义头信息
|
||||
if (headers) {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
axelArgs.push(`-H "${ key }: ${ value }"`);
|
||||
axelArgs.push(`-H "${key}: ${value}"`);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果使用代理
|
||||
if (proxyOption && proxyOption.httpAgent) {
|
||||
const proxyUrl = proxyOption.httpAgent.proxy.href;
|
||||
axelArgs.push(`--proxy="${ proxyUrl }"`);
|
||||
axelArgs.push(`--proxy="${proxyUrl}"`);
|
||||
}
|
||||
|
||||
try {
|
||||
await checkAndRemoveFile(target);
|
||||
logger.mark(`开始下载: ${ url }`);
|
||||
logger.mark(`开始下载: ${url}`);
|
||||
|
||||
|
||||
// 执行axel命令
|
||||
const command = `axel ${ axelArgs.join(' ') }`;
|
||||
const command = `axel ${axelArgs.join(' ')}`;
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ stderr }`);
|
||||
logger.error(`下载视频发生错误!\ninfo:${stderr}`);
|
||||
throw error;
|
||||
} else {
|
||||
logger.mark(`下载完成: ${ url }`);
|
||||
logger.mark(`下载完成: ${url}`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -2272,12 +2274,12 @@ export class tools extends plugin {
|
||||
// 监听文件生成完成
|
||||
return new Promise((resolve, reject) => {
|
||||
const checkInterval = setInterval(() => {
|
||||
logger.info(logger.red(`[R插件][Aria2] 没有检测到文件!重试第${ count + 1 }次`));
|
||||
logger.info(logger.red(`[R插件][Aria2] 没有检测到文件!重试第${count + 1}次`));
|
||||
count += 1;
|
||||
if (fs.existsSync(target)) {
|
||||
logger.info("[R插件][Axel] 检测到文件!");
|
||||
clearInterval(checkInterval);
|
||||
logger.info(`[R插件][Axel] 下载到${ groupPath }`);
|
||||
logger.info(`[R插件][Axel] 下载到${groupPath}`);
|
||||
resolve(groupPath);
|
||||
}
|
||||
if (count === 6) {
|
||||
@ -2288,7 +2290,7 @@ export class tools extends plugin {
|
||||
}, DOWNLOAD_WAIT_DETECT_FILE_TIME);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ err }`);
|
||||
logger.error(`下载视频发生错误!\ninfo:${err}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@ -2311,7 +2313,7 @@ export class tools extends plugin {
|
||||
await checkAndRemoveFile(target);
|
||||
|
||||
const res = await axios.get(url, axiosConfig);
|
||||
logger.mark(`开始下载: ${ url }`);
|
||||
logger.mark(`开始下载: ${url}`);
|
||||
const writer = fs.createWriteStream(target);
|
||||
res.data.pipe(writer);
|
||||
|
||||
@ -2320,7 +2322,7 @@ export class tools extends plugin {
|
||||
writer.on("error", reject);
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(`下载视频发生错误!\ninfo:${ err }`);
|
||||
logger.error(`下载视频发生错误!\ninfo:${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2419,13 +2421,13 @@ export class tools extends plugin {
|
||||
}
|
||||
// 正常发送视频
|
||||
if (videoSize > videoSizeLimit) {
|
||||
e.reply(`当前视频大小:${ videoSize }MB,\n大于设置的最大限制:${ videoSizeLimit }MB,\n改为上传群文件`);
|
||||
e.reply(`当前视频大小:${videoSize}MB,\n大于设置的最大限制:${videoSizeLimit}MB,\n改为上传群文件`);
|
||||
await this.uploadGroupFile(e, path);
|
||||
} else {
|
||||
e.reply(segment.video(path));
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`[R插件][发送视频判断是否需要上传] 发生错误:\n ${ err }`);
|
||||
logger.error(`[R插件][发送视频判断是否需要上传] 发生错误:\n ${err}`);
|
||||
// logger.info(logger.yellow(`上传发生错误,R插件正在为你采用备用策略,请稍等,如果发不出来请再次尝试!`));
|
||||
// e.reply(segment.video(path));
|
||||
}
|
||||
|
@ -8,13 +8,24 @@ import path from 'path';
|
||||
import { BILI_DOWNLOAD_METHOD, COMMON_USER_AGENT, SHORT_LINKS, TEN_THOUSAND } from "../constants/constant.js";
|
||||
import { mkdirIfNotExists } from "./file.js";
|
||||
|
||||
/**
|
||||
* 生成随机字符串
|
||||
*
|
||||
* @param {number} [randomlength=16] 生成的字符串长度,默认为16
|
||||
* @returns {string} 生成的随机字符串
|
||||
*
|
||||
* @description
|
||||
* 此函数生成一个指定长度的随机字符串。
|
||||
* 字符串由大小写字母、数字和等号组成。
|
||||
* 使用 Array.from 和箭头函数来创建随机字符数组,然后用 join 方法连接。
|
||||
*
|
||||
* @example
|
||||
* const randomString = generateRandomStr(); // 生成默认长度16的随机字符串
|
||||
* const randomString20 = generateRandomStr(20); // 生成长度为20的随机字符串
|
||||
*/
|
||||
export function generateRandomStr(randomlength = 16) {
|
||||
const base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789='
|
||||
let random_str = ''
|
||||
for (let i = 0; i < randomlength; i++) {
|
||||
random_str += base_str.charAt(Math.floor(Math.random() * base_str.length))
|
||||
}
|
||||
return random_str
|
||||
const base_str = 'ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789=';
|
||||
return Array.from({ length: randomlength }, () => base_str.charAt(Math.floor(Math.random() * base_str.length))).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,24 +44,10 @@ export async function downloadAudio(mp3Url, filePath, title = "temp", redirect =
|
||||
// 补充保存文件名
|
||||
filePath += `/${ title }.${ audioType }`;
|
||||
if (fs.existsSync(filePath)) {
|
||||
console.log(`音频已存在`);
|
||||
logger.info(`音频已存在`);
|
||||
fs.unlinkSync(filePath);
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
const response = await fetch(mp3Url, {
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Mobile Safari/537.36",
|
||||
},
|
||||
responseType: "stream",
|
||||
redirect: redirect,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch ${ response.statusText }`);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
@ -72,12 +69,11 @@ export async function downloadAudio(mp3Url, filePath, title = "temp", redirect =
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`下载音乐失败,错误信息为: ${ error.message }`);
|
||||
logger.error(`下载音乐失败,错误信息为: ${ error.message }`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下载图片网关
|
||||
* @param {Object} options 参数对象
|
||||
@ -183,6 +179,7 @@ async function normalDownloadImg({
|
||||
* @param {boolean} [options.isProxy] 是否使用代理 (可选)
|
||||
* @param {Object} [options.headersExt] 自定义请求头 (可选)
|
||||
* @param {Object} [options.proxyInfo] 代理信息 (可选)
|
||||
* @param {number} [options.numThread] 线程数 (可选)
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
async function downloadImgWithAria2({
|
||||
@ -227,7 +224,6 @@ async function downloadImgWithAria2({
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 千位数的数据处理
|
||||
* @param data
|
||||
@ -299,17 +295,9 @@ export function containsChineseOrPunctuation(str) {
|
||||
* @returns {*|string}
|
||||
*/
|
||||
export function truncateString(inputString, maxLength = 50) {
|
||||
if (maxLength === 0 || maxLength === -1) {
|
||||
return inputString;
|
||||
} else if (inputString.length <= maxLength) {
|
||||
return inputString;
|
||||
} else {
|
||||
// 截取字符串,保留前面 maxLength 个字符
|
||||
let truncatedString = inputString.substring(0, maxLength);
|
||||
// 添加省略号
|
||||
truncatedString += '...';
|
||||
return truncatedString;
|
||||
}
|
||||
return maxLength === 0 || maxLength === -1 || inputString.length <= maxLength
|
||||
? inputString
|
||||
: inputString.substring(0, maxLength) + '...';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -391,20 +379,23 @@ export function estimateReadingTime(text, wpm = 200) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在某个命令
|
||||
* @param command
|
||||
* 检测当前环境是否存在某个命令
|
||||
* @param someCommand
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export function checkCommandExists(command) {
|
||||
export function checkToolInCurEnv(someCommand) {
|
||||
// 根据操作系统选择命令
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`which ${ command }`, (error, stdout, stderr) => {
|
||||
const command = os.platform() === 'win32' ? `where ${ someCommand }` : `which ${ someCommand }`;
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
// Command not found
|
||||
logger.error(`[R插件][命令环境检测]未找到${ someCommand }: ${ stderr || error.message }`);
|
||||
resolve(false);
|
||||
} else {
|
||||
// Command found
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
logger.info(`[R插件][命令环境检测]找到${ someCommand }: ${ stdout.trim() }`);
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -448,28 +439,6 @@ export function cleanFilename(filename) {
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测当前环境是否存在某个命令
|
||||
* @param someCommand
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
export function checkToolInCurEnv(someCommand) {
|
||||
// 根据操作系统选择命令
|
||||
return new Promise((resolve, reject) => {
|
||||
const command = os.platform() === 'win32' ? `where ${ someCommand }` : `which ${ someCommand }`;
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
logger.error(`[R插件][checkTool]未找到${ someCommand }: ${ stderr || error.message }`);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
logger.info(`[R插件][checkTool]找到${ someCommand }: ${ stdout.trim() }`);
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换短链接
|
||||
* @param url
|
||||
|
Loading…
x
Reference in New Issue
Block a user