Skip to content

🚀 高级用法示例

展示 Zhin.js 框架的高级特性和复杂用法。

🎯 高级插件开发

插件间通信

typescript
// src/plugins/weather-plugin.ts
import { usePlugin, useLogger, register, useContext } from 'zhin.js';

const plugin = usePlugin();
const logger = useLogger();

// 注册天气服务
register({
  name: 'weather',
  description: '天气查询服务',
  async mounted() {
    logger.info('天气服务已启动');
    return {
      async getWeather(city: string) {
        // 模拟天气API调用
        return {
          city,
          temperature: Math.floor(Math.random() * 30) + 5,
          description: '晴天'
        };
      }
    };
  }
});

// 使用其他插件的服务
useContext('database', async (db) => {
  logger.info('数据库服务已就绪,可以存储天气历史');
  
  // 注册天气查询命令
  addCommand(new MessageCommand('weather <city:text>')
    .action(async (message, result) => {
      const weather = await plugin.getContext('weather').getWeather(result.args.city);
      await db.query('INSERT INTO weather_history (city, temperature, timestamp) VALUES (?, ?, ?)', 
        [weather.city, weather.temperature, Date.now()]);
      
      return `🌤️ ${weather.city} 天气:${weather.temperature}°C,${weather.description}`;
    })
  );
});

复杂命令系统

typescript
// src/plugins/admin-plugin.ts
import { addCommand, MessageCommand, useLogger, useContext } from 'zhin.js';

const logger = useLogger();

// 权限检查中间件
const requireAdmin = (message: Message) => {
  const adminUsers = ['123456789', '987654321'];
  if (!adminUsers.includes(message.sender.id)) {
    throw new Error('权限不足');
  }
};

// 用户管理命令
addCommand(new MessageCommand('user list [page:number=1]')
  .action(async (message, result) => {
    requireAdmin(message);
    
    const page = result.args.page || 1;
    const limit = 10;
    const offset = (page - 1) * limit;
    
    const users = await db.query('SELECT * FROM users LIMIT ? OFFSET ?', [limit, offset]);
    const total = await db.query('SELECT COUNT(*) as count FROM users');
    
    let response = `👥 用户列表 (第${page}页)\n\n`;
    users.forEach((user, index) => {
      response += `${offset + index + 1}. ${user.name} (${user.id})\n`;
    });
    response += `\n总计: ${total[0].count} 用户`;
    
    return response;
  })
);

// 群组管理命令
addCommand(new MessageCommand('group mute <userId:text> [duration:number=300]')
  .action(async (message, result) => {
    requireAdmin(message);
    
    const { userId, duration } = result.args;
    const groupId = message.channel.id;
    
    try {
      await message.reply(`🔇 用户 ${userId} 已被禁言 ${duration} 秒`);
      
      // 这里应该调用实际的禁言API
      // await bot.setGroupBan(groupId, userId, duration);
      
      // 记录操作日志
      await db.query('INSERT INTO admin_logs (action, target, operator, timestamp) VALUES (?, ?, ?, ?)',
        ['mute', userId, message.sender.id, Date.now()]);
      
      return `✅ 禁言操作已记录`;
    } catch (error) {
      logger.error('禁言操作失败:', error);
      return `❌ 禁言操作失败: ${error.message}`;
    }
  })
);

🔄 热重载开发

开发环境配置

typescript
// zhin.config.ts
import { defineConfig } from 'zhin.js';

export default defineConfig(async (env) => {
  return {
    bots: [
      {
        name: 'dev-bot',
        context: 'process'
      }
    ],
    plugins: [
      'adapter-process',
      'http',
      'console',
      'my-plugin'
    ],
    debug: true,
    hmr: {
      enabled: true,
      watch: ['./src/plugins/**/*.ts'],
      ignore: ['**/*.test.ts', '**/*.spec.ts']
    }
  };
});

热重载插件

typescript
// src/plugins/hot-reload-plugin.ts
import { usePlugin, useLogger, onMounted, onDispose } from 'zhin.js';

const plugin = usePlugin();
const logger = useLogger();

let fileWatcher: any;
let reloadCount = 0;

onMounted(() => {
  logger.info('热重载插件已启动');
  
  // 监听文件变化
  const chokidar = require('chokidar');
  fileWatcher = chokidar.watch('./src/plugins/**/*.ts', {
    ignored: /(^|[\/\\])\../, // 忽略点文件
    persistent: true
  });
  
  fileWatcher.on('change', (path: string) => {
    logger.info(`文件变化: ${path}`);
    reloadCount++;
    
    // 触发热重载
    plugin.emit('hmr.reload', { path, count: reloadCount });
  });
  
  fileWatcher.on('error', (error: any) => {
    logger.error('文件监听错误:', error);
  });
});

onDispose(() => {
  if (fileWatcher) {
    fileWatcher.close();
    logger.info('文件监听器已关闭');
  }
});

// 提供重载统计
addCommand(new MessageCommand('reload stats')
  .action(async () => {
    return `🔄 热重载统计:\n- 重载次数: ${reloadCount}\n- 监听文件: ./src/plugins/**/*.ts`;
  })
);

📊 数据持久化

数据库集成

typescript
// src/plugins/database-plugin.ts
import { register, useContext, useLogger } from 'zhin.js';
import Database from 'better-sqlite3';

const logger = useLogger();

register({
  name: 'database',
  description: 'SQLite 数据库服务',
  async mounted() {
    const db = new Database('./data/bot.db');
    
    // 创建表
    db.exec(`
      CREATE TABLE IF NOT EXISTS users (
        id TEXT PRIMARY KEY,
        name TEXT NOT NULL,
        created_at INTEGER DEFAULT (strftime('%s', 'now')),
        last_seen INTEGER DEFAULT (strftime('%s', 'now'))
      );
      
      CREATE TABLE IF NOT EXISTS messages (
        id TEXT PRIMARY KEY,
        user_id TEXT NOT NULL,
        channel_id TEXT NOT NULL,
        content TEXT NOT NULL,
        timestamp INTEGER DEFAULT (strftime('%s', 'now')),
        FOREIGN KEY (user_id) REFERENCES users (id)
      );
      
      CREATE TABLE IF NOT EXISTS commands (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id TEXT NOT NULL,
        command TEXT NOT NULL,
        args TEXT,
        timestamp INTEGER DEFAULT (strftime('%s', 'now')),
        FOREIGN KEY (user_id) REFERENCES users (id)
      );
    `);
    
    logger.info('数据库已初始化');
    
    return {
      query: (sql: string, params: any[] = []) => {
        try {
          const stmt = db.prepare(sql);
          if (sql.trim().toLowerCase().startsWith('select')) {
            return stmt.all(params);
          } else {
            return stmt.run(params);
          }
        } catch (error) {
          logger.error('数据库查询失败:', error);
          throw error;
        }
      },
      
      transaction: (callback: (db: any) => void) => {
        const transaction = db.transaction(callback);
        return transaction;
      },
      
      close: () => {
        db.close();
        logger.info('数据库连接已关闭');
      }
    };
  },
  
  async dispose(db) {
    db.close();
  }
});

缓存系统

typescript
// src/plugins/cache-plugin.ts
import { register, useContext, useLogger } from 'zhin.js';

const logger = useLogger();

interface CacheItem<T> {
  value: T;
  expires: number;
}

class CacheManager {
  private cache = new Map<string, CacheItem<any>>();
  private timers = new Map<string, NodeJS.Timeout>();
  
  set<T>(key: string, value: T, ttl: number = 300000): void {
    // 清除现有定时器
    const existingTimer = this.timers.get(key);
    if (existingTimer) {
      clearTimeout(existingTimer);
    }
    
    // 设置缓存项
    this.cache.set(key, {
      value,
      expires: Date.now() + ttl
    });
    
    // 设置过期定时器
    const timer = setTimeout(() => {
      this.delete(key);
    }, ttl);
    
    this.timers.set(key, timer);
    
    logger.debug(`缓存已设置: ${key} (TTL: ${ttl}ms)`);
  }
  
  get<T>(key: string): T | null {
    const item = this.cache.get(key);
    if (!item) {
      return null;
    }
    
    if (Date.now() > item.expires) {
      this.delete(key);
      return null;
    }
    
    return item.value;
  }
  
  delete(key: string): boolean {
    const timer = this.timers.get(key);
    if (timer) {
      clearTimeout(timer);
      this.timers.delete(key);
    }
    
    return this.cache.delete(key);
  }
  
  clear(): void {
    for (const timer of this.timers.values()) {
      clearTimeout(timer);
    }
    this.timers.clear();
    this.cache.clear();
  }
  
  size(): number {
    return this.cache.size;
  }
  
  keys(): string[] {
    return Array.from(this.cache.keys());
  }
}

register({
  name: 'cache',
  description: '内存缓存服务',
  async mounted() {
    const cache = new CacheManager();
    
    logger.info('缓存服务已启动');
    
    return cache;
  }
});

🔌 多平台适配

跨平台消息处理

typescript
// src/plugins/cross-platform-plugin.ts
import { onMessage, addCommand, MessageCommand, useLogger } from 'zhin.js';

const logger = useLogger();

// 跨平台消息转发
onMessage(async (message) => {
  // 只处理特定关键词的消息
  if (message.raw.includes('转发')) {
    const targetPlatform = message.raw.split('转发')[1]?.trim();
    
    if (targetPlatform) {
      try {
        // 根据平台转发消息
        await forwardMessage(message, targetPlatform);
        await message.reply(`✅ 消息已转发到 ${targetPlatform}`);
      } catch (error) {
        logger.error('消息转发失败:', error);
        await message.reply(`❌ 转发失败: ${error.message}`);
      }
    }
  }
});

async function forwardMessage(sourceMessage: Message, targetPlatform: string) {
  const content = `📨 来自 ${sourceMessage.adapter} 的转发消息:\n${sourceMessage.raw}`;
  
  // 这里应该根据目标平台发送消息
  // 实际实现需要根据具体的适配器API
  switch (targetPlatform) {
    case 'qq':
      // await qqBot.sendMessage(content);
      break;
    case 'discord':
      // await discordBot.sendMessage(content);
      break;
    case 'telegram':
      // await telegramBot.sendMessage(content);
      break;
    default:
      throw new Error(`不支持的平台: ${targetPlatform}`);
  }
}

// 跨平台统计命令
addCommand(new MessageCommand('stats all')
  .action(async (message) => {
    const stats = await getCrossPlatformStats();
    
    let response = '📊 跨平台统计:\n\n';
    for (const [platform, data] of Object.entries(stats)) {
      response += `**${platform}**:\n`;
      response += `- 消息数: ${data.messages}\n`;
      response += `- 用户数: ${data.users}\n`;
      response += `- 在线状态: ${data.online ? '🟢' : '🔴'}\n\n`;
    }
    
    return response;
  })
);

async function getCrossPlatformStats() {
  // 这里应该从各个平台获取统计信息
  return {
    qq: { messages: 1234, users: 56, online: true },
    discord: { messages: 567, users: 23, online: true },
    telegram: { messages: 890, users: 34, online: false }
  };
}

🎨 函数式组件系统

消息组件系统

typescript
// src/plugins/component-plugin.ts
import { defineComponent, segment } from 'zhin.js';

// 卡片组件 - 函数式组件
const CardComponent = defineComponent(async function CardComponent(props: {
  title: string;
  content: string;
  color?: string;
  children?: string;
}, context) {
  const colorMap = {
    blue: '🔵',
    green: '🟢',
    red: '🔴',
    yellow: '🟡'
  };
  
  const icon = colorMap[props.color || 'blue'] || '🔵';
  const content = props.children || props.content;
  
  return [
    segment('text', { text: `${icon} **${props.title}**\n` }),
    segment('text', { text: content }),
    segment('text', { text: '\n' + '─'.repeat(20) })
  ];
}, 'card');

// 进度条组件 - 函数式组件
const ProgressComponent = defineComponent(async function ProgressComponent(props: {
  value: number;
  max?: number;
  width?: number;
}, context) {
  const max = props.max || 100;
  const width = props.width || 20;
  const percentage = Math.min(100, Math.max(0, (props.value / max) * 100));
  const filled = Math.floor((percentage / 100) * width);
  const empty = width - filled;
  
  const bar = '█'.repeat(filled) + '░'.repeat(empty);
  
  return [
    segment('text', { text: `进度: ${bar} ${percentage.toFixed(1)}%` })
  ];
}, 'progress');

// 表格组件 - 函数式组件
const TableComponent = defineComponent(async function TableComponent(props: {
  headers: string[];
  rows: string[][];
  border?: boolean;
}, context) {
  if (!props.headers || !props.rows) {
    return [segment('text', { text: '表格数据不完整' })];
  }
  
  let table = '';
  const border = props.border !== false;
  
  if (border) {
    const separator = '┌' + '─'.repeat(20) + '┬' + '─'.repeat(20) + '┐\n';
    table += separator;
  }
  
  // 表头
  table += '│ ' + props.headers.join(' │ ') + ' │\n';
  
  if (border) {
    table += '├' + '─'.repeat(20) + '┼' + '─'.repeat(20) + '┤\n';
  }
  
  // 数据行
  props.rows.forEach(row => {
    table += '│ ' + row.join(' │ ') + ' │\n';
  });
  
  if (border) {
    table += '└' + '─'.repeat(20) + '┴' + '─'.repeat(20) + '┘';
  }
  
  return [segment('text', { text: table })];
}, 'table');

// 条件渲染组件 - 函数式组件
const ConditionalComponent = defineComponent(async function ConditionalComponent(props: {
  condition: boolean;
  children?: string;
  fallback?: string;
}, context) {
  if (props.condition) {
    return props.children || '';
  }
  return props.fallback || '';
}, 'conditional');

// 列表组件 - 使用 Fragment 和 children
const ListComponent = defineComponent(async function ListComponent(props: {
  items: string[];
  children?: string;
}, context) {
  const items = props.items.map((item, index) => `${index + 1}. ${item}`).join('\n');
  const header = props.children ? `\n=== ${props.children} ===\n` : '';
  return `${header}${items}`;
}, 'list');

// 使用示例命令
addCommand(new MessageCommand('demo components')
  .action(async () => {
    return [
      // 使用卡片组件
      segment('card', { 
        title: '组件演示', 
        content: '这是一个卡片组件示例',
        color: 'blue'
      }),
      
      // 使用进度条组件
      segment('progress', { value: 75, max: 100, width: 15 }),
      
      // 使用表格组件
      segment('table', {
        headers: ['姓名', '年龄'],
        rows: [
          ['张三', '25'],
          ['李四', '30']
        ]
      }),
      
      // 使用条件渲染组件
      segment('conditional', { 
        condition: true,
        children: '条件为真时显示的内容'
      }),
      
      // 使用列表组件
      segment('list', {
        items: ['功能1', '功能2', '功能3'],
        children: '功能列表'
      })
    ];
  })
);

// 使用模板语法
addCommand(new MessageCommand('demo template')
  .action(async () => {
    return `
<Card title="用户信息" color="green">
  <List items={["特性1", "特性2", "特性3"]}>功能列表</List>
  <Progress value={60} max={100} width={20} />
</Card>
    `;
  })
);

🔗 相关链接