UNPKG

xincbot

Version:

A flexible QQ bot framework based on NapCat and node-napcat-ts

706 lines 30.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PluginManager = void 0; exports.definePlugin = definePlugin; const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const node_napcat_ts_1 = require("node-napcat-ts"); const config_1 = require("./config"); const logger_1 = require("./logger"); class PluginManager { plugins = new Map(); cleanupFns = new Map(); eventHandlers = new Map(); // 新增:记录插件的事件处理器 eventBus; dataDir; configDir; startTime = Date.now(); constructor(ws, dataDir, configDir) { this.eventBus = ws; this.dataDir = dataDir; this.configDir = configDir; } async loadPlugin(plugin) { // 类型检查 if (!plugin || typeof plugin !== 'object') { throw new Error('Invalid plugin: plugin must be an object'); } if (!plugin.name || typeof plugin.name !== 'string') { throw new Error('Invalid plugin: plugin.name must be a string'); } if (plugin.setup && typeof plugin.setup !== 'function') { throw new Error('Invalid plugin: plugin.setup must be a function'); } // 检查是否已加载 if (this.plugins.has(plugin.name)) { throw new Error(`Plugin ${plugin.name} is already loaded`); } const ctx = { eventBus: this.eventBus, handle: (event, handler) => { // 记录事件处理器 if (!this.eventHandlers.has(plugin.name)) { this.eventHandlers.set(plugin.name, new Set()); } this.eventHandlers.get(plugin.name).add(handler); this.eventBus.on(event, handler); }, off: (event, handler) => { this.eventBus.off(event, handler); }, getDataPath: () => { const dir = path_1.default.join(this.dataDir, plugin.name); if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } return dir; }, getConfigPath: () => { const dir = path_1.default.join(this.configDir, plugin.name); if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } return dir; }, getText: (e) => { // 处理消息数组 if (Array.isArray(e.message)) { const text = e.message .filter((msg) => msg.type === 'text') .map((msg) => msg.data.text) .join(''); return text.trim(); } return ''; }, getAvatarLink: (qq) => { return `https://thirdqq.qlogo.cn/headimg_dl?dst_uin=${qq}&spec=0`; }, getGroupLink: (group_id) => { return `https://p.qlogo.cn/gh/${group_id}/${group_id}/0`; }, getQuoteMsg: async (e) => { try { // 检查是否存在引用消息 const reply = e.message?.find((msg) => msg.type === 'reply'); if (!reply) return null; // 获取引用消息ID const messageId = reply.data.id; if (!messageId) return null; // 获取消息详情 try { const result = await this.eventBus.get_msg({ message_id: parseInt(messageId) }); return result; } catch (error) { console.error('Failed to get message by ID:', error); return null; } } catch (error) { console.error('Failed to get quote message:', error); return null; } }, getImageURL: (e) => { const imageMsg = e.message.find((msg) => msg.type === 'image'); return imageMsg ? imageMsg.data.url : null; }, getQuoteImageURL: async (e) => { const quoteMsg = await ctx.getQuoteMsg(e); if (!quoteMsg) return null; return ctx.getImageURL(quoteMsg); }, getMentionedImageURL: async (e) => { const directImageUrl = ctx.getImageURL(e); if (directImageUrl) return directImageUrl; const quoteImageUrl = await ctx.getQuoteImageURL(e); return quoteImageUrl; }, getTaggedUserID: (e) => { const atMessages = e.message.filter((msg) => msg.type === 'at'); if (atMessages.length === 0) return null; return atMessages[atMessages.length - 1].data.qq; }, uploadFileToDir: async (group_id, file, name) => { try { await this.eventBus.upload_group_file({ group_id: Number(group_id), file, name: name || path_1.default.basename(file), folder_id: "/" }); } catch (error) { throw new Error(`Failed to upload file: ${error instanceof Error ? error.message : String(error)}`); } }, isRoot: (e) => { const config = config_1.configManager.getConfig(); const rootList = config.root || []; return rootList.includes(String(e.user_id)); }, isAdmin: (e) => { const config = config_1.configManager.getConfig(); const adminList = config.admin || []; // 主人也是管理员 return ctx.isRoot(e) || adminList.includes(String(e.user_id)); }, isGroupOwner: (e) => { return e.message_type === 'group' && e.sender.role === 'owner'; }, isGroupAdmin: (e) => { return e.message_type === 'group' && (e.sender.role === 'owner' || e.sender.role === 'admin'); }, getLoadedPlugins: () => { return Array.from(this.plugins.values()); }, reloadPlugin: async (name) => { await this.unloadPlugin(name); const pluginPath = path_1.default.join(process.cwd(), 'plugins', name); const plugin = (await Promise.resolve(`${path_1.default.join(pluginPath, 'index.ts')}`).then(s => __importStar(require(s)))).default; await this.loadPlugin(plugin); }, disablePlugin: async (name) => { if (!this.plugins.has(name)) { throw new Error(`Plugin ${name} not found`); } await this.unloadPlugin(name); }, enablePlugin: async (name) => { const pluginPath = path_1.default.join(process.cwd(), 'plugins', name, 'index.ts'); const devPluginPath = path_1.default.join(process.cwd(), 'plugins', name, 'index.ts'); // 检查插件文件是否存在 if (!fs_1.default.existsSync(devPluginPath)) { throw new Error(`找不到插件 ${name}`); } try { // 尝试导入编译后的文件 const pluginModule = await Promise.resolve(`${pluginPath}`).then(s => __importStar(require(s))); const plugin = pluginModule.default; if (!plugin || !plugin.name) { throw new Error(`插件 ${name} 格式不正确`); } await this.loadPlugin(plugin); // 更新配置文件 const config = config_1.configManager.getConfig(); config.plugins = Array.from(new Set([...(config.plugins || []), name])); config_1.configManager.saveConfig(); } catch (importError) { // 如果导入失败,尝试使用 require try { const plugin = require(devPluginPath).default; if (!plugin || !plugin.name) { throw new Error(`插件 ${name} 格式不正确`); } await this.loadPlugin(plugin); // 更新配置文件 const config = config_1.configManager.getConfig(); config.plugins = Array.from(new Set([...(config.plugins || []), name])); config_1.configManager.saveConfig(); } catch (requireError) { throw new Error(`无法加载插件: ${requireError.message}`); } } }, getStatus: async () => { const uptime = this.formatUptime(Date.now() - this.startTime); const memory = Math.round(process.memoryUsage().heapUsed / 1024 / 1024); const pluginCount = this.plugins.size; // 获取活跃群组数 const groups = await this.eventBus.get_group_list(); const groupCount = groups.length; return { uptime, memory, pluginCount, groupCount }; }, shutdown: async () => { // 卸载所有插件 for (const name of this.plugins.keys()) { await this.unloadPlugin(name); } // 断开连接 this.eventBus.disconnect(); // 退出进程 process.exit(0); }, recallMsg: async (message_id) => { try { await this.eventBus.delete_msg({ message_id }); } catch (error) { throw new Error(`Failed to recall message: ${error instanceof Error ? error.message : String(error)}`); } }, respond: async (e, message) => { try { const formattedMessage = message.map((msg) => typeof msg === 'string' ? node_napcat_ts_1.Structs.text(msg) : msg); let result; if (e.message_type === 'group') { result = await this.eventBus.send_group_msg({ group_id: e.group_id, message: formattedMessage }); // 添加日志 const replyText = formattedMessage.map((msg) => msg.type === 'text' ? msg.data.text : `[${msg.type}]`).join(''); logger_1.logger.info(`succeed to send: [Group(${e.group_id})] ${replyText}`); } else { result = await this.eventBus.send_private_msg({ user_id: e.user_id, message: formattedMessage }); // 添加日志 const replyText = formattedMessage.map((msg) => msg.type === 'text' ? msg.data.text : `[${msg.type}]`).join(''); logger_1.logger.info(`succeed to send: [Private(${e.user_id})] ${replyText}`); } return result; } catch (error) { throw new Error(`Failed to send message: ${error instanceof Error ? error.message : String(error)}`); } }, sendLike: async (user_id, times = 50) => { try { // 确保次数在1-50之间 const count = Math.max(1, Math.min(50, times)); await this.eventBus.send_like({ user_id: Number(user_id), times: count }); } catch (error) { throw new Error(`Failed to send like: ${error instanceof Error ? error.message : String(error)}`); } }, kick: async (group_id, user_id, reject_add_request = false) => { try { await this.eventBus.set_group_kick({ group_id: Number(group_id), user_id: Number(user_id), reject_add_request }); } catch (error) { throw new Error(`Failed to kick member: ${error instanceof Error ? error.message : String(error)}`); } }, mute: async (group_id, user_id, duration = 1800) => { try { await this.eventBus.set_group_ban({ group_id: Number(group_id), user_id: Number(user_id), duration: Math.max(0, duration) // 确保时长不为负数 }); } catch (error) { throw new Error(`Failed to mute member: ${error instanceof Error ? error.message : String(error)}`); } }, muteGroup: async (group_id, enable = true) => { try { await this.eventBus.set_group_whole_ban({ group_id: Number(group_id), enable }); } catch (error) { throw new Error(`Failed to set group mute: ${error instanceof Error ? error.message : String(error)}`); } }, setAdmin: async (group_id, user_id, enable = true) => { try { await this.eventBus.set_group_admin({ group_id: Number(group_id), user_id: Number(user_id), enable }); } catch (error) { throw new Error(`Failed to set admin: ${error instanceof Error ? error.message : String(error)}`); } }, setGroupCard: async (group_id, user_id, card) => { try { await this.eventBus.set_group_card({ group_id: Number(group_id), user_id: Number(user_id), card }); } catch (error) { throw new Error(`Failed to set group card: ${error instanceof Error ? error.message : String(error)}`); } }, setGroupName: async (group_id, group_name) => { try { await this.eventBus.set_group_name({ group_id: Number(group_id), group_name }); } catch (error) { throw new Error(`Failed to set group name: ${error instanceof Error ? error.message : String(error)}`); } }, quitGroup: async (group_id) => { try { await this.eventBus.set_group_leave({ group_id: Number(group_id) }); } catch (error) { throw new Error(`Failed to quit group: ${error instanceof Error ? error.message : String(error)}`); } }, setTitle: async (group_id, user_id, special_title) => { try { await this.eventBus.set_group_special_title({ group_id: Number(group_id), user_id: Number(user_id), special_title }); } catch (error) { throw new Error(`Failed to set group title: ${error instanceof Error ? error.message : String(error)}`); } }, getGroupInfo: async (group_id) => { try { const info = await this.eventBus.get_group_info({ group_id: Number(group_id) }); return { group_id: info.group_id, group_name: info.group_name, member_count: info.member_count, max_member_count: info.max_member_count }; } catch (error) { throw new Error(`Failed to get group info: ${error instanceof Error ? error.message : String(error)}`); } }, getGroupList: async () => { try { const groups = await this.eventBus.get_group_list(); return groups.map(group => ({ group_id: group.group_id, group_name: group.group_name, member_count: group.member_count, max_member_count: group.max_member_count })); } catch (error) { throw new Error(`Failed to get group list: ${error instanceof Error ? error.message : String(error)}`); } }, getGroupMemberInfo: async (group_id, user_id, no_cache) => { try { const info = await this.eventBus.get_group_member_info({ group_id: Number(group_id), user_id: Number(user_id), no_cache }); return { group_id: info.group_id, user_id: info.user_id, nickname: info.nickname, card: info.card, role: info.role, title: info.title, join_time: info.join_time, last_sent_time: info.last_sent_time }; } catch (error) { throw new Error(`Failed to get member info: ${error instanceof Error ? error.message : String(error)}`); } }, getCookie: async (domain) => { try { const info = await this.eventBus.get_cookies({ domain }); return { cookies: info.cookies, bkn: info.bkn }; } catch (error) { throw new Error(`Failed to get cookies: ${error instanceof Error ? error.message : String(error)}`); } }, getCsrfToken: async () => { try { const info = await this.eventBus.get_csrf_token(); return info.token; } catch (error) { throw new Error(`Failed to get CSRF token: ${error instanceof Error ? error.message : String(error)}`); } }, getCredentials: async () => { try { const info = await this.eventBus.get_credentials(); return { cookies: info.cookies, token: info.token }; } catch (error) { throw new Error(`Failed to get credentials: ${error instanceof Error ? error.message : String(error)}`); } }, getVersionInfo: async () => { try { const info = await this.eventBus.get_version_info(); return { app_name: info.app_name, app_version: info.app_version, protocol_version: info.protocol_version }; } catch (error) { throw new Error(`Failed to get version info: ${error instanceof Error ? error.message : String(error)}`); } }, getAiRoleList: async (group_id) => { try { const result = await this.eventBus.get_ai_characters({ group_id: Number(group_id) }); return result; } catch (error) { throw new Error(`Failed to get AI characters: ${error instanceof Error ? error.message : String(error)}`); } }, sendGroupAiRecord: async (group_id, character, text) => { try { const result = await this.eventBus.send_group_ai_record({ group_id: Number(group_id), character, text }); return result; } catch (error) { throw new Error(`Failed to send AI voice record: ${error instanceof Error ? error.message : String(error)}`); } }, wsSend: async (action, params) => { try { const json = { action: action, params: params, echo: `${action}_${Date.now()}` }; return await this.eventBus.send(action, params); } catch (error) { throw new Error(`Failed to send WebSocket message: ${error instanceof Error ? error.message : String(error)}`); } }, getPlugins: () => { // 获取插件目录下的所有插件 const pluginsDir = path_1.default.join(process.cwd(), 'plugins'); const allPlugins = fs_1.default.readdirSync(pluginsDir) .filter(name => fs_1.default.statSync(path_1.default.join(pluginsDir, name)).isDirectory()) .map(name => { try { // 尝试获取插件信息 const pluginPath = path_1.default.join(pluginsDir, name, 'index.ts'); if (fs_1.default.existsSync(pluginPath)) { // 检查是否已加载 const loadedPlugin = this.plugins.get(name); if (loadedPlugin) { return { ...loadedPlugin, enabled: true }; } // 如果未加载,返回基本信息 return { name, enabled: false }; } return null; } catch (error) { return null; } }) .filter((p) => p !== null); return allPlugins; } }; const cleanup = await plugin.setup?.(ctx); if (typeof cleanup === 'function') { this.cleanupFns.set(plugin.name, cleanup); } this.plugins.set(plugin.name, plugin); } async unloadPlugin(name) { try { const handlers = this.eventHandlers.get(name); if (handlers) { for (const handler of handlers) { this.eventBus.off('message', handler); // 使用正确的事件类型 const events = ['notice', 'request', 'meta_event']; for (const event of events) { this.eventBus.off(event, handler); } } this.eventHandlers.delete(name); } // 2. 执行清理函数 const cleanup = this.cleanupFns.get(name); if (cleanup) { await Promise.resolve(cleanup()); this.cleanupFns.delete(name); } // 3. 从内存中移除插件 this.plugins.delete(name); logger_1.logger.info(`Plugin ${name} unloaded successfully`); } catch (error) { throw new Error(`Failed to unload plugin: ${error.message}`); } } formatUptime(ms) { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); return `${days}${hours % 24}${minutes % 60}${seconds % 60}秒`; } getLoadedPlugins() { return Array.from(this.plugins.values()); } findPlugin(name) { // 直接匹配 let plugin = this.plugins.get(name); // 不区分大小写匹配 if (!plugin) { for (const [key, value] of this.plugins.entries()) { if (key.toLowerCase() === name.toLowerCase()) { plugin = value; break; } } } return plugin; } async reloadPlugin(name) { try { // 1. 先禁用 await this.disablePlugin(name); // 2. 再启用 await this.enablePlugin(name); logger_1.logger.info(`Plugin ${name} reloaded successfully`); } catch (error) { throw new Error(`重载插件失败: ${error.message}`); } } async enablePlugin(name) { try { // 检查插件是否已启用 if (this.plugins.has(name)) { throw new Error(`插件 ${name} 已经启用`); } // 统一使用正确的插件路径 const pluginPath = path_1.default.join(process.cwd(), 'plugins', name, 'index.ts'); if (!fs_1.default.existsSync(pluginPath)) { throw new Error(`找不到插件文件: ${pluginPath}`); } // 导入插件 const plugin = (await Promise.resolve(`${`${pluginPath}?t=${Date.now()}`}`).then(s => __importStar(require(s)))).default; await this.loadPlugin(plugin); // 更新配置 const config = config_1.configManager.getConfig(); if (!config.plugins.includes(name)) { config.plugins.push(name); config_1.configManager.saveConfig(); } logger_1.logger.info(`Plugin ${name} enabled successfully`); } catch (error) { throw new Error(`启用插件失败: ${error.message}`); } } async disablePlugin(name) { try { // 1. 检查插件是否存在 const plugin = this.plugins.get(name); if (!plugin) { throw new Error(`找不到插件 ${name}`); } // 2. 卸载插件 await this.unloadPlugin(name); // 3. 更新配置文件 const config = config_1.configManager.getConfig(); config.plugins = config.plugins.filter(p => p !== name); config_1.configManager.saveConfig(); logger_1.logger.info(`Plugin ${name} disabled successfully`); } catch (error) { throw new Error(`禁用插件失败: ${error.message}`); } } } exports.PluginManager = PluginManager; function definePlugin(plugin) { console.log(`定义插件: ${plugin.name}`); return plugin; } //# sourceMappingURL=plugin.js.map