UNPKG

cyberbot-next

Version:

cyberbot, 基于napcat-ts, nodejs,轻量qq机器人框架。

643 lines (582 loc) 22.7 kB
#!/usr/bin/env node import * as fs from 'fs'; import * as path from 'path'; import * as readline from 'readline'; const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); const questions = [ { name: 'master', message: '请输入主人qq:' }, { name: 'admins', message: '请输入管理员qq(空格分隔):' }, { name: 'bot', message: '请输入机器人qq:' }, { name: 'baseUrl', message: '请输入napcat服务端正向websocket地址(示例:ws://127.0.0.1:3001):' }, { name: 'accessToken', message: '请输入napcat webui内你自己设置的accessToken:' } ]; function createPluginDir(projectDir, pluginName) { const pluginPath = path.join(projectDir, 'plugins', pluginName, 'index.ts'); const pluginDir = path.dirname(pluginPath); if (!fs.existsSync(pluginDir)) { fs.mkdirSync(pluginDir, { recursive: true }); } return pluginPath; } function saveConfig(projectDir, config) { const jsonconfig = { bot: parseInt(config.bot), master: parseInt(config.master), admins: config.admins.split(/\s+/).map(id => parseInt(id.trim())).filter(id => !isNaN(id)), baseUrl: config.baseUrl, accessToken: config.accessToken, throwPromise: true, debug: false, reconnection: { enable: true, attempts: 10, delay: 5000, }, plugins: { system: ["cmds"], user: ["demo"] }, logger: { level: "info", maxSize: "10m", maxDays: 7 } }; fs.writeFileSync(path.join(projectDir, 'config.json'), JSON.stringify(jsonconfig, null, 2), 'utf-8'); console.log(`配置文件已生成 ${path.join(projectDir, 'config.json')}`); const packageJson = { type: "module", scripts: { start: "node app.js" }, dependencies: { "axios": "^1.8.4", "jiti": "^2.4.2", "log4js": "^6.9.1", "node-cron": "^3.0.3", "node-napcat-ts": "^0.4.9", "systeminformation": "^5.25.11", "cyberbot-next": "^0.4.0" } }; const packagePath = path.join(projectDir, 'package.json'); fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); console.log('已生成package.json文件'); const appJsContent = `import { BootStrap } from "cyberbot-next";\nBootStrap();`; const appJsPath = path.join(projectDir, 'app.js'); fs.writeFileSync(appJsPath, appJsContent); console.log('已生成入口文件app.js'); const demoPluginPath = createPluginDir(projectDir, 'demo'); const cmdsPluginPath = createPluginDir(projectDir, 'cmds'); const demoPluginContent = ` import { CybePlugin, logger, Structs, AllHandlers, http } from 'cyberbot-next'; /** * 示例插件 * 展示如何使用基础插件类 */ export class UserPlugin extends CybePlugin<AllHandlers['message']> { name = 'demo'; version = '1.0.0'; description = '这是一个示例插件,展示如何处理不同类型的消息和定时任务'; setup() { // 处理全部消息 this.registerMessageHandler('message', async(e: AllHandlers['message']) => { if (e.raw_message === 'hello') { const { message_id } = await this.reply('word'); setTimeout(() => { this.client.deleteMessage(message_id); }, 1000);// 1秒后撤回消息 } if (e.raw_message === 'love') { // 复杂消息消息可以使用数组组合 await this.reply(['爱你哟 ', Structs.face(66)]) } if (e.raw_message === '壁纸') { // 第一个参数是图片的 URL,第二个参数是是否使用缓存,true 为使用缓存,false 为不使用缓存 await this.reply([Structs.image('https://p2.qpic.cn/gdynamic/m7yRCticIwlKMnXkIat8nNRyD95wf24YNBoiblNYKYdXs/0')]) } if (e.raw_message === '一言') { const { data } = await http.get('https://v1.hitokoto.cn/') await this.reply(data.hitokoto, true) } }); // 处理私聊消息 this.registerMessageHandler('message.private', async (e: AllHandlers['message.private']) => { // 处理私聊消息 if(e.raw_message == "私聊"){ await this.reply("私聊消息") } }); // 处理群聊消息 this.registerMessageHandler('message.group', async (e: AllHandlers['message.group']) => { // 处理群消息 if(e.raw_message === "群消息"){ await this.reply("这是一条群消息") } }); // 处理通知消息 this.registerMessageHandler('notice', (e: AllHandlers['notice']) => { // 处理所有通知:好友、群的数量增加与减少、戳一戳、撤回,以及群的签到、禁言、管理变动、转让等等 // console.log('收到通知:', JSON.stringify(e)); }); // 处理请求消息 this.registerMessageHandler('request', (e: AllHandlers['request']) => { // 处理所有请求:好友、群,添加好友、邀请入群等等 // 群组相关请求 if (e.request_type === 'group') { // 自动同意群邀请或加群请求 this.client.aprroveGroup(e.flag); console.log('已自动同意群组请求'); } // 好友相关请求可以在这里处理 if (e.request_type === 'friend') { // 处理好友请求 } }); } // 定义定时任务 // cron: [string, (e: AllHandlers['message']) => void][] = [ // [ // '*/5 * * * * *', // 每5秒执行一次 // (e) => { // this.client.sendPrivateMessage(12345678, '每5秒执行一次') // }, // ], // [ // '0 */1 * * * *', // 每分钟执行一次 // (ctx) => { // logger.info(\`[\${this.name}] 定时任务执行: 每分钟执行一次\`); // }, // ], // ]; } `; const cmdsPluginContent = ` /*** * @author: @星火 * @description: * 本文件是机器人框架的核心命令插件,负责处理所有与机器人命令相关的逻辑。此插件不可删除,因为它是控制和管理机器人的基础。 * * 主要功能: * - 提供管理员权限验证机制,确保只有授权用户可以执行敏感操作。 * - 实现了对插件的启停、禁用及重新加载等命令的支持,通过解析用户输入的命令参数来动态调整配置。 * - 提供详细的错误处理机制,确保在命令执行失败时能够及时反馈给用户。 * - 提供对插件的安装、卸载、更新等命令的支持,通过解析用户输入的命令参数来动态调整配置。 */ import { CybePlugin, logger, AllHandlers, FsSizeData } from 'cyberbot-next'; import os from 'os'; import { fsSize } from 'systeminformation'; /** * 示例插件 * 展示如何使用基础插件类 */ export class CmdsPlugin extends CybePlugin { name = 'cmds'; version = '1.0.0'; description = '这是核心命令插件'; setup() { // 处理私聊消息 this.registerMessageHandler('message', async (context: AllHandlers['message']) => { if (!('message' in context)) return; const message = context.raw_message.trim(); if (!message.startsWith('#')) return; // 检查权限 if (!this.client.hasRight(context.user_id)) { await this.reply('您没有权限执行此命令', true); return; } const [cmd, ...args] = message.slice(1).split(' '); switch (cmd) { case '帮助': await this.showHelp(context); break; case '插件': await this.handlePluginCommand(context, args); break; case '设置': if (!this.client.isMaster(context.user_id)) { await this.reply('您没有权限执行此命令', true); return; } await this.handleSettingCommand(context, args); break; case '状态': await this.showStatus(context); break; case '更新': await this.updateFramework(context); break; case '退出': if (!this.client.isMaster(context.user_id)) { await this.reply('您没有权限执行此命令', true); return; } await this.exitFramework(context); break; } }); } private async showHelp(context: AllHandlers['message']) { const helpText = [ '〓 💡 CyberBot 帮助 〓', '#帮助 👉 显示帮助信息', '#插件 👉 框架插件管理', '#设置 👉 框架设置管理', '#状态 👉 显示框架状态', '#更新 👉 更新框架版本', '#退出 👉 退出框架进程' ].join('\\n'); await this.reply(helpText, true); } private async showPluginHelp(context: AllHandlers['message']) { const helpText = [ '〓 🧩 Bot 插件管理 〓', '#插件 列表 👉 显示所有插件', '#插件 启用 <插件名>', '#插件 禁用 <插件名>', '#插件 重载 <插件名>' ].join('\\n'); await this.reply(helpText, true); } private async showSettingHelp(context: AllHandlers['message']) { const helpText = [ '〓 ⚙️ Bot 设置管理 〓', '#设置 详情', '#设置 加管理 <QQ/AT>', '#设置 删管理 <QQ/AT>' ].join('\\n'); await this.reply(helpText, true); } private async handlePluginCommand(context: AllHandlers['message'], args: string[]) { if (args.length === 0) { await this.showPluginHelp(context); return; } const [action, pluginName] = args; switch (action) { case '列表': // 获取所有插件信息并展示 await this.showPluginList(context); break; case '启用': if (!pluginName) { await this.reply('请指定要启用的插件名称', true); return; } // 通过反射获取 pluginManager const pluginManager = (this.client as any).pluginManager; if (!pluginManager) { await this.reply('无法获取插件管理器', true); return; } // 启用插件 if (pluginManager.enablePlugin(pluginName)) { await this.reply(\`✅ 插件 \${pluginName} 已启用\`, true); } else { await this.reply(\`❌ 无法启用插件 \${pluginName},可能该插件不存在或已经启用\`, true); } break; case '禁用': if (!pluginName) { await this.reply('请指定要禁用的插件名称', true); return; } // 通过反射获取 pluginManager const pluginManager2 = (this.client as any).pluginManager; if (!pluginManager2) { await this.reply('无法获取插件管理器', true); return; } // 禁用插件 if (pluginManager2.disablePlugin(pluginName)) { await this.reply(\`✅ 插件 \${pluginName} 已禁用\`, true); } else { await this.reply(\`❌ 无法禁用插件 \${pluginName},可能该插件不存在、已经禁用或是系统插件\`, true); } break; case '重载': if (!pluginName) { await this.reply('请指定要重载的插件名称', true); return; } // 通过反射获取 pluginManager const pluginManager3 = (this.client as any).pluginManager; if (!pluginManager3) { await this.reply('无法获取插件管理器', true); return; } // 重载插件 if (pluginManager3.reloadPlugin(pluginName)) { await this.reply(\`✅ 插件 \${pluginName} 已重载\`, true); } else { await this.reply(\`❌ 无法重载插件 \${pluginName},可能该插件不存在\`, true); } break; default: await this.showPluginHelp(context); } } /** * 显示插件列表 */ private async showPluginList(context: AllHandlers['message']) { // 通过反射获取 pluginManager const pluginManager = (this.client as any).pluginManager; if (!pluginManager) { await this.reply('无法获取插件管理器', true); return; } const pluginInfo = pluginManager.getPluginInfo(); if (pluginInfo.length === 0) { await this.reply('当前没有加载任何插件', true); return; } const lines = ['〓 🧩 CyberBot 插件 〓']; // 先按照插件类型排序(内置在前),然后按名称排序 pluginInfo.sort((a, b) => { // 首先按类型排序,内置插件在前 if (a.isSystem !== b.isSystem) { return a.isSystem ? -1 : 1; } // 然后按名称字母顺序排序 return a.name.localeCompare(b.name); }).forEach(plugin => { const statusIcon = plugin.enabled ? '🟢' : '🔴'; const pluginType = plugin.isSystem ? '(内置)' : '(用户)'; lines.push(\`\${statusIcon} \${plugin.name}-\${plugin.version} \${pluginType}\`); }); await this.reply(lines.join('\\n'), true); } private async handleSettingCommand(context: AllHandlers['message'], args: string[]) { if (args.length === 0) { await this.showSettingHelp(context); return; } const [action, ...params] = args; const config = this.client.getConfig(); switch (action) { case '详情': // TODO: 实现设置详情显示 const text = ['〓 ⚙️ Bot 设置 〓','主人: ' + config.master, '管理员: ' + config.admins.join(',')].join('\\n'); await this.reply(text); break; case '加管理': case '删管理': // 提取用户ID的辅助函数 const extractUserId = () => { // 优先从@中获取用户ID const atUsers = this.client.getMessageAt(context.raw_message); if (atUsers?.length > 0) return atUsers[0]; // 尝试从参数获取 if (Array.isArray(params) && params.length > 0) return Number(params[0]); return Number(params); }; // 获取用户ID const adminId = extractUserId(); // 验证ID有效性 if (isNaN(adminId)) { await this.reply('无法识别用户ID,请确保使用正确的格式: @用户 或直接输入QQ号', true); break; } // 根据操作类型执行不同逻辑 if (action === '加管理') { // 添加管理员 if (config.admins.includes(adminId)) { await this.reply(\`用户 \${adminId} 已经是管理员\`, true); } else { config.admins.push(adminId); await this.reply(\`✅已添加管理员: \${adminId}\`, true); } } else { // 删除管理员 if (!config.admins.includes(adminId)) { await this.reply(\`用户 \${adminId} 不是管理员\`, true); } else { config.admins = config.admins.filter(admin => admin !== adminId); await this.reply(\`✅已删除管理员: \${adminId}\`, true); } } // 保存更新后的配置 this.client.setConfig(config); break; default: await this.showHelp(context); } } private async showStatus(context: AllHandlers['message']) { const pluginManager = this.client.pluginManager; if (!pluginManager) { logger.error("无法获取插件管理器"); await this.reply("获取插件管理器失败", true); return; } try { // 定义类型接口 interface VersionInfo { app_name?: string; protocol_version?: string; app_version?: string; } interface LoginInfo { nickname?: string; user_id?: number; } // 并行获取所有异步数据 const [ versionInfo, loginInfo, friendList, groupList, diskInfo ] = await Promise.all([ this.client.napcat.get_version_info().catch(() => ({} as VersionInfo)), this.client.napcat.get_login_info().catch(() => ({} as LoginInfo)), this.client.napcat.get_friend_list().catch(() => []), this.client.napcat.get_group_list().catch(() => []), getDiskInfo() ]); // 处理插件信息 const pluginNum = pluginManager.getPluginInfo().length; const openedPluginInfo = this.client.getConfig().plugins; const openedNum = openedPluginInfo.system.length + openedPluginInfo.user.length; // 处理框架信息 const ver_info = { app_name: versionInfo.app_name || "CyberBot", protocol_version: versionInfo.protocol_version || "Unknown", app_version: versionInfo.app_version || "Unknown" }; // 处理登录信息 const login_qq = { nickname: loginInfo.nickname || "Unknown", user_id: loginInfo.user_id ? String(loginInfo.user_id) : "Unknown" }; // 内存使用情况 const memoryUsage = process.memoryUsage(); const totalMemory = os.totalmem(); const freeMemory = os.freemem(); // 系统信息 const nodeVersion = process.version; const platform = os.platform() === 'win32' ? 'Windows' : os.platform(); const arch = os.arch(); // 运行时间 const uptimeSeconds = process.uptime(); const days = Math.floor(uptimeSeconds / 86400); const hours = Math.floor((uptimeSeconds % 86400) / 3600); const minutes = Math.floor((uptimeSeconds % 3600) / 60); const seconds = Math.floor(uptimeSeconds % 60); // 格式化状态信息 const statusMessage = [ '〓 🟢 Bot 状态 〓', \`🤖 CyberBot(\${login_qq.nickname})\`, \`❄ \${login_qq.user_id}\`, \`🧩 插件\${openedNum}/\${pluginNum}个已启用\`, \`🕦 \${days}d \${hours}h \${minutes}m \${seconds}s\`, \`📋 \${friendList.length}个好友,\${groupList.length}个群\`, \`🔷 \${ver_info.app_name}-\${ver_info.protocol_version}-\${ver_info.app_version}\`, \`🚀 bot占用-\${(memoryUsage.rss / 1024 / 1024).toFixed(2)} MB-\${((memoryUsage.rss / totalMemory) * 100).toFixed(2)}%\`, \`💻 \${platform}-\${arch}-node\${nodeVersion.slice(1)}\`, \`⚡ \${((totalMemory - freeMemory) / 1024 / 1024 / 1024).toFixed(2)} GB/\${(totalMemory / 1024 / 1024 / 1024).toFixed(2)} GB-\${(((totalMemory - freeMemory) / totalMemory) * 100).toFixed(2)}%\`, \`💾 \${diskInfo.used.toFixed(0)} GB/\${diskInfo.total.toFixed(0)} GB-\${((diskInfo.used/diskInfo.total) * 100).toFixed(2)}%\` ].join('\\n'); await this.reply(statusMessage); } catch (error) { logger.error(\`状态查询失败: \${error}\`); await this.reply('获取状态信息时出错,请查看日志', true); } } private async updateFramework(context: AllHandlers['message']) { // TODO: 实现框架更新逻辑 await this.reply('框架更新功能待实现', true); } private async exitFramework(context: AllHandlers['message']) { await this.reply('✅已退出框架', true); process.exit(0); } } // 封装成一个函数,获取指定路径所在硬盘的信息 const getDiskInfo = async (path = process.cwd()) => { try { const disks = await fsSize(); const GB = 1073741824; // 明确声明 targetDisk 可能是 FsSizeData 或 null let targetDisk: FsSizeData | null = null; let maxMountLength = 0; for (const disk of disks) { if (path.startsWith(disk.mount) && disk.mount.length > maxMountLength) { targetDisk = disk; maxMountLength = disk.mount.length; } } if (!targetDisk) throw new Error(\`找不到路径 \${path} 对应的磁盘\`); const sizeGB = targetDisk.size / GB; const availableGB = targetDisk.available / GB; return { total: parseFloat(sizeGB.toFixed(2)), used: parseFloat((sizeGB - availableGB).toFixed(2)), available: parseFloat(availableGB.toFixed(2)) }; } catch (err) { console.error("获取磁盘信息失败:", err); return { total: 100, used: 50, available: 50 }; } }; `; fs.writeFileSync(demoPluginPath, demoPluginContent); fs.writeFileSync(cmdsPluginPath, cmdsPluginContent); console.log(`插件文件已生成: ${demoPluginPath}、${cmdsPluginPath}`); console.log('\n请执行以下命令以下载依赖:'); console.log(`cd ${config.projectName}`); console.log('npm install'); console.log('\n等待依赖安装完成后执行以下命令启动机器人:'); console.log('npm start'); } export function generateConfig() { rl.question('请输入项目名称: ', (projectName) => { const projectDir = path.join(process.cwd(), projectName.trim()); if (!fs.existsSync(projectDir)) { fs.mkdirSync(projectDir, { recursive: true }); } const configFilePath = path.join(projectDir, 'config.toml'); if (fs.existsSync(configFilePath)) { console.log('配置文件 config.toml 已存在,跳过配置步骤。'); rl.close(); process.exit(0); } const answers = { projectName: projectName.trim() }; let questionIndex = 0; function askNextQuestion() { if (questionIndex >= questions.length) { saveConfig(projectDir, answers); rl.close(); return; } const question = questions[questionIndex]; rl.question(question.message + ' ', (answer) => { answers[question.name] = answer; questionIndex++; askNextQuestion(); }); } askNextQuestion(); }); } generateConfig(); //# sourceMappingURL=cli.js.map