diff --git a/apps/fanqie.js b/apps/fanqie.js deleted file mode 100644 index 8a213b3..0000000 --- a/apps/fanqie.js +++ /dev/null @@ -1,225 +0,0 @@ -import fs from 'node:fs'; -import path from 'path'; -import chokidar from 'chokidar'; -import ConfigControl from '../lib/config/configControl.js'; -import Fanqie from '../modules/apps/fanqie/fanqie.js'; - -/** - * 本功能由 y68(github@yeqiu6080) 提供技术支持 - */ -export default class FanqiePlugin extends plugin { - constructor() { - super({ - name: 'crystelf-fanqie', - dsc: '番茄小说下载器', - event: 'message', - priority: -114, - rule: [ - { - reg: '(changdunovel.com/wap/share-v2.html|fanqienovel.com/page)', - fnc: 'handleFanqieLink', - }, - { - reg: '#?fq下载(.*)', - fnc: 'downloadByBookId', - }, - { - reg: '^fq清(理|除|空)缓存$', - fnc: 'clearFanqieCache', - }, - ], - }); - - this.initPromise = this.initFanqieConfig(); - this.fanqieClient = null; - - // 注册计划任务 - this.task = { - cron: '0 0 16 * * ?', - name: '定时清理番茄缓存', - fnc: () => this.clearFanqieCache(false, true), - }; - } - - async initFanqieConfig() { - this.outDir = await ConfigControl.get('fanqieConfig')?.outDir; - this.apiUrl = await ConfigControl.get('fanqieConfig')?.url; - this.fanqieClient = new Fanqie(this.apiUrl); - } - - /** - * 监听下载输出目录 - */ - async waitForOutputFile(dir, timeout = 30000) { - if (!dir) return false; - - return new Promise((resolve) => { - const watcher = chokidar.watch(dir, { - persistent: true, - ignoreInitial: true, - }); - - const timer = setTimeout(() => { - watcher.close(); - resolve(false); - }, timeout); - - watcher.on('add', (filePath) => { - clearTimeout(timer); - watcher.close(); - resolve(filePath); - }); - }); - } - - /** - * 清理缓存 - */ - async clearFanqieCache(e, isScheduled = false, specificId = false) { - if (!isScheduled && e && !e.isMaster) { - e.reply('你没有权限使用此功能', true); - return false; - } - - if (!this.outDir) { - await this.initPromise; - if (!this.outDir) { - if (e) e.reply('缓存目录未初始化,无法清理', true); - return false; - } - } - - if (specificId) { - const specificPath = path.join(this.outDir, 'files', specificId); - if (fs.existsSync(specificPath)) { - fs.rmSync(specificPath, { recursive: true, force: true }); - } - } - - const mainCachePath = path.join(this.outDir, 'fanqie'); - if (fs.existsSync(mainCachePath)) { - fs.readdirSync(mainCachePath).forEach((file) => { - const fullPath = path.join(mainCachePath, file); - const stat = fs.statSync(fullPath); - if (stat.isDirectory()) { - fs.rmSync(fullPath, { recursive: true, force: true }); - } else { - fs.unlinkSync(fullPath); - } - }); - } - - if (!isScheduled && e) e.reply('缓存清理完成', true); - return true; - } - - /** - * 解析网页链接中的 book_id - */ - async handleFanqieLink(e) { - const message = e.msg.trim(); - let bookId = null; - - try { - if (message.includes('changdunovel.com')) { - bookId = message.match(/book_id=(\d+)/)[1]; - } else { - bookId = message.match(/page\/(\d+)/)[1]; - } - } catch { - return e.reply('解析失败,请检查链接是否正确', true); - } - - return this.downloadFanqieBook(e, bookId); - } - - /** - * 使用 #fq下载 命令下载 - */ - async downloadByBookId(e) { - const bookId = e.msg.replace(/^#?fq下载/, '').trim(); - return this.downloadFanqieBook(e, bookId); - } - - /** - * 执行下载并上传文件 - */ - async downloadFanqieBook(e, bookId) { - await this.initPromise; - - let bookInfo; - try { - bookInfo = await this.fanqieClient.get_info(bookId); - } catch (err) { - logger.error(err); - return e.reply('获取小说信息失败', true); - } - - if (!bookInfo) return e.reply('获取失败,请稍后再试', true); - - e.reply( - `识别小说:[番茄小说]《${bookInfo.book_name}》\n作者:${bookInfo.author}\n原名:${bookInfo.original_book_name}`, - true - ); - - e.reply('开始下载,请稍等片刻...', true); - const startTime = Date.now(); - - try { - await this.fanqieClient.down(bookId, e.message_id); - } catch (err) { - logger.error(err); - return e.reply('下载失败,请稍后重试', true); - } - - const outPath = path.join(this.outDir, 'files', String(e.message_id)); - let finalFilePath = await this.waitForOutputFile(outPath); - if (!finalFilePath) return e.reply('下载超时', true); - - // 文件重命名防止空格 - const safePath = finalFilePath.replace(/ /g, '_'); - if (finalFilePath !== safePath) { - try { - fs.renameSync(finalFilePath, safePath); - finalFilePath = safePath; - } catch (err) { - logger.error(`重命名失败:${err.stack}`); - return e.reply('重命名失败', true); - } - } - - const uploaded = await this.sendFileToUser(e, finalFilePath); - await this.clearFanqieCache(false, true, String(e.message_id)); - - if (!uploaded) return e.reply('上传失败', true); - - e.reply(`《${bookInfo.book_name}》上传成功,耗时 ${(Date.now() - startTime) / 1000}s`); - return true; - } - - /** - * 上传文件至群或私聊 - */ - async sendFileToUser(e, filePath) { - try { - const fileName = path.basename(filePath); - if (e.isGroup) { - return await e.bot.sendApi('upload_group_file', { - group_id: e.group_id, - file: filePath, - name: fileName, - }); - } else if (e.friend) { - return await e.bot.sendApi('upload_private_file', { - user_id: e.user_id, - file: filePath, - name: fileName, - }); - } - } catch (err) { - logger.error(`文件上传失败:${logger.red(err.stack)}`); - e.reply(`上传失败:${err.message}`, true); - return null; - } - } -} diff --git a/components/date.js b/components/date.js deleted file mode 100644 index 2a4b254..0000000 --- a/components/date.js +++ /dev/null @@ -1,3 +0,0 @@ -let date = {}; //咕咕咕 - -export default date; diff --git a/components/tool.js b/components/tool.js index 98233d0..0687703 100644 --- a/components/tool.js +++ b/components/tool.js @@ -7,15 +7,6 @@ let tools = { sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }, - - /** - * 生成指定范围内的随机整数 - * @param {number} min - 最小值 - * @param {number} max - 最大值 - */ - randomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; - }, }; export default tools; diff --git a/config/fanqieConfig.json b/config/fanqieConfig.json deleted file mode 100644 index ef4fcf7..0000000 --- a/config/fanqieConfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "url": "http://127.0.0.1:6868", - "outDir": "/home/user/debian/cache/Downloads" -} diff --git a/modules/apps/fanqie/fanqie.js b/modules/apps/fanqie/fanqie.js deleted file mode 100644 index 8127677..0000000 --- a/modules/apps/fanqie/fanqie.js +++ /dev/null @@ -1,38 +0,0 @@ -import axios from 'axios'; - -class Fanqie { - constructor(apiurl) { - this.apiurl = apiurl; - } - - async get_info(book_id) { - try { - let url = `${this.apiurl}/api/info?book_id=${book_id}&source=fanqie`; - let res = await axios.get(url); - if (res.status !== 200 || !res.data) throw new Error('请求失败或无数据'); - let result = res.data['data']; - if (!result) throw new Error('data 字段不存在'); - return { - author: result.author, - book_name: result.book_name, - original_book_name: result.original_book_name, - }; - } catch (e) { - logger.error(e); - return false; - } - } - async down(book_id, msg_id) { - try { - let url = `${this.apiurl}/api/down?book_id=${book_id}&source=fanqie&type=txt&user_id=${msg_id}`; - // 发送get请求 - await axios.get(url); - return true; - } catch (e) { - logger.error(e); - return false; - } - } -} - -export default Fanqie; diff --git a/utils/pinyin.js b/utils/pinyin.js deleted file mode 100644 index 9957cf1..0000000 --- a/utils/pinyin.js +++ /dev/null @@ -1,72 +0,0 @@ -import pinyin from 'pinyin-pro'; - -class PinyinUtils { - /** - * 将中文转化为拼音 - * @param text 文本 - * @param toneType none - * @returns {*|string} - */ - static toPinyin(text, toneType = 'none') { - try { - return pinyin.pinyin(text, { - toneType, - type: 'string', - nonZh: 'consecutive' - }); - } catch (error) { - logger.error(`[crystelf-ai] 拼音转换失败: ${error.message}`); - return text; - } - } - - /** - * 检查文本是否包含拼音关键词 - * @param text - * @param pinyinKeywords - * @returns {{keyword: *, matched: boolean, type: string}|null} - */ - static matchPinyin(text, pinyinKeywords) { - if (!text || !pinyinKeywords || pinyinKeywords.length === 0) { - return null; - } - const textPinyin = this.toPinyin(text.toLowerCase()); - for (const keyword of pinyinKeywords) { - if (textPinyin.includes(keyword.toLowerCase())) { - return { - keyword, - matched: true, - type: 'pinyin' - }; - } - } - return null; - } - - /** - * 检查文本是否包含关键词 - * @param text 文本 - * @param chineseKeywords 中文关键词数组 - * @param pinyinKeywords 拼音关键词数组 - * @returns {{keyword: *, matched: boolean, type: string}|null|{keyword: *, matched: boolean, type: string}} - */ - static matchKeywords(text, chineseKeywords = [], pinyinKeywords = []) { - if (!text) return null; - const lowerText = text.toLowerCase(); - for (const keyword of chineseKeywords) { - if (lowerText.includes(keyword.toLowerCase())) { - return { - keyword, - matched: true, - type: 'chinese' - }; - } - } - if (pinyinKeywords.length > 0) { - return this.matchPinyin(text, pinyinKeywords); - } - return null; - } -} - -export default PinyinUtils;