From ed5b216eac514720d80c7b733287e8666d89fd87 Mon Sep 17 00:00:00 2001 From: Jerryplusy Date: Thu, 11 Sep 2025 13:10:29 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/file.js | 11 - utils/general-link-adapter.js | 400 ++++++++++++++++--------------- utils/kugou.js | 153 ------------ utils/link-share-summary-util.js | 20 -- utils/llm-util.js | 39 --- utils/openai-builder.js | 118 --------- utils/redis-util.js | 24 +- utils/tdl-util.js | 57 ----- utils/tiktok.js | 36 --- utils/trans-strategy.js | 140 ----------- utils/weibo.js | 31 --- utils/youtube.js | 46 ---- utils/yt-dlp-util.js | 194 --------------- utils/yunzai-util.js | 169 +------------ 14 files changed, 212 insertions(+), 1226 deletions(-) delete mode 100644 utils/kugou.js delete mode 100644 utils/link-share-summary-util.js delete mode 100644 utils/llm-util.js delete mode 100644 utils/openai-builder.js delete mode 100644 utils/tdl-util.js delete mode 100644 utils/tiktok.js delete mode 100644 utils/trans-strategy.js delete mode 100644 utils/weibo.js delete mode 100644 utils/youtube.js delete mode 100644 utils/yt-dlp-util.js diff --git a/utils/file.js b/utils/file.js index 5f73b59..34c5a17 100644 --- a/utils/file.js +++ b/utils/file.js @@ -1,17 +1,6 @@ import { promises as fs } from 'fs'; import path from 'path'; -// 常量提取 -const mimeTypes = { - '.jpg': 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png': 'image/png', - '.gif': 'image/gif', - '.pdf': 'application/pdf', - '.txt': 'text/plain', - // 添加其他文件类型和MIME类型的映射 -}; - /** * 通用错误处理函数 * @param err diff --git a/utils/general-link-adapter.js b/utils/general-link-adapter.js index c353aaa..063301f 100644 --- a/utils/general-link-adapter.js +++ b/utils/general-link-adapter.js @@ -1,214 +1,218 @@ -import { - GENERAL_REQ_LINK, - GENERAL_REQ_LINK_2, GENERAL_REQ_LINK_3 -} from "../constants/tools.js"; +import { GENERAL_REQ_LINK, GENERAL_REQ_LINK_3 } from '../constants/tools.js'; /** * 第三方接口适配器,用于大面积覆盖解析视频的内容 */ class GeneralLinkAdapter { + constructor() {} - constructor() { + /** + * 暂时用这个来处理短链接 + * @param url + * @param includeRedirect + * @returns {Promise} + */ + async fetchUrl(url, includeRedirect = false) { + let response = await fetch(url, { + headers: { + 'User-Agent': + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + }, + }); + return includeRedirect ? response.url : response; + } + + /** + * 【辅助函数】创造一个第三方接口的链接 + * @param externalInterface 第三方接口:这个链接来自常量 constants/tools.js @GENERAL_REQ_LINK / ... + * @param requestURL 请求的链接 + * @returns {*} + */ + createReqLink(externalInterface, requestURL) { + // 这里必须使用{ ...GENERAL_REQ_LINK_2 }赋值,不然就是对象的引用赋值,会造成全局数据问题! + let reqLink = { ...externalInterface }; + reqLink.link = reqLink.link.replace('{}', requestURL); + return reqLink; + } + + async ks(link) { + // 例子:https://www.kuaishou.com/short-video/3xkfs8p4pnd67p4?authorId=3xkznsztpwetngu&streamSource=find&area=homexxbrilliant + // https://v.m.chenzhongtech.com/fw/photo/3xburnkmj3auazc + // https://v.kuaishou.com/1ff8QP + let msg = + /(?:https?:\/\/)?(www|v)\.(kuaishou|m\.chenzhongtech)\.com\/[A-Za-z\d._?%&+\-=\/#]*/g.exec( + link + )[0]; + // 跳转短号 + if (msg.includes('v.kuaishou')) { + msg = await this.fetchUrl(msg, true); + } + let video_id; + if (msg.includes('/fw/photo/')) { + video_id = msg.match(/\/fw\/photo\/([^/?]+)/)[1]; + } else if (msg.includes('short-video')) { + video_id = msg.match(/short-video\/([^/?]+)/)[1]; + } else { + throw Error('无法提取快手的信息,请重试或者换一个视频!'); + } + const reqLink = this.createReqLink( + GENERAL_REQ_LINK, + `https://www.kuaishou.com/short-video/${video_id}` + ); + // 提取视频 + return { + name: '快手', + reqLink, + }; + } + + async xigua(link) { + // 1. https://v.ixigua.com/ienrQ5bR/ + // 2. https://www.ixigua.com/7270448082586698281 + // 3. https://m.ixigua.com/video/7270448082586698281 + let msg = /(?:https?:\/\/)?(www|v|m)\.ixigua\.com\/[A-Za-z\d._?%&+\-=\/#]*/g.exec(link)[0]; + // 跳转短号 + if (msg.includes('v.ixigua')) { + msg = await this.fetchUrl(msg, true); } - /** - * 暂时用这个来处理短链接 - * @param url - * @param includeRedirect - * @returns {Promise} - */ - async fetchUrl(url, includeRedirect = false) { - let response = await fetch(url, { - headers: { - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', - } - }); - return includeRedirect ? response.url : response; - } + const id = /ixigua\.com\/(\d+)/.exec(msg)[1] || /\/video\/(\d+)/.exec(msg)[1]; + const videoReq = `https://www.ixigua.com/${id}`; + const reqLink = this.createReqLink(GENERAL_REQ_LINK, videoReq); + return { name: '西瓜', reqLink }; + } - /** - * 【辅助函数】创造一个第三方接口的链接 - * @param externalInterface 第三方接口:这个链接来自常量 constants/tools.js @GENERAL_REQ_LINK / ... - * @param requestURL 请求的链接 - * @returns {*} - */ - createReqLink(externalInterface, requestURL) { - // 这里必须使用{ ...GENERAL_REQ_LINK_2 }赋值,不然就是对象的引用赋值,会造成全局数据问题! - let reqLink = { ...externalInterface }; - reqLink.link = reqLink.link.replace("{}", requestURL); - return reqLink; - } + async pipixia(link) { + const msg = /https:\/\/h5\.pipix\.com\/(s|item)\/[A-Za-z0-9]+/.exec(link)?.[0]; + const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); + return { name: '皮皮虾', reqLink }; + } - async ks(link) { - // 例子:https://www.kuaishou.com/short-video/3xkfs8p4pnd67p4?authorId=3xkznsztpwetngu&streamSource=find&area=homexxbrilliant - // https://v.m.chenzhongtech.com/fw/photo/3xburnkmj3auazc - // https://v.kuaishou.com/1ff8QP - let msg = /(?:https?:\/\/)?(www|v)\.(kuaishou|m\.chenzhongtech)\.com\/[A-Za-z\d._?%&+\-=\/#]*/g.exec(link)[0]; - // 跳转短号 - if (msg.includes("v.kuaishou")) { - msg = await this.fetchUrl(msg, true); - } - let video_id; - if (msg.includes('/fw/photo/')) { - video_id = msg.match(/\/fw\/photo\/([^/?]+)/)[1]; - } else if (msg.includes("short-video")) { - video_id = msg.match(/short-video\/([^/?]+)/)[1]; - } else { - throw Error("无法提取快手的信息,请重试或者换一个视频!"); - } - const reqLink = this.createReqLink(GENERAL_REQ_LINK, `https://www.kuaishou.com/short-video/${ video_id }`); - // 提取视频 + async pipigx(link) { + const msg = /https:\/\/h5\.pipigx\.com\/pp\/post\/[A-Za-z0-9]+/.exec(link)?.[0]; + const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); + return { name: '皮皮搞笑', reqLink }; + } + + async tieba(link) { + const msg = /https:\/\/tieba\.baidu\.com\/p\/[A-Za-z0-9]+/.exec(link)?.[0]; + // 这里必须使用{ ...GENERAL_REQ_LINK_2 }赋值,不然就是对象的引用赋值,会造成全局数据问题! + // !!!这里加了 '\?' 是因为 API 问题 + const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg + '"?'); + return { name: '贴吧', reqLink }; + } + + async qqSmallWorld(link) { + const msg = /https:\/\/s.xsj\.qq\.com\/[A-Za-z0-9]+/.exec(link)?.[0]; + const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); + return { name: 'QQ小世界', reqLink }; + } + + async jike(link) { + // https://m.okjike.com/originalPosts/6583b4421f0812cca58402a6?s=ewoidSI6ICI1YTgzMTY4ZmRmNDA2MDAwMTE5N2MwZmQiCn0= + const msg = /https:\/\/m.okjike.com\/originalPosts\/[A-Za-z0-9]+/.exec(link)?.[0]; + const reqLink = this.createReqLink(GENERAL_REQ_LINK_3, msg); + return { name: '即刻', reqLink }; + } + + async douyinBackup(link) { + const msg = /(http:\/\/|https:\/\/)v.douyin.com\/[A-Za-z\d._?%&+\-=\/#]*/.exec(link)?.[0]; + const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); + return { name: '抖音动图', reqLink }; + } + + /** + * 初始化通用适配器 + * @param link 通用链接 + * @returns {Promise<*>} + */ + async init(link) { + logger.mark('[R插件][通用解析]', link); + const handlers = new Map([ + [/(kuaishou.com|chenzhongtech.com)/, this.ks.bind(this)], + [/ixigua.com/, this.xigua.bind(this)], + [/h5.pipix.com/, this.pipixia.bind(this)], + [/h5.pipigx.com/, this.pipigx.bind(this)], + [/tieba.baidu.com/, this.tieba.bind(this)], + [/xsj.qq.com/, this.qqSmallWorld.bind(this)], + [/m.okjike.com/, this.jike.bind(this)], + [/v.douyin.com/, this.douyinBackup.bind(this)], + ]); + + for (let [regex, handler] of handlers) { + if (regex.test(link)) { + return handler(link); + } + } + } + + /** + * 通用解析适配器,将其他的第三方接口转换为统一的接口 + * @param adapter 通用解析适配器 + * @param sign 通用解析标识:1、2 【在适配器的reqLink中】 + * @returns {Promise} + */ + async resolve(adapter, sign) { + // 发送GET请求 + return fetch(adapter.reqLink.link, { + headers: { + 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.7', + 'Accept-Language': 'zh-CN,zh;q=0.9', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + Pragma: 'no-cache', + 'Sec-Fetch-Dest': 'document', + 'Sec-Fetch-Mode': 'navigate', + 'Sec-Fetch-Site': 'none', + 'Sec-Fetch-User': '?1', + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + }, + timeout: 10000, + }).then(async (resp) => { + const data = await resp.json(); + if (sign === 1) { + // @link GENERAL_REQ_LINK return { - name: "快手", - reqLink + name: adapter.name, + images: data.data?.imageUrl, + video: data.data?.url, }; - } + } else if (sign === 2) { + // @link GENERAL_REQ_LINK_2 + return { + name: adapter.name, - async xigua(link) { - // 1. https://v.ixigua.com/ienrQ5bR/ - // 2. https://www.ixigua.com/7270448082586698281 - // 3. https://m.ixigua.com/video/7270448082586698281 - let msg = /(?:https?:\/\/)?(www|v|m)\.ixigua\.com\/[A-Za-z\d._?%&+\-=\/#]*/g.exec(link)[0]; - // 跳转短号 - if (msg.includes("v.ixigua")) { - msg = await this.fetchUrl(msg, true); - } + images: data.data?.images, + video: data.data?.videoUrl, + desc: data.data?.desc, + }; + } else if (sign === 3) { + console.log(data); + return { + name: adapter.name, + images: data?.images.map((item) => item.url), + }; + } else { + throw Error('[R插件][通用解析]错误Sign标识'); + } + }); + } - const id = /ixigua\.com\/(\d+)/.exec(msg)[1] || /\/video\/(\d+)/.exec(msg)[1]; - const videoReq = `https://www.ixigua.com/${ id }`; - const reqLink = this.createReqLink(GENERAL_REQ_LINK, videoReq); - return { name: "西瓜", reqLink }; - } - - async pipixia(link) { - const msg = /https:\/\/h5\.pipix\.com\/(s|item)\/[A-Za-z0-9]+/.exec(link)?.[0]; - const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); - return { name: "皮皮虾", reqLink }; - } - - async pipigx(link) { - const msg = /https:\/\/h5\.pipigx\.com\/pp\/post\/[A-Za-z0-9]+/.exec(link)?.[0]; - const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); - return { name: "皮皮搞笑", reqLink }; - } - - async tieba(link) { - const msg = /https:\/\/tieba\.baidu\.com\/p\/[A-Za-z0-9]+/.exec(link)?.[0]; - // 这里必须使用{ ...GENERAL_REQ_LINK_2 }赋值,不然就是对象的引用赋值,会造成全局数据问题! - // !!!这里加了 '\?' 是因为 API 问题 - const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg + "\"?") - return { name: "贴吧", reqLink }; - } - - async qqSmallWorld(link) { - const msg = /https:\/\/s.xsj\.qq\.com\/[A-Za-z0-9]+/.exec(link)?.[0]; - const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); - return { name: "QQ小世界", reqLink }; - } - - async jike(link) { - // https://m.okjike.com/originalPosts/6583b4421f0812cca58402a6?s=ewoidSI6ICI1YTgzMTY4ZmRmNDA2MDAwMTE5N2MwZmQiCn0= - const msg = /https:\/\/m.okjike.com\/originalPosts\/[A-Za-z0-9]+/.exec(link)?.[0]; - const reqLink = this.createReqLink(GENERAL_REQ_LINK_3, msg); - return { name: "即刻", reqLink }; - } - - async douyinBackup(link) { - const msg = /(http:\/\/|https:\/\/)v.douyin.com\/[A-Za-z\d._?%&+\-=\/#]*/.exec(link)?.[0]; - const reqLink = this.createReqLink(GENERAL_REQ_LINK, msg); - return { name: "抖音动图", reqLink }; - } - - /** - * 初始化通用适配器 - * @param link 通用链接 - * @returns {Promise<*>} - */ - async init(link) { - logger.mark("[R插件][通用解析]", link) - const handlers = new Map([ - [/(kuaishou.com|chenzhongtech.com)/, this.ks.bind(this)], - [/ixigua.com/, this.xigua.bind(this)], - [/h5.pipix.com/, this.pipixia.bind(this)], - [/h5.pipigx.com/, this.pipigx.bind(this)], - [/tieba.baidu.com/, this.tieba.bind(this)], - [/xsj.qq.com/, this.qqSmallWorld.bind(this)], - [/m.okjike.com/, this.jike.bind(this)], - [/v.douyin.com/, this.douyinBackup.bind(this)], - ]); - - for (let [regex, handler] of handlers) { - if (regex.test(link)) { - return handler(link); - } - } - } - - /** - * 通用解析适配器,将其他的第三方接口转换为统一的接口 - * @param adapter 通用解析适配器 - * @param sign 通用解析标识:1、2 【在适配器的reqLink中】 - * @returns {Promise} - */ - async resolve(adapter, sign) { - // 发送GET请求 - return fetch(adapter.reqLink.link, { - headers: { - '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.7', - 'Accept-Language': 'zh-CN,zh;q=0.9', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', - 'Pragma': 'no-cache', - 'Sec-Fetch-Dest': 'document', - 'Sec-Fetch-Mode': 'navigate', - 'Sec-Fetch-Site': 'none', - 'Sec-Fetch-User': '?1', - 'Upgrade-Insecure-Requests': '1', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', - - }, - timeout: 10000 - }).then(async resp => { - const data = await resp.json(); - if (sign === 1) { - // @link GENERAL_REQ_LINK - return { - name: adapter.name, - images: data.data?.imageUrl, - video: data.data?.url, - } - } else if (sign === 2) { - // @link GENERAL_REQ_LINK_2 - return { - name: adapter.name, - images: data.data?.images, - video: data.data?.videoUrl, - desc: data.data?.desc - } - } else if (sign === 3) { - console.log(data) - return { - name: adapter.name, - images: data?.images.map(item => item.url), - } - } else { - throw Error("[R插件][通用解析]错误Sign标识"); - } - }) - } - - /** - * 通过工厂方式创建一个通用解析的JSON对象 - * @param link - * @returns {Promise<*>} - */ - static async create(link) { - // 先正则匹配到函数进行出策略处理 - const adapter = await new GeneralLinkAdapter(); - const adapterHandler = await adapter.init(link); - // 对处理完的信息进行通用解析 - return adapter.resolve(adapterHandler, adapterHandler.reqLink.sign); - } + /** + * 通过工厂方式创建一个通用解析的JSON对象 + * @param link + * @returns {Promise<*>} + */ + static async create(link) { + // 先正则匹配到函数进行出策略处理 + const adapter = await new GeneralLinkAdapter(); + const adapterHandler = await adapter.init(link); + // 对处理完的信息进行通用解析 + return adapter.resolve(adapterHandler, adapterHandler.reqLink.sign); + } } -export default GeneralLinkAdapter +export default GeneralLinkAdapter; diff --git a/utils/kugou.js b/utils/kugou.js deleted file mode 100644 index 5b851ff..0000000 --- a/utils/kugou.js +++ /dev/null @@ -1,153 +0,0 @@ -// 获取MV信息的函数 -export async function getKugouMv(msg, page_limit, count_limit, n) { - const url = `https://mobiles.kugou.com/api/v3/search/mv?format=json&keyword=${encodeURIComponent( - msg - )}&page=${page_limit}&pagesize=${count_limit}&showtype=1`; - try { - const response = await fetch(url); - const json = await response.json(); - const info_list = json.data.info; - let data_list = []; - - if (n !== "") { - const info = info_list[n]; - const json_data2 = await getMvData(info.hash); - const mvdata_list = json_data2.mvdata; - - let mvdata = null; - if ("sq" in mvdata_list) { - mvdata = mvdata_list["sq"]; - } else if ("le" in mvdata_list) { - mvdata = mvdata_list["le"]; - } else if ("rq" in mvdata_list) { - mvdata = mvdata_list["rq"]; - } - - data_list = [ - { - name: info["filename"], - singername: info["singername"], - duration: new Date(info["duration"] * 1000) - .toISOString() - .substr(14, 5), - file_size: `${(mvdata["filesize"] / (1024 * 1024)).toFixed(2)} MB`, - mv_url: mvdata["downurl"], - cover_url: info["imgurl"].replace("/{size}", ""), - // 下面这些字段可能需要你从其他地方获取,因为它们不是直接从这个API返回的 - // "play_count": json.play_count, - // "like_count": json.like_count, - // "comment_count": json.comment_count, - // "collect_count": json.collect_count, - // "publish_date": json.publish_date - }, - ]; - } else { - data_list = info_list.map((info) => ({ - name: info["filename"], - singername: info["singername"], - duration: new Date(info["duration"] * 1000).toISOString().substr(14, 5), - cover_url: info["imgurl"].replace("/{size}", ""), - })); - } - - return data_list; - } catch (error) { - console.error("Error fetching MV data:", error); - return []; - } -} - -// 获取歌曲信息的函数 -export async function getKugouSong(msg, page_limit, count_limit, n) { - const url = `https://mobiles.kugou.com/api/v3/search/song?format=json&keyword=${encodeURIComponent( - msg - )}&page=${page_limit}&pagesize=${count_limit}&showtype=1`; - try { - const response = await fetch(url); - const json = await response.json(); - const info_list = json.data.info; - // console.log(info_list) - let data_list = []; - - if (n !== "") { - const info = info_list[n]; - const song_hash = info.hash; - let song_url = "付费歌曲暂时无法获取歌曲下载链接"; - let json_data2 = {}; - if (song_hash !== "") { - json_data2 = await getMp3Data(song_hash); - song_url = json_data2.error ? song_url : json_data2.url; - } - - data_list = [ - { - name: info.filename, - singername: info.singername, - duration: new Date(info.duration * 1000).toISOString().substr(14, 5), - file_size: `${(json_data2.fileSize / (1024 * 1024)).toFixed(2)} MB`, - song_url: song_url, - album_img: json_data2.album_img?.replace("/{size}", ""), - // "mv_url": await get_kugou_mv(msg, page_limit, count_limit, n) 这可能会导致递归调用,视具体情况而定 - }, - ]; - } else { - data_list = info_list.map((info) => ({ - name: info.filename, - singername: info.singername, - duration: new Date(info.duration * 1000).toISOString().substr(14, 5), - hash: info.hash, - mvhash: info.mvhash ? info.mvhash : null, - })); - } - - // 发送响应 - return { - code: 200, - text: "解析成功", - type: "歌曲解析", - now: new Date().toISOString(), - data: data_list, - }; - } catch (error) { - console.error("Error fetching song data:", error); - return { code: 500, text: "服务器内部错误" }; - } -} - -// 获取MP3数据的函数 -async function getMp3Data(song_hash) { - const url = `https://m.kugou.com/app/i/getSongInfo.php?hash=${song_hash}&cmd=playInfo`; - try { - const response = await fetch(url, { - headers: { - "User-Agent": "Mozilla/6.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36" - }, - redirect: 'follow', - method: 'GET', - }); - const json = await response.json(); - return json; - } catch (error) { - console.error("Error fetching MP3 data:", error); - return {}; - } -} - -// 获取MV数据的函数 -async function getMvData(mv_hash) { - const url = `http://m.kugou.com/app/i/mv.php?cmd=100&hash=${mv_hash}&ismp3=1&ext=mp4`; - try { - const response = await fetch(url, { - headers: { - "User-Agent": "Mozilla/6.0 (Linux; Android 11; SAMSUNG SM-G973U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/14.2 Chrome/87.0.4280.141 Mobile Safari/537.36" - }, - redirect: 'follow', - method: 'GET', - }); - const json = await response.json(); - return json; - } catch (error) { - console.error("Error fetching MV data:", error); - return {}; - } -} diff --git a/utils/link-share-summary-util.js b/utils/link-share-summary-util.js deleted file mode 100644 index bfadcb5..0000000 --- a/utils/link-share-summary-util.js +++ /dev/null @@ -1,20 +0,0 @@ -import { SUMMARY_CONTENT_ESTIMATOR_PATTERNS } from "../constants/constant.js"; - -/** - * 内容评估器 - * @link {linkShareSummary} - * @param link 链接 - */ -export function contentEstimator(link) { - for (const pattern of SUMMARY_CONTENT_ESTIMATOR_PATTERNS) { - if (pattern.reg.test(link)) { - return { - name: pattern.name, - summaryLink: pattern.reg.exec(link)?.[0] - }; - } - } - - logger.error("[R插件][总结模块] 内容评估出错..."); - throw Error("内容评估出错..."); -} \ No newline at end of file diff --git a/utils/llm-util.js b/utils/llm-util.js deleted file mode 100644 index bab0d53..0000000 --- a/utils/llm-util.js +++ /dev/null @@ -1,39 +0,0 @@ -import { retryFetch } from "./common.js"; -import { PearAPI_CRAWLER, PearAPI_DEEPSEEK } from "../constants/tools.js"; - -/** - * LLM 爬虫 - * @param summaryLink - * @returns {Promise} - */ -export async function llmRead(summaryLink) { - const llmCrawler = await retryFetch(PearAPI_CRAWLER.replace("{}", summaryLink)); - return (await llmCrawler.json())?.data; -} - -/** - * DeepSeek对话 - * @param content - * @param prompt - * @returns {Promise} - */ -export async function deepSeekChat(content, prompt) { - const deepseekFreeSummary = await fetch(PearAPI_DEEPSEEK, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - "messages": [ - { - "role": "system", - "content": prompt - }, - { - "role": "user", - "content": content, - }] - }), - }); - return (await deepseekFreeSummary.json())?.message; -} diff --git a/utils/openai-builder.js b/utils/openai-builder.js deleted file mode 100644 index 4aaa9bc..0000000 --- a/utils/openai-builder.js +++ /dev/null @@ -1,118 +0,0 @@ -import axios from "axios"; - -export class OpenaiBuilder { - constructor() { - this.baseURL = "https://api.moonshot.cn"; // 默认模型 - this.apiKey = ""; // 默认API密钥 - this.prompt = "描述一下这个图片"; // 默认提示 - this.model = 'claude-3-haiku-20240307' - this.provider = "kimi"; // 默认提供商 - } - - setBaseURL(baseURL) { - this.baseURL = baseURL; - return this; - } - - setApiKey(apiKey) { - this.apiKey = apiKey; - return this; - } - - setPrompt(prompt) { - this.prompt = prompt; - return this; - } - - setModel(model) { - this.model = model; - return this; - } - - setProvider(provider) { - this.provider = provider; - return this; - } - - setPath(path) { - this.path = path; - return this; - } - - async build() { - // logger.info(this.baseURL, this.apiKey) - // 创建客户端 - this.client = axios.create({ - baseURL: this.baseURL, - timeout: 100000, - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer " + this.apiKey - } - }); - return this; - } - - /** - * 调用与OpenAI兼容的API(如Kimi/Moonshot)。 - * @param {Array} messages - 发送给模型的消息列表。 - * @param {Array} [tools=[]] - (可选) 一个描述可供模型使用的工具的数组。 - * @returns {Promise} 返回一个包含模型响应的对象。如果模型决定调用工具,则包含 'tool_calls' 字段;否则,包含 'ans' 文本响应。 - */ - async chat(messages, tools = []) { - if (this.provider === 'deepseek') { - const content = messages.find(m => m.role === 'user')?.content; - const ans = await deepSeekChat(content, this.prompt); - return { - "model": "deepseek", - "ans": ans - } - } - - // 准备发送给API的消息 - let requestMessages = [...messages]; - // 检查是否已存在系统提示 - const hasSystemPrompt = requestMessages.some(m => m.role === 'system'); - - // 如果没有系统提示并且builder中已设置,则添加 - if (!hasSystemPrompt && this.prompt) { - requestMessages.unshift({ - role: 'system', - content: this.prompt, - }); - } - - // 构建API请求的负载 - const payload = { - model: this.model, // 使用在builder中设置的模型 - messages: requestMessages, - }; - - // 如果提供了工具,将其添加到负载中,并让模型自动决定是否使用 - if (tools && tools.length > 0) { - payload.tools = tools; - payload.tool_choice = "auto"; - } - - // 发送POST请求到聊天完成端点 - const completion = await this.client.post("/v1/chat/completions", payload); - const message = completion.data.choices[0].message; - - // 从响应中获取实际使用的模型名称 - const modelName = completion.data.model; - - // 如果模型的响应中包含工具调用 - if (message.tool_calls) { - return { - "model": modelName, - "tool_calls": message.tool_calls - } - } - - // 否则,返回包含文本答案的响应 - return { - "model": modelName, - "ans": message.content - } - } -} diff --git a/utils/redis-util.js b/utils/redis-util.js index 4c4a67c..d199216 100644 --- a/utils/redis-util.js +++ b/utils/redis-util.js @@ -7,7 +7,7 @@ * console.log(exists); // true or false */ export async function redisExistKey(key) { - return redis.exists(key); + return redis.exists(key); } /** @@ -19,7 +19,7 @@ export async function redisExistKey(key) { * console.log(value); // { ... } */ export async function redisGetKey(key) { - return JSON.parse(await redis.get(key)); + return JSON.parse(await redis.get(key)); } /** @@ -31,23 +31,5 @@ export async function redisGetKey(key) { * await redisSetKey('myKey', { foo: 'bar' }); */ export async function redisSetKey(key, value = {}) { - return redis.set( - key, - JSON.stringify(value), - ); + return redis.set(key, JSON.stringify(value)); } - -/** - * 判断是否存在这个key然后再取值,如果没有就返回null - * @param key - * @returns {Promise} - * @example - * const value = await redisExistAndGetKey('myKey'); - * console.log(value); // { ... } or null - */ -export async function redisExistAndGetKey(key) { - if (await redisExistKey(key)) { - return redisGetKey(key); - } - return null; -} \ No newline at end of file diff --git a/utils/tdl-util.js b/utils/tdl-util.js deleted file mode 100644 index d593704..0000000 --- a/utils/tdl-util.js +++ /dev/null @@ -1,57 +0,0 @@ -import { exec } from 'child_process'; -import path from 'path' - -/** - * 执行 TDL 进行下载 - * @param url - * @param curPath - * @param isOversea - * @param proxyAddr - * @param videoDownloadConcurrency - * @returns {Promise} - */ -export async function startTDL(url, curPath, isOversea, proxyAddr, videoDownloadConcurrency = 1) { - return new Promise((resolve, reject) => { - curPath = path.resolve(curPath); - const proxyStr = isOversea ? `` : `--proxy ${ proxyAddr }`; - const concurrencyStr = videoDownloadConcurrency > 1 ? `-t ${ videoDownloadConcurrency } -l ${ videoDownloadConcurrency }` : ''; - const command = `tdl dl -u ${ url } -d ${ curPath } ${ concurrencyStr } ${ proxyStr }` - logger.mark(`[R插件][TDL] ${ command }`); - exec(command, (error, stdout, stderr) => { - if (error) { - reject(`[R插件][TDL]执行出错: ${ error.message }`); - return; - } - if (stderr) { - reject(`[R插件][TDL]错误信息: ${ stderr }`); - return; - } - resolve(stdout); - }) - }) -} - -/** - * 保存小飞机内容到小飞机的收藏 - * @param url - * @param isOversea - * @param proxyAddr - * @returns {Promise} - */ -export async function saveTDL(url, isOversea, proxyAddr) { - return new Promise((resolve, reject) => { - const proxyStr = isOversea ? `` : `--proxy ${ proxyAddr }`; - const command = `tdl forward --from ${ url } ${ proxyStr }` - exec(command, (error, stdout, stderr) => { - if (error) { - reject(`[R插件][TDL保存]执行出错: ${ error.message }`); - return; - } - if (stderr) { - reject(`[R插件][TDL保存]错误信息: ${ stderr }`); - return; - } - resolve(stdout); - }) - }) -} diff --git a/utils/tiktok.js b/utils/tiktok.js deleted file mode 100644 index 7f86e51..0000000 --- a/utils/tiktok.js +++ /dev/null @@ -1,36 +0,0 @@ -export function genVerifyFp() { - const baseStr = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - const t = baseStr.length; - let milliseconds = Date.now(); // 获取当前的时间戳(毫秒) - let base36 = ""; - - // 将时间戳转换为base36 - while (milliseconds > 0) { - let remainder = milliseconds % 36; - if (remainder < 10) { - base36 = remainder.toString() + base36; - } else { - base36 = String.fromCharCode('a'.charCodeAt(0) + remainder - 10) + base36; - } - milliseconds = Math.floor(milliseconds / 36); - } - - const r = base36; - let o = new Array(36).fill(""); - o[8] = o[13] = o[18] = o[23] = "_"; - o[14] = "4"; - - // 生成随机字符 - for (let i = 0; i < 36; i++) { - if (!o[i]) { - let n = Math.floor(Math.random() * t); - if (i === 19) { - n = (3 & n) | 8; - } - o[i] = baseStr[n]; - } - } - - return "verify_" + r + "_" + o.join(""); -} - diff --git a/utils/trans-strategy.js b/utils/trans-strategy.js deleted file mode 100644 index aa4ac9f..0000000 --- a/utils/trans-strategy.js +++ /dev/null @@ -1,140 +0,0 @@ -import { tencentTransMap } from "../constants/constant.js"; -import fetch from "node-fetch"; -import _ from 'lodash' - -// 定义翻译策略接口 -class TranslateStrategy { - async translate(query, targetLanguage) { - throw new Error("This method should be implemented by subclasses"); - } -} - -// 企鹅翻译策略 -class TencentTranslateStrategy extends TranslateStrategy { - constructor(config) { - super(); - this.config = config; - this.url = "https://transmart.qq.com/api/imt"; - this.commonHeaders = { - "USER-AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0" - }; - this.clientKey = "browser-firefox-111.0.0-Mac OS-d35fca23-eb48-45ba-9913-114f1177b02b-1679376552800"; - } - - async detectLanguage(query) { - try { - const response = await fetch(this.url, { - method: "POST", - headers: this.commonHeaders, - body: JSON.stringify({ - "header": { - "fn": "text_analysis", - "client_key": this.clientKey - }, - "text": query, - "type": "plain", - "normalize": { - "merge_broken_line": false - } - }) - }); - const data = await response.json(); - return data.header.ret_code === 'succ' ? data.language : "en"; - } catch (error) { - logger.error("Error detecting language:", error); - return "en"; - } - } - - async translate(query, targetLanguage) { - try { - const sourceLanguage = await this.detectLanguage(query); - const response = await fetch(this.url, { - method: "POST", - headers: this.commonHeaders, - body: JSON.stringify({ - "header": { - "fn": "auto_translation", - "client_key": this.clientKey - }, - "type": "plain", - "model_category": "normal", - "text_domain": "general", - "source": { - "lang": sourceLanguage, - "text_list": ["", query, ""] - }, - "target": { - "lang": tencentTransMap[targetLanguage] - } - }) - }); - const data = await response.json(); - return data.header.ret_code === 'succ' ? data.auto_translation?.[1] : "翻译失败"; - } catch (error) { - logger.error("Error translating text:", error); - return "翻译失败"; - } - } -} - -// Deepl翻译策略 -class DeeplTranslateStrategy extends TranslateStrategy { - constructor(config) { - super(); - this.config = config; - this.deeplUrls = this.config.deeplApiUrls.includes(",") ? this.config.deeplApiUrls.split(",") : [this.config.deeplApiUrls]; - } - - async translate(query, targetLanguage) { - const url = this.deeplUrls[Math.floor(Math.random() * this.deeplUrls.length)]; - logger.info(`[R插件][Deepl翻译]:当前使用的API:${url}`); - try { - const source_lang = await new TencentTranslateStrategy(this.config).detectLanguage(query); - logger.info(`[R插件][Deepl翻译]:检测到的源语言:${source_lang}`); - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - ...this.commonHeaders - }, - body: JSON.stringify({ - text: query, - source_lang, - target_lang: tencentTransMap[targetLanguage] - }), - - }); - const data = await response.json(); - return data.data; - } catch (error) { - logger.error("Error translating text:", error); - return "翻译失败"; - } - } -} - -// 主逻辑 -export default class Translate { - constructor(config) { - this.config = config; - this.strategy = null; - } - - selectStrategy() { - if (!_.isEmpty(this.config.deeplApiUrls)) { - logger.info("[R插件][翻译策略]:当前选择 Deepl翻译") - return new DeeplTranslateStrategy(this.config); - } else { - logger.info("[R插件][翻译策略]:当前选择 企鹅翻译") - return new TencentTranslateStrategy(this.config); - } - } - - async translate(query, targetLanguage) { - if (!this.strategy) { - this.strategy = this.selectStrategy(); - } - return this.strategy.translate(query, targetLanguage); - } -} diff --git a/utils/weibo.js b/utils/weibo.js deleted file mode 100644 index 8d985b3..0000000 --- a/utils/weibo.js +++ /dev/null @@ -1,31 +0,0 @@ -// Base62 encode function in JavaScript -const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; -const base62_encode = (number) => { - if (number === 0) return '0'; - let result = ''; - while (number > 0) { - result = ALPHABET[number % 62] + result; - number = Math.floor(number / 62); - } - return result; -}; - -// Convert mid to id -export const mid2id = (mid) => { - mid = mid.toString().split('').reverse().join(''); // Reverse the input string - const size = Math.ceil(mid.length / 7); - let result = []; - - for (let i = 0; i < size; i++) { - let s = mid.slice(i * 7, (i + 1) * 7).split('').reverse().join(''); // Chunk and reverse each chunk - s = base62_encode(parseInt(s, 10)); // Encode each chunk using base62 - if (i < size - 1 && s.length < 4) { - // Pad with leading zeros if necessary - s = '0'.repeat(4 - s.length) + s; - } - result.push(s); - } - - result.reverse(); // Reverse the result array to maintain order - return result.join(''); // Join the array into a single string -}; diff --git a/utils/youtube.js b/utils/youtube.js deleted file mode 100644 index 0a5367c..0000000 --- a/utils/youtube.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 用于YouTube的格式化 - * @param seconds - * @returns {string} - */ -export function ytbFormatTime(seconds) { - // 计算小时、分钟和秒 - const hours = Math.floor(seconds / 3600); - const minutes = Math.floor((seconds % 3600) / 60); - const secs = seconds % 60; - // 将小时、分钟和秒格式化为两位数 - const formattedHours = String(hours).padStart(2, '0'); - const formattedMinutes = String(minutes).padStart(2, '0'); - const formattedSeconds = String(secs).padStart(2, '0'); - // 构造时间范围字符串 - return `00:00:00-${formattedHours}:${formattedMinutes}:${formattedSeconds}`; -} - -/** - * 移除链接中的不需要的参数 - * @param url - * @returns {*} - */ -export function removeParams(url) { - return url - .replace(/&list=[^&]*/g, '') - .replace(/&start_radio=[^&]*/g, '') - .replace(/&index=[^&]*/g, '') - .replace(/&si=[^&]*/g, ''); -} - -export function convertToSeconds(timeStr) { - const parts = timeStr.split(':').map(Number); - if (parts.length === 2) { - const [minutes, seconds] = parts; - return minutes * 60 + seconds; - } else if (parts.length === 3) { - const [hours, minutes, seconds] = parts; - return hours * 3600 + minutes * 60 + seconds; - } - return timeStr; -} - -export async function autoSelectMusicOrVideoSend() { - -} diff --git a/utils/yt-dlp-util.js b/utils/yt-dlp-util.js deleted file mode 100644 index 8f0f9be..0000000 --- a/utils/yt-dlp-util.js +++ /dev/null @@ -1,194 +0,0 @@ -import { exec } from 'child_process'; - -/** - * 构建梯子参数 - * @param isOversea - * @param proxy - * @returns {string|string} - */ -function constructProxyParam(isOversea, proxy) { - return isOversea ? '' : `--proxy ${proxy}`; -} - -/** - * 构造cookie参数 - * 目前只支持YouTube构造cookie,否则就必须修改`url.includes("youtu")` - * @param url - * @param cookiePath - * @returns {string} - */ -function constructCookiePath(url, cookiePath) { - return cookiePath !== '' && url.includes('youtu') ? `--cookies ${cookiePath}` : ''; -} - -/** - * yt-dlp获取标题的时候可能需要的一个编码参数,也在一定程度上解决部分window系统乱码问题 - * @param url - * @returns {string} - */ -function constructEncodingParam(url) { - return '--encoding UTF-8'; // 始终为标题获取使用 UTF-8 编码 -} - -/** - * 获取时长 - * @param url - * @param isOversea - * @param proxy - * @param cookiePath - * @returns string - */ -export function ytDlpGetDuration(url, isOversea, proxy, cookiePath = '') { - return new Promise((resolve, reject) => { - // 构造 cookie 参数 - const cookieParam = constructCookiePath(url, cookiePath); - const command = `yt-dlp --get-duration --skip-download ${cookieParam} ${constructProxyParam(isOversea, proxy)} "${url}"`; - exec(command, (error, stdout, stderr) => { - if (error) { - logger.error( - `[R插件][yt-dlp审计] Error executing ytDlpGetDuration: ${error}. Stderr: ${stderr}` - ); - reject(error); - } else { - resolve(stdout.trim()); - } - }); - }); -} - -/** - * 获取标题 - * @param url - * @param isOversea - * @param proxy - * @param cookiePath - * @returns string - */ -export async function ytDlpGetTilt(url, isOversea, proxy, cookiePath = '') { - return new Promise((resolve, reject) => { - // 构造 cookie 参数 - const cookieParam = constructCookiePath(url, cookiePath); - // 构造 编码 参数 - const encodingParam = constructEncodingParam(url); - const command = `yt-dlp --get-title --skip-download ${cookieParam} ${constructProxyParam(isOversea, proxy)} "${url}" ${encodingParam}`; - exec(command, (error, stdout, stderr) => { - if (error) { - logger.error( - `[R插件][yt-dlp审计] Error executing ytDlpGetTilt: ${error}. Stderr: ${stderr}` - ); - reject(error); - } else { - resolve(stdout.trim()); - } - }); - }); -} - -/** - * 获取封面 - * @param path - * @param url - * @param isOversea - * @param proxy - * @param cookiePath - * @param thumbnailFilenamePrefix 缩略图文件名前缀 (不含扩展名) - */ -export function ytDlpGetThumbnail( - path, - url, - isOversea, - proxy, - cookiePath = '', - thumbnailFilenamePrefix = 'thumbnail' -) { - return new Promise((resolve, reject) => { - const cookieParam = constructCookiePath(url, cookiePath); - const finalThumbnailName = thumbnailFilenamePrefix || 'thumbnail'; - const command = `yt-dlp --write-thumbnail --convert-thumbnails png --skip-download ${cookieParam} ${constructProxyParam(isOversea, proxy)} "${url}" -P "${path}" -o "${finalThumbnailName}.%(ext)s"`; - - exec(command, (error, stdout, stderr) => { - if (error) { - logger.error( - `[R插件][yt-dlp审计] Error executing ytDlpGetThumbnail: ${error}. Stderr: ${stderr}` - ); - return reject(error); - } - // 从yt-dlp的输出中提取文件名 - const match = stdout.match(/Writing thumbnail to: (.*)/); - if (match && match[1]) { - const thumbnailPath = match[1].trim(); - // 只返回文件名部分 - const thumbnailFilename = thumbnailPath.split(/[\\/]/).pop(); - logger.info(`[R插件][yt-dlp审计] Thumbnail downloaded: ${thumbnailFilename}`); - resolve(thumbnailFilename); - } else { - // 兜底方案:如果无法从输出中解析,则按原逻辑拼接 - logger.warn( - '[R插件][yt-dlp审计] Could not parse thumbnail filename from stdout. Falling back to default.' - ); - // 尝试查找文件,因为yt-dlp可能没有输出我们期望的格式 - const expectedPngPath = `${finalThumbnailName}.png`; - resolve(expectedPngPath); - } - }); - }); -} - -/** - * yt-dlp 工具类 - * @returns {Promise} - * @param path 下载路径 - * @param url 下载链接 - * @param isOversea 是否是海外用户 - * @param proxy 代理地址 - * @param merge 是否合并输出为 mp4 格式 (仅适用于视频合并需求) - * @param graphics YouTube画质参数 - * @param timeRange 截取时间段 - * @param maxThreads 最大并发 - * @param outputFilename 输出文件名 (不含扩展名) - * @param cookiePath Cookie所在位置 - */ -export async function ytDlpHelper( - path, - url, - isOversea, - proxy, - maxThreads, - outputFilename, - merge = false, - graphics, - timeRange, - cookiePath = '' -) { - return new Promise((resolve, reject) => { - let command = ''; - // 构造 cookie 参数 - const cookieParam = constructCookiePath(url, cookiePath); - // 确保 outputFilename 不为空,提供一个默认值以防万一 - const finalOutputFilename = outputFilename || 'temp_download'; - - if (url.includes('music')) { - // 这里是 YouTube Music的处理逻辑 - // e.g yt-dlp -x --audio-format mp3 https://youtu.be/5wEtefq9VzM -o test.mp3 - command = `yt-dlp -x --audio-format flac -f ba ${cookieParam} ${constructProxyParam(isOversea, proxy)} -P "${path}" -o "${finalOutputFilename}.flac" "${url}"`; - } else { - // 正常情况下的处理逻辑 - const fParam = url.includes('youtu') - ? `--download-sections "*${timeRange}" -f "bv${graphics}[ext=mp4]+ba[ext=m4a]" ` - : ''; - - command = `yt-dlp -N ${maxThreads} ${fParam} --concurrent-fragments ${maxThreads} ${cookieParam} ${constructProxyParam(isOversea, proxy)} -P "${path}" -o "${finalOutputFilename}.%(ext)s" "${url}"`; - } - - logger.info(`[R插件][yt-dlp审计] ${command}`); - - exec(command, (error, stdout) => { - if (error) { - logger.error(`[R插件][yt-dlp审计] 执行命令时出错: ${error}`); - reject(error); - } else { - resolve(stdout); - } - }); - }); -} diff --git a/utils/yunzai-util.js b/utils/yunzai-util.js index 0cda1d1..f6d7c8d 100644 --- a/utils/yunzai-util.js +++ b/utils/yunzai-util.js @@ -1,169 +1,14 @@ -import os from "os"; - /** * 将只有 text 类型的数组转换为原生的 {Bot.makeForwardMsg} * @param e * @param textArray {string[]} */ export function textArrayToMakeForward(e, textArray) { - return textArray.map(item => { - return { - message: { type: "text", text: item }, - nickname: e.sender.card || e.user_id, - user_id: e.user_id, - }; - }) -} - -/** - * 发送群组音乐卡片 - * @param e - * @param platformType 音乐平台 - * @param musicId 音乐id - */ -export async function sendMusicCard(e, platformType, musicId) { - await e.bot.sendApi('send_group_msg', { - group_id: e.group_id, - message: [ - { - type: 'music', - data: { - type: platformType, - id: musicId - } - } - ] - }); -} - -/** - * 获取群文件最新的图片 - * @param e - * @param count 获取群聊条数 - * @returns {Promise<*|string>} - */ -export async function getLatestImage(e, count = 10) { - // 获取最新的聊天记录,阈值为5 - const latestChat = await e.bot.sendApi("get_group_msg_history", { - "group_id": e.group_id, - "count": count - }); - const messages = latestChat.data.messages; - // 找到最新的图片 - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages?.[i]?.message; - if (message?.[0]?.type === "image") { - return message?.[0].data?.url; - } - } - return ""; -} - -/** - * 获取群文件Url地址 - * @param e - * @param count 获取群聊条数 - */ -export async function getGroupFileUrl(e, count = 10) { - const latestChat = await e.bot.sendApi("get_group_msg_history", { - "group_id": e.group_id, - "count": count - }); - const messages = latestChat.data.messages; - let file_id = ""; - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages?.[i]?.message; - if (message?.[0]?.type === "file") { - file_id = message?.[0].data?.file_id; - break; - } - } - if (file_id === "") { - logger.info('未找到群文件') - return ""; - } - // 获取文件信息 - let latestFileUrl = await e.bot.sendApi("get_group_file_url", { - "group_id": e.group_id, - "file_id": file_id - }); - let cleanPath = decodeURIComponent(latestFileUrl.data.url) - // 适配 低版本 Napcat 例如:3.6.4 - if (cleanPath.startsWith("https")) { - // https://njc-download.ftn.qq.com/.... - const urlObj = new URL(cleanPath); - // 检查URL中是否包含 fname 参数 - if (urlObj.searchParams.has('fname')) { - // 获取 fname 参数的值 - const originalFname = urlObj.searchParams.get('fname'); - - // 提取 file_id(第一个"."后面的内容) - const fileId = file_id.split('.').slice(1).join('.'); // 分割并去掉第一个部分 - urlObj.searchParams.set('fname', `${originalFname}${fileId}`); - return { - cleanPath: urlObj.toString(), - file_id - }; - } - } else if (cleanPath.startsWith('file:///')) { - cleanPath = cleanPath.replace('file:///', '') - } - - return { cleanPath, file_id }; -} - -/** - * 获取群回复 - * @param e - */ -export async function getReplyMsg(e) { - const msgList = await e.bot.sendApi("get_group_msg_history", { - "group_id": e.group_id, - "count": 1 - }); - let msgId = msgList.data.messages[0]?.message[0]?.data.id - let msg = await e.bot.sendApi("get_msg",{ - "message_id" : msgId - }) - return msg.data -} - -/** - * 获取机器人信息 - * @param e - * @returns {Promise<*>} - */ -export async function getBotLoginInfo(e) { - return await e.bot.sendApi("get_login_info"); -} - -/** - * 获取运行状态 - * @param e - * @returns {Promise<*>} - */ -export async function getBotStatus(e) { - return await e.bot.sendApi("get_status"); -} - -/** - * 获取版本信息 - * @param e - * @returns {Promise<*>} - */ -export async function getBotVersionInfo(e) { - return await e.bot.sendApi("get_version_info"); -} - -/** - * 发送私聊消息 - * @param e - * @param message - * @returns {Promise} - */ -export async function sendPrivateMsg(e, message) { - e.bot.sendApi("send_private_msg", { - user_id: e.user_id, - message: message, - }) + return textArray.map((item) => { + return { + message: { type: 'text', text: item }, + nickname: e.sender.card || e.user_id, + user_id: e.user_id, + }; + }); }