跳转至

🛠️ WxBot Enhanced 插件开发指南

![Plugin Development](../images/plugin-dev-banner.png) **从零开始学习WxBot插件开发,打造属于你的Enhanced插件**

📋 目录导航


🎯 快速开始

开发环境准备

# 1. 环境要求
go version  # 需要 Go 1.21+
git --version  # 需要 Git

# 2. 克隆项目
git clone https://github.com/ruk1ng001/wxbot.git
cd wxbot

# 3. 安装依赖
go mod download

# 4. 创建你的插件目录
mkdir plugins/myfirstplugin
cd plugins/myfirstplugin

创建你的第一个插件

创建 plugins/myfirstplugin/init.go

package myfirstplugin

import (
    "github.com/ruk1ng001/wxbot/engine/control"
    "github.com/ruk1ng001/wxbot/engine/robot"
)

func init() {
    // 注册插件
    engine := control.Register("myfirstplugin", &control.Options{
        Alias:            "我的第一个插件",     // 插件显示名称
        Help:             "这是一个示例插件",   // 帮助信息
        DataFolder:       "myfirstplugin",   // 数据目录名
        DisableOnDefault: false,            // 是否默认禁用
        HideMenu:         false,            // 是否在菜单中隐藏
    })

    // 注册消息处理器
    engine.OnPrefix("hello").SetBlock(true).Handle(helloHandler)
    engine.OnFullMatch("测试").Handle(testHandler)

    // 管理员命令
    engine.OnCommand("myplugin", robot.AdminPermission).Handle(adminHandler)
}

// Hello处理器
func helloHandler(ctx *robot.Ctx) {
    name := ctx.State["args"].(string) // 获取参数
    if name == "" {
        ctx.ReplyText("请告诉我你的名字,例如:hello 张三")
        return
    }
    ctx.ReplyText("你好," + name + "!欢迎使用WxBot Enhanced!")
}

// 测试处理器
func testHandler(ctx *robot.Ctx) {
    ctx.ReplyText("插件测试成功!🎉")
}

// 管理员处理器
func adminHandler(ctx *robot.Ctx) {
    ctx.ReplyText("管理员命令执行成功!")
}

注册插件

plugins.yaml 中添加你的插件:

# 在文件末尾添加
- "github.com/ruk1ng001/wxbot/plugins/myfirstplugin"  # 我的第一个插件

测试插件

# 生成插件导入
make plugins

# 运行测试
go run main.go

# 在微信中测试
# 发送: hello 世界
# 期望回复: 你好,世界!欢迎使用WxBot Enhanced!

🏗️ 插件架构

插件生命周期

graph TD
    A[插件加载] --> B[init函数执行]
    B --> C[Register注册]
    C --> D[配置Options]
    D --> E[注册处理器]
    E --> F[插件就绪]
    F --> G[接收消息]
    G --> H[规则匹配]
    H --> I[执行处理器]
    I --> J[发送响应]
    J --> G

核心组件

组件 作用 示例
Register 插件注册入口 control.Register("pluginname", options)
Options 插件配置选项 别名、帮助、数据目录等
Engine 插件引擎 提供API调用和数据存储
Handler 消息处理器 业务逻辑实现
Matcher 消息匹配器 定义触发条件
Context 上下文对象 包含事件信息和响应方法

📝 基础插件开发

插件配置详解

type Options struct {
    Alias            string  // 插件别名,用于显示
    Help             string  // 插件帮助信息
    DataFolder       string  // 数据目录名称(必须唯一)
    DisableOnDefault bool    // 是否默认禁用
    HideMenu         bool    // 是否在插件菜单中隐藏
    Priority         uint64  // 优先级(可选,自动分配)
}

// 使用示例
engine := control.Register("weather", &control.Options{
    Alias:            "天气查询",
    Help:             "查询城市天气信息\n使用方法:天气 城市名",
    DataFolder:       "weather",
    DisableOnDefault: false,
    HideMenu:         false,
})

消息匹配器类型

// 1. 前缀匹配 - 最常用
engine.OnPrefix("天气", "weather").Handle(weatherHandler)
// 匹配: "天气 北京", "weather beijing"
// 参数存储在: ctx.State["args"]

// 2. 完全匹配
engine.OnFullMatch("查看帮助", "help").Handle(helpHandler)
// 匹配: "查看帮助", "help"

// 3. 后缀匹配
engine.OnSuffix("查询", "query").Handle(queryHandler)
// 匹配: "北京天气查询"

// 4. 正则匹配 - 高级用法
engine.OnRegex(`查询(\d+)`).Handle(regexHandler)
// 匹配: "查询12345"
// 结果存储在: ctx.State["regex_matched"]

// 5. 关键词匹配
engine.OnKeyword("你好", "hello").Handle(greetHandler)
// 匹配包含关键词的消息

// 6. 命令匹配 - 管理功能
engine.OnCommand("reload", robot.AdminPermission).Handle(reloadHandler)
// 匹配: "/reload" (需要命令前缀)

// 7. 通用消息匹配 - 高级用法
engine.OnMessage(robot.OnlyGroup, robot.AdminPermission).Handle(generalHandler)
// 可组合多个条件

匹配器配置选项

// 设置优先级(数值越小优先级越高)
engine.OnPrefix("urgent").SetPriority(1).Handle(urgentHandler)

// 设置为最高优先级
engine.OnPrefix("emergency").FirstPriority().Handle(emergencyHandler)

// 设置阻断(阻止后续匹配器处理同一消息)
engine.OnPrefix("stop").SetBlock(true).Handle(stopHandler)

// 设置无超时处理(对于长时间运行的任务)
engine.OnPrefix("longtask").SetNoTimeout(true).Handle(longTaskHandler)

// 链式调用
engine.OnPrefix("weather").
    SetBlock(true).
    SetPriority(10).
    Handle(weatherHandler)

上下文(Context)使用

func weatherHandler(ctx *robot.Ctx) {
    // 1. 获取消息信息
    message := ctx.MessageString()      // 消息内容
    userId := ctx.Event.FinalFromWxId   // 发送者ID
    groupId := ctx.Event.FromWxId       // 群组ID(如果是群聊)

    // 2. 获取参数
    city := ctx.State["args"].(string)  // 前缀后的参数

    // 3. 基础回复
    ctx.ReplyText("今天北京晴,温度25°C")
    ctx.ReplyImage("local://weather.png")  // 本地图片
    ctx.ReplyImage("https://example.com/weather.jpg")  // 网络图片

    // 4. 智能@回复(群聊时自动@,私聊时直接回复)
    ctx.ReplyTextAndAt("@你 今天天气不错!")

    // 5. 发送到指定对象
    ctx.SendText("wxid_target", "发送给特定用户")
    ctx.SendTextAndAt("group_id", "user_id", "昵称", "群内@特定人")

    // 6. 高级消息类型
    ctx.ReplyShareLink("标题", "描述", "图片URL", "跳转链接")
    ctx.ReplyMiniProgram("小程序ID", "标题", "内容", "图片", "跳转路径")
    ctx.ReplyMusic("歌名", "歌手", "app", "跳转链接", "播放链接", "封面")

    // 7. 判断消息类型和来源
    if ctx.IsEventGroupChat() {
        // 群聊消息处理
    } else if ctx.IsEventPrivateChat() {
        // 私聊消息处理
    }

    if ctx.IsAt() {
        // 被@的消息
    }

    // 8. 获取用户和群组信息
    userInfo, _ := ctx.GetObjectInfo(userId)
    friends, _ := ctx.GetFriends()
    groups, _ := ctx.GetGroups()
    if ctx.IsEventGroupChat() {
        members, _ := ctx.GetGroupMembers(groupId)
    }
}

权限控制系统

// 内置权限规则
engine.OnCommand("admin", robot.AdminPermission).Handle(adminHandler)
engine.OnCommand("manage", robot.GroupAdminPermission).Handle(manageHandler)
engine.OnCommand("user", robot.UserOrGroupAdmin).Handle(userHandler)

// 环境限制
engine.OnPrefix("群功能", robot.OnlyGroup).Handle(groupHandler)
engine.OnPrefix("私聊功能", robot.OnlyPrivate).Handle(privateHandler)
engine.OnPrefix("@功能", robot.OnlyAtMe).Handle(atHandler)

// 自定义权限规则
func vipUserOnly(ctx *robot.Ctx) bool {
    vipUsers := []string{"wxid1", "wxid2", "wxid3"}
    for _, vip := range vipUsers {
        if vip == ctx.Event.FinalFromWxId {
            return true
        }
    }
    ctx.ReplyText("此功能仅限VIP用户使用")
    return false
}

engine.OnPrefix("vip", vipUserOnly).Handle(vipHandler)

数据存储

func dataHandler(ctx *robot.Ctx) {
    engine := ctx.GetMatcher().Engine // 获取插件引擎

    // 获取插件数据目录
    dataDir := engine.GetDataFolder()     // data/plugins/myplugin/
    cacheDir := engine.GetCacheFolder()   // data/plugins/myplugin/cache/

    // 文件操作
    configFile := filepath.Join(dataDir, "config.json")
    cacheFile := filepath.Join(cacheDir, "temp.dat")

    // 读写文件...
}

异步事件处理

func interactiveHandler(ctx *robot.Ctx) {
    ctx.ReplyText("请输入你的选择(输入数字1-3):")

    // 等待用户下一条消息
    next := ctx.EventChannel(ctx.CheckUserSession()).Next()
    select {
    case <-time.After(30 * time.Second):
        ctx.ReplyText("超时,操作取消")
        return
    case nextCtx := <-next:
        choice := nextCtx.MessageString()
        switch choice {
        case "1", "2", "3":
            ctx.ReplyText("你选择了: " + choice)
        default:
            ctx.ReplyText("无效选择")
        }
    }
}

📚 更多文档

示例插件

查看完整的插件示例:


🎮 游戏插件开发

游戏类插件特点

游戏插件需要处理复杂的状态管理和用户交互:

// 游戏状态管理
type GameState struct {
    PlayerID    string
    GameID      string
    Status      GameStatus
    Score       int
    StartTime   time.Time
}

// 游戏插件示例
func gameHandler(ctx *robot.Ctx) {
    // 创建游戏实例
    game := NewGame(ctx.Event.FinalFromWxId)

    // 游戏逻辑处理
    result := game.Process(ctx.MessageString())

    // 发送游戏结果
    ctx.ReplyText(result.Message)
}

详细的游戏插件开发请参考: - 猜成语Enhanced - 完整的游戏插件实现 - 成语接龙 - 多人游戏状态管理

📊 监控集成

插件监控统计

WxBot Enhanced提供完整的监控系统集成:

import "github.com/ruk1ng001/wxbot/engine/pkg/monitor"

func monitoredHandler(ctx *robot.Ctx) {
    start := time.Now()
    defer func() {
        // 自动记录执行时间
        duration := time.Since(start)
        monitor.GlobalMetrics.RecordPluginExecution("pluginname", duration, false)
    }()

    // 插件逻辑
    processMessage(ctx)
}

自定义指标

// 业务指标统计
monitor.GlobalMetrics.RecordMessage()
monitor.GlobalMetrics.UpdateConnectionStatus("connected")
monitor.GlobalMetrics.IncrementErrorCount("pluginname")

监控数据会自动显示在Web仪表板:http://localhost:7601/monitor.html

🔒 安全最佳实践

输入验证

import "github.com/ruk1ng001/wxbot/engine/pkg/validator"

func secureHandler(ctx *robot.Ctx) {
    input := ctx.MessageString()

    // 输入验证
    if err := validator.ValidateMessage(input); err != nil {
        ctx.ReplyText("输入格式有误: " + err.Error())
        return
    }

    // 文件路径安全检查
    if err := validator.ValidateFilePath(filePath); err != nil {
        log.Warnf("文件路径不安全: %v", err)
        return
    }
}

权限控制

// 管理员权限检查
engine.OnCommand("admin", robot.AdminPermission)

// 自定义权限验证
func customPermission(ctx *robot.Ctx) bool {
    userID := ctx.Event.FinalFromWxId
    return isUserAuthorized(userID)
}

engine.OnPrefix("vip", customPermission)

频率限制

var userRateLimit = make(map[string]time.Time)
var rateMutex sync.RWMutex

func rateLimitedHandler(ctx *robot.Ctx) {
    userID := ctx.Event.FinalFromWxId

    rateMutex.Lock()
    lastTime, exists := userRateLimit[userID]
    if exists && time.Since(lastTime) < time.Minute {
        rateMutex.Unlock()
        ctx.ReplyText("请求过于频繁,请稍后再试")
        return
    }
    userRateLimit[userID] = time.Now()
    rateMutex.Unlock()

    // 处理请求
}

🧪 测试和调试

单元测试

package myplugin

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestPluginHandler(t *testing.T) {
    // 模拟消息上下文
    ctx := &robot.Ctx{
        Event: &robot.Event{
            Message: &robot.Message{
                Content: "test message",
            },
        },
    }

    // 测试处理器
    result := processMessage(ctx.MessageString())
    assert.NotEmpty(t, result)
}

调试日志

import "github.com/ruk1ng001/wxbot/engine/pkg/log"

func debugHandler(ctx *robot.Ctx) {
    log.Debugf("[%s] 收到消息: %s", "pluginname", ctx.MessageString())

    // 业务逻辑
    result := processMessage(ctx)

    log.Infof("[%s] 处理结果: %s", "pluginname", result)
}

性能测试

func BenchmarkPluginHandler(b *testing.B) {
    ctx := createTestContext()

    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        pluginHandler(ctx)
    }
}

**🛠️ 开始你的插件开发之旅!** **让WxBot Enhanced变得更加强大和有趣!**