📄 qqbot-cli.js

#!/usr/bin/env node

/**
 * QQBot CLI - 用于升级和管理 QQBot 插件
 * 
 * 用法:
 *   npx @sliverp/qqbot upgrade    # 升级插件
 *   npx @sliverp/qqbot install    # 安装插件
 */

import { execSync } from 'child_process';
import { existsSync, readFileSync, writeFileSync, rmSync } from 'fs';
import { homedir } from 'os';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// 获取包的根目录
const PKG_ROOT = join(__dirname, '..');

const args = process.argv.slice(2);
const command = args[0];

// 检测使用的是 clawdbot 还是 openclaw
function detectInstallation() {
  const home = homedir();
  if (existsSync(join(home, '.openclaw'))) {
    return 'openclaw';
  }
  if (existsSync(join(home, '.clawdbot'))) {
    return 'clawdbot';
  }
  return null;
}

// 清理旧版本插件,返回旧的 qqbot 配置
function cleanupInstallation(appName) {
  const home = homedir();
  const appDir = join(home, `.${appName}`);
  const configFile = join(appDir, `${appName}.json`);
  const extensionDir = join(appDir, 'extensions', 'qqbot');

  let oldQqbotConfig = null;

  console.log(`\n>>> 处理 ${appName} 安装...`);

  // 1. 先读取旧的 qqbot 配置
  if (existsSync(configFile)) {
    try {
      const config = JSON.parse(readFileSync(configFile, 'utf8'));
      if (config.channels?.qqbot) {
        oldQqbotConfig = { ...config.channels.qqbot };
        console.log('已保存旧的 qqbot 配置');
      }
    } catch (err) {
      console.error('读取配置文件失败:', err.message);
    }
  }

  // 2. 删除旧的扩展目录
  if (existsSync(extensionDir)) {
    console.log(`删除旧版本插件: ${extensionDir}`);
    rmSync(extensionDir, { recursive: true, force: true });
  } else {
    console.log('未找到旧版本插件目录,跳过删除');
  }

  // 3. 清理配置文件中的 qqbot 相关字段
  if (existsSync(configFile)) {
    console.log('清理配置文件中的 qqbot 字段...');
    try {
      const config = JSON.parse(readFileSync(configFile, 'utf8'));

      // 删除 channels.qqbot
      if (config.channels?.qqbot) {
        delete config.channels.qqbot;
        console.log('  - 已删除 channels.qqbot');
      }

      // 删除 plugins.entries.qqbot
      if (config.plugins?.entries?.qqbot) {
        delete config.plugins.entries.qqbot;
        console.log('  - 已删除 plugins.entries.qqbot');
      }

      // 删除 plugins.installs.qqbot
      if (config.plugins?.installs?.qqbot) {
        delete config.plugins.installs.qqbot;
        console.log('  - 已删除 plugins.installs.qqbot');
      }

      writeFileSync(configFile, JSON.stringify(config, null, 2));
      console.log('配置文件已更新');
    } catch (err) {
      console.error('清理配置文件失败:', err.message);
    }
  } else {
    console.log(`未找到配置文件: ${configFile}`);
  }

  return oldQqbotConfig;
}

// 执行命令并继承 stdio
function runCommand(cmd, args = []) {
  try {
    execSync([cmd, ...args].join(' '), { stdio: 'inherit' });
    return true;
  } catch (err) {
    return false;
  }
}

// 升级命令
function upgrade() {
  console.log('=== QQBot 插件升级脚本 ===');

  let foundInstallation = null;
  let savedConfig = null;
  const home = homedir();

  // 检查 openclaw
  if (existsSync(join(home, '.openclaw'))) {
    savedConfig = cleanupInstallation('openclaw');
    foundInstallation = 'openclaw';
  }

  // 检查 clawdbot
  if (existsSync(join(home, '.clawdbot'))) {
    const clawdbotConfig = cleanupInstallation('clawdbot');
    if (!savedConfig) savedConfig = clawdbotConfig;
    foundInstallation = 'clawdbot';
  }

  if (!foundInstallation) {
    console.log('\n未找到 clawdbot 或 openclaw 安装目录');
    console.log('请确认已安装 clawdbot 或 openclaw');
    process.exit(1);
  }

  console.log('\n=== 清理完成 ===');

  // 自动安装插件
  console.log('\n[1/2] 安装新版本插件...');
  runCommand(foundInstallation, ['plugins', 'install', '@sliverp/qqbot']);

  // 自动配置通道(使用保存的 appId 和 clientSecret)
  console.log('\n[2/2] 配置机器人通道...');
  if (savedConfig?.appId && savedConfig?.clientSecret) {
    const token = `${savedConfig.appId}:${savedConfig.clientSecret}`;
    console.log(`使用已保存的配置: appId=${savedConfig.appId}`);
    runCommand(foundInstallation, ['channels', 'add', '--channel', 'qqbot', '--token', `"${token}"`]);
    
    // 恢复其他配置项(如 markdownSupport)
    if (savedConfig.markdownSupport !== undefined) {
      runCommand(foundInstallation, ['config', 'set', 'channels.qqbot.markdownSupport', String(savedConfig.markdownSupport)]);
    }
  } else {
    console.log('未找到已保存的 qqbot 配置,请手动配置:');
    console.log(`  ${foundInstallation} channels add --channel qqbot --token "AppID:AppSecret"`);
    return;
  }

  console.log('\n=== 升级完成 ===');
  console.log(`\n可以运行以下命令前台运行启动机器人:`);
  console.log(`  ${foundInstallation} gateway  stop && ${foundInstallation} gateway --port 18789 --verbose`);
}

// 安装命令
function install() {
  console.log('=== QQBot 插件安装 ===');

  const cmd = detectInstallation();
  if (!cmd) {
    console.log('未找到 clawdbot 或 openclaw 安装');
    console.log('请先安装 openclaw 或 clawdbot');
    process.exit(1);
  }

  console.log(`\n使用 ${cmd} 安装插件...`);
  runCommand(cmd, ['plugins', 'install', '@sliverp/qqbot']);

  console.log('\n=== 安装完成 ===');
  console.log('\n请配置机器人通道:');
  console.log(`  ${cmd} channels add --channel qqbot --token "AppID:AppSecret"`);
}

// 显示帮助
function showHelp() {
  console.log(`
QQBot CLI - QQ机器人插件管理工具

用法:
  npx @sliverp/qqbot <命令>

命令:
  upgrade       清理旧版本插件(升级前执行)
  install       安装插件到 openclaw/clawdbot

示例:
  npx @sliverp/qqbot upgrade
  npx @sliverp/qqbot install
`);
}

// 主入口
switch (command) {
  case 'upgrade':
    upgrade();
    break;
  case 'install':
    install();
    break;
  case '-h':
  case '--help':
  case 'help':
    showHelp();
    break;
  default:
    if (command) {
      console.log(`未知命令: ${command}`);
    }
    showHelp();
    process.exit(command ? 1 : 0);
}