cyberbot-next
Version:
cyberbot, 基于napcat-ts, nodejs,轻量qq机器人框架。
643 lines (582 loc) • 22.7 kB
JavaScript
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