import fs from 'fs'; import path from 'path'; import { exec } from 'child_process'; import util from 'util'; import configControl from '../config/configControl.js'; const execAsync = util.promisify(exec); export default class NapcatService { constructor() { const config = configControl.get('config')?.napcat || {}; this.basePath = config.basePath; this.userPath = config.userPath; if (!this.basePath || !this.userPath) { logger.error('[crystelf-admin] 未检测到napcat配置..'); } this.qrPath = path.join(this.basePath, 'cache', 'qrcode.png'); this.configPath = path.join(this.basePath, 'config'); } /** * nc登录方法 * @param qq qq号 * @param nickname 昵称 * @returns {Promise} */ async login(qq, nickname) { const shFile = path.join(this.userPath, `${qq}.sh`); if (!fs.existsSync(this.userPath)) { fs.mkdirSync(this.userPath, { recursive: true }); } const userConfigFile = path.join(this.configPath, `onebot11_${qq}.json`); if (!fs.existsSync(userConfigFile)) { try { const defaultConfigFile = path.join(configControl.get('config')?.path || '', 'napcat.json'); if (!fs.existsSync(defaultConfigFile)) { logger.error(`[crystelf-admin] 默认配置文件不存在: ${defaultConfigFile}`); } fs.copyFileSync(defaultConfigFile, userConfigFile); logger.info(`[crystelf-admin] 已复制默认配置到 ${userConfigFile}`); } catch (err) { logger.error(`[crystelf-admin] 复制默认配置失败: ${err.message}`); } } if (!fs.existsSync(shFile)) { const scriptContent = `#!/bin/bash\ncd "${this.basePath}"\n./napcat --qq ${qq}\n`; fs.writeFileSync(shFile, scriptContent, { mode: 0o755 }); } try { await execAsync(`tmux has-session -t ${nickname}`); // 存在就先干掉 await execAsync(`tmux kill-session -t ${nickname}`); await execAsync(`tmux new -s ${nickname} -d "bash '${shFile}'"`); } catch { // 不存在再新建 await execAsync(`tmux new -s ${nickname} -d "bash '${shFile}'"`); } return await this.waitForQrUpdate(); } /** * 等待qrcode图像更新 * @param timeout * @returns {Promise} */ async waitForQrUpdate(timeout = 30000) { if (!fs.existsSync(this.qrPath)) { return 'none'; } let lastMtime = fs.statSync(this.qrPath).mtimeMs; return new Promise((resolve) => { let resolved = false; const timer = setTimeout(() => { if (!resolved) { resolved = true; watcher.close(); resolve(undefined); } }, timeout); const watcher = fs.watch(this.qrPath, (eventType) => { if (eventType === 'change') { const stat = fs.statSync(this.qrPath); if (stat.mtimeMs !== lastMtime) { lastMtime = stat.mtimeMs; if (!resolved) { resolved = true; clearTimeout(timer); watcher.close(); resolve(this.qrPath); } } } }); }); } /** * 断开nc连接 * @param nickname 昵称 * @returns {Promise} */ async disconnect(nickname) { try { await execAsync(`tmux kill-session -t ${nickname}`); return `已关闭会话: ${nickname}`; } catch (err) { return `关闭会话失败: ${err.message}`; } } /** * qq是否登录成功 * @param qq * @returns {Promise} */ async checkStatus(qq) { return Bot.uin.includes(qq); } }