Skip to content

🧩 插件开发指南

本指南将帮助你深入掌握 Zhin.js 插件开发,从基础到高级技巧。

生态说明:Zhin.js 开箱即用支持控制台适配器、HTTP 服务、Web 控制台、SQLite 数据库。Telegram、Discord、QQ、KOOK、OneBot v11、MySQL、PostgreSQL 等需手动安装扩展包。建议插件开发优先兼容主仓库内置服务,跨平台请注明依赖。

🎯 插件核心概念

插件是 Zhin 框架的灵魂,每个插件都是一个功能完整的模块。一个优秀的插件应该:

  • 🎮 处理消息 - 响应用户输入,提供智能交互
  • 注册命令 - 定义清晰的命令接口和参数解析
  • 🔧 提供服务 - 通过上下文系统为其他插件提供功能
  • 🔗 添加中间件 - 在消息处理流程中插入自定义逻辑
  • 📡 监听事件 - 响应系统和其他插件的事件
  • 💾 管理状态 - 维护插件的持久化数据和缓存

🏗️ 插件架构设计

mermaid
graph TD
    A[用户消息] --> B[中间件管道]
    B --> C[命令匹配]
    C --> D[插件处理]
    D --> E[上下文服务]
    E --> F[消息回复]
    
    G[HMR系统] --> H[插件热重载]
    H --> I[依赖注入]
    I --> D
    
    J[配置系统] --> K[插件配置]
    K --> D

📁 插件目录配置

Zhin 支持从多个目录加载插件,推荐配置如下:

typescript
// 📂 插件目录配置
plugin_dirs: [
  './src/plugins',           // 项目自定义插件目录
  'node_modules',            // 第三方 npm 插件目录  
  'node_modules/@zhin.js'    // Zhin 官方插件目录(推荐)
]

目录说明:

  • ./src/plugins: 存放项目自定义插件,便于开发和调试
  • node_modules: 存放通过 npm 安装的第三方插件
  • node_modules/@zhin.js: 存放 Zhin 官方插件,提供稳定可靠的功能

📁 现代插件结构

基于实际项目生成的标准插件结构:

typescript
import {
  useLogger,          // 日志系统
  onMessage,          // 消息监听
  addCommand,         // 命令注册
  addMiddleware,      // 中间件系统
  MessageCommand,     // 命令解析器
  useContext,         // 上下文依赖
  onDispose,          // 生命周期
  register,           // 服务注册
  defineComponent,    // 组件定义
  segment             // 消息段工具
} from 'zhin.js'

// 📝 获取日志记录器
const logger = useLogger()

// 🎯 注册简单命令
addCommand(new MessageCommand('hello')
  .action(async (message) => {
    logger.info('Hello command called by:', message.sender.name)
    return '你好!欢迎使用 Zhin 机器人框架!'
  })
)

// 📊 带参数的复杂命令
addCommand(new MessageCommand('weather <city:text> [date:string]')
  .action(async (message, result) => {
    const { city, date = 'today' } = result.args
    const weather = await getWeatherData(city, date)
    return `${city} ${date}的天气:${weather}`
  })
)

// 🔧 添加中间件处理
addMiddleware(async (message, next) => {
  const start = Date.now()
  logger.debug(`处理消息: ${message.raw}`)
  
  await next() // 继续处理管道
  
  const duration = Date.now() - start
  logger.debug(`消息处理完成,耗时: ${duration}ms`)
})

// 💬 消息监听和智能响应
onMessage(async (message) => {
  // 智能问候检测
  const greetings = ['你好', 'hello', 'hi', '早上好', '晚上好']
  const text = message.raw.toLowerCase()
  
  if (greetings.some(greeting => text.includes(greeting))) {
    const responses = [
      '你好呀!👋 有什么可以帮助你的吗?',
      '嗨!很高兴见到你!😊',
      '早安!今天过得怎么样?🌅',
      '晚上好!休息得好吗?🌙'
    ]
    const randomResponse = responses[Math.floor(Math.random() * responses.length)]
    await message.reply(randomResponse)
  }
})

// 🔧 注册可复用服务
register({
  name: 'weatherService',
  async mounted(plugin) {
    const cache = new Map()
    
    return {
      async getWeather(city: string) {
        const cacheKey = `weather:${city}`
        
        if (cache.has(cacheKey)) {
          return cache.get(cacheKey)
        }
        
        const weather = await fetchWeatherAPI(city)
        cache.set(cacheKey, weather)
        
        // 5分钟后清除缓存
        setTimeout(() => cache.delete(cacheKey), 5 * 60 * 1000)
        
        return weather
      }
    }
  },
  
  async dispose(service) {
    logger.info('天气服务已清理')
  }
})

// 🎯 使用其他服务的上下文依赖
useContext('database', async (db) => {
  // 数据库就绪后执行初始化
  await db.query(`
    CREATE TABLE IF NOT EXISTS user_preferences (
      user_id VARCHAR(255) PRIMARY KEY,
      preferred_weather_city VARCHAR(255),
      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
  `)
  
  logger.info('用户偏好表已准备就绪')
})

// 🧩 定义函数式组件
const WeatherCard = defineComponent(async function WeatherCard(props: {
  city: string;
  temperature: number;
  condition: string;
  icon: string;
}, context: ComponentContext) {
  return [
    `🌡️ **${props.city}天气预报**`,
    `温度:${props.temperature}°C`,
    `天气:${props.condition} ${props.icon}`,
    `更新时间:${new Date().toLocaleString()}`
  ].join('\n');
}, 'weather-card');

// 🧹 插件销毁时的清理工作
onDispose(() => {
  logger.info('天气插件已销毁,资源已清理')
})

// 🛠️ 工具函数
async function getWeatherData(city: string, date: string) {
  // 实现天气数据获取逻辑
  return `${city} ${date} 晴天,温度 22°C`
}

async function fetchWeatherAPI(city: string) {
  // 实际的API调用逻辑
  return { temperature: 22, condition: '晴天', icon: '☀️' }
}

logger.info('天气插件已加载 🌤️')

⚡ 快速开始:你的第一个插件

1️⃣ 创建插件文件

src/plugins/ 目录下创建 my-awesome-plugin.ts

typescript
import { 
  useLogger, 
  onMessage, 
  addCommand, 
  MessageCommand 
} from 'zhin.js'

const logger = useLogger()

// 🎯 创建你的第一个命令
addCommand(new MessageCommand('awesome')
  .action(async () => {
    return '🚀 这是我的第一个 Zhin 插件!'
  })
)

// 💬 智能消息响应
onMessage(async (message) => {
  if (message.raw.includes('插件')) {
    await message.reply('🧩 插件开发很有趣!')
  }
})

logger.info('我的超棒插件已启动! ✨')

2️⃣ 启用插件

zhin.config.ts 中添加插件:

javascript
export default defineConfig(async (env) => {
  return {
    // ... 其他配置
    plugins: [
      'adapter-process',
      'http',
      'console', 
      'my-awesome-plugin'  // 添加你的插件
    ]
  }
})

3️⃣ 测试插件

启动开发服务器并测试:

bash
pnpm dev

# 在控制台输入测试
> awesome
< 🚀 这是我的第一个 Zhin 插件!

> 我想了解插件开发
< 🧩 插件开发很有趣!

🔄 插件生命周期详解

📋 完整生命周期流程

mermaid
sequenceDiagram
    participant HMR as HMR系统
    participant Plugin as 插件实例
    participant Context as 上下文系统
    participant Message as 消息系统
    
    HMR->>Plugin: 1. 加载插件文件
    Plugin->>Plugin: 2. 执行插件代码
    Plugin->>Context: 3. 注册上下文服务
    Plugin->>Message: 4. 注册命令和中间件
    
    Note over Plugin: 🚀 挂载阶段
    Context->>Plugin: 5. 触发 mounted 事件
    Plugin->>Plugin: 6. 初始化资源
    
    Note over Plugin: ⚡ 运行阶段
    Message->>Plugin: 7. 处理消息和命令
    Plugin->>Context: 8. 使用上下文服务
    
    Note over Plugin: 🛑 卸载阶段 (热重载时)
    HMR->>Plugin: 9. 触发 dispose 事件
    Plugin->>Plugin: 10. 清理资源
    Plugin->>Context: 11. 注销上下文服务

🎯 各阶段详细说明

1️⃣ 加载阶段

typescript
// ✅ 在这个阶段可以做的事情:
import { useLogger, addCommand } from 'zhin.js'

const logger = useLogger()  // 获取日志记录器

// ❌ 不能做的事情:
// - 访问其他插件的上下文(还未就绪)
// - 发送消息(机器人可能还未连接)

2️⃣ 挂载阶段

typescript
import { onMounted, useContext } from 'zhin.js'

onMounted(async () => {
  // ✅ 所有上下文都已就绪,可以安全使用
  const db = useContext('database')
  await db.query('SELECT 1')  // 现在可以安全使用数据库
  
  logger.info('插件挂载完成,所有依赖就绪')
})

3️⃣ 运行阶段

typescript
import { onMessage, addCommand } from 'zhin.js'

// 🎯 处理用户消息
onMessage(async (message) => {
  // 插件的主要工作逻辑
})

// ⚡ 响应命令
addCommand(new MessageCommand('status')
  .action(async () => {
    return '插件运行正常!'
  })
)

4️⃣ 卸载阶段

typescript
import { onDispose } from 'zhin.js'

let timer: NodeJS.Timeout

// 创建定时器
timer = setInterval(() => {
  logger.debug('定时任务执行中...')
}, 1000)

onDispose(() => {
  // 🧹 清理资源,防止内存泄漏
  if (timer) {
    clearInterval(timer)
  }
  
  // 关闭数据库连接、清理缓存等
  logger.info('插件资源已清理')
})

🚀 高级特性和最佳实践

💡 智能错误处理

typescript
import { useLogger, onMessage } from 'zhin.js'

const logger = useLogger()

// 🛡️ 全局错误捕获
process.on('unhandledRejection', (reason, promise) => {
  logger.error('未处理的 Promise 拒绝:', reason)
})

// 🎯 优雅的命令错误处理
addCommand(new MessageCommand('risky')
  .action(async (message) => {
    try {
      const result = await riskyOperation()
      return `操作成功: ${result}`
    } catch (error) {
      logger.error('操作失败:', error)
      return '😅 操作失败了,请稍后重试'
    }
  })
)

🎭 插件配置管理

typescript
// config/plugin-config.ts
interface PluginConfig {
  apiKey: string
  maxRetries: number
  debug: boolean
}

const defaultConfig: PluginConfig = {
  apiKey: process.env.API_KEY || '',
  maxRetries: 3,
  debug: process.env.NODE_ENV === 'development'
}

export { PluginConfig, defaultConfig }

// my-plugin.ts
import { defaultConfig } from './config/plugin-config'

const config = { ...defaultConfig }

📊 性能优化技巧

typescript
import { addMiddleware, useContext } from 'zhin.js'

// 🚀 缓存服务示例
register({
  name: 'cache',
  mounted() {
    const cache = new Map()
    const stats = { hits: 0, misses: 0 }
    
    return {
      get(key: string) {
        if (cache.has(key)) {
          stats.hits++
          return cache.get(key)
        }
        stats.misses++
        return null
      },
      
      set(key: string, value: any, ttl = 300000) { // 5分钟默认TTL
        cache.set(key, value)
        setTimeout(() => cache.delete(key), ttl)
      },
      
      getStats: () => ({ ...stats, size: cache.size })
    }
  }
})

// 📈 使用缓存优化性能
useContext('cache', (cache) => {
  addCommand(new MessageCommand('weather <city>')
    .action(async (message, { args }) => {
      const cacheKey = `weather:${args.city}`
      
      // 先检查缓存
      let weather = cache.get(cacheKey)
      if (weather) {
        return `${weather} (来自缓存)`
      }
      
      // 调用API获取数据
      weather = await fetchWeatherAPI(args.city)
      cache.set(cacheKey, weather, 600000) // 缓存10分钟
      
      return weather
    })
  )
})

🔄 定时任务和调度

typescript
import { onMounted, onDispose } from 'zhin.js'

let scheduledTasks: NodeJS.Timeout[] = []

onMounted(() => {
  // 🕒 简单定时任务
  const dailyTask = setInterval(async () => {
    logger.info('执行每日任务...')
    await performDailyMaintenance()
  }, 24 * 60 * 60 * 1000) // 24小时
  
  // 📅 复杂调度示例
  const hourlyCheck = setInterval(async () => {
    const hour = new Date().getHours()
    if (hour >= 9 && hour <= 18) { // 工作时间
      await performHourlyCheck()
    }
  }, 60 * 60 * 1000) // 每小时
  
  scheduledTasks.push(dailyTask, hourlyCheck)
})

onDispose(() => {
  // 🧹 清理所有定时任务
  scheduledTasks.forEach(clearInterval)
  scheduledTasks = []
})

📝 插件发布指南

1️⃣ NPM 包结构

my-awesome-zhin-plugin/
├── src/
│   ├── index.ts          # 主插件文件
│   └── utils/            # 工具函数
├── dist/                 # 编译输出
├── package.json          # 包配置
├── README.md            # 使用说明
└── tsconfig.json        # TypeScript配置

2️⃣ package.json 配置

json
{
  "name": "@your-org/zhin-plugin-awesome",
  "version": "1.0.0",
  "description": "一个超棒的 Zhin 插件",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "keywords": ["zhin", "plugin", "bot"],
  "peerDependencies": {
    "zhin.js": ">=1.0.0"
  },
  "files": ["dist", "README.md"],
  "publishConfig": {
    "access": "public"
  }
}

3️⃣ 发布流程

bash
# 构建插件
pnpm build

# 发布到 NPM
npm publish

🎯 实用插件示例

🤖 AI 聊天插件

typescript
import { onMessage, addCommand, MessageCommand } from 'zhin.js'

addCommand(new MessageCommand('ai <prompt:text>')
  .action(async (message, { args }) => {
    const response = await callAIAPI(args.prompt)
    return `🤖 AI回复: ${response}`
  })
)

async function callAIAPI(prompt: string) {
  // 调用 AI API 的实现
  return "这是AI的回复"
}

📊 数据统计插件

typescript
register({
  name: 'stats',
  mounted() {
    const stats = {
      messageCount: 0,
      commandCount: 0,
      userCount: new Set()
    }
    
    return {
      increment(type: keyof typeof stats, user?: string) {
        if (type === 'userCount' && user) {
          stats.userCount.add(user)
        } else if (typeof stats[type] === 'number') {
          (stats[type] as number)++
        }
      },
      getStats: () => ({
        ...stats,
        userCount: stats.userCount.size
      })
    }
  }
})

useContext('stats', (stats) => {
  addMiddleware(async (message, next) => {
    stats.increment('messageCount', message.sender.id)
    await next()
  })
  
  addCommand(new MessageCommand('stats')
    .action(() => {
      const data = stats.getStats()
      return `📊 统计数据:
      消息总数:${data.messageCount}
      命令总数:${data.commandCount}  
      用户总数:${data.userCount}`
    })
  )
})

🌍 生态系统与扩展

� 开箱即用

  • 控制台适配器(@zhin.js/adapter-process,默认内置)
  • HTTP 服务(@zhin.js/http)
  • Web 控制台(@zhin.js/console)
  • SQLite 数据库(默认)

🔌 可选扩展(需手动安装)

  • Telegram(@zhin.js/adapter-telegram)
  • Discord(@zhin.js/adapter-discord)
  • QQ(@zhin.js/adapter-qq)
  • KOOK(@zhin.js/adapter-kook)
  • OneBot v11(@zhin.js/adapter-onebot11)
  • MySQL(@zhin.js/database-mysql)
  • PostgreSQL(@zhin.js/database-pg)

�📚 更多资源


🎉 恭喜! 你现在已经掌握了 Zhin 插件开发的核心技能,可以创建功能强大的机器人插件了!