Go语言如何接入微信机器人API?
一、为什么用 Go 写微信机器人
做微信机器人时,最常见的语言选择是 Python 或 Java,但 Go 在这类场景里其实有明显优势:
- 单二进制部署:编译出一个可执行文件,扔到服务器上就能跑,不用装运行时
- goroutine 天然适合消息并发:Webhook 回调接收和消息发送天然分成两条协程,靠 channel 解耦
- 内存占用低:常驻服务挂一天,内存也就几十 MB
这篇文章不聊原理,直接从扫码登录讲到回调接收、再到消息发送,所有接口以实际可调用为准(接口细节以官方文档 https://weiti.apifox.cn 为准)。
二、整体架构
一个最小可用的 Go 版微信机器人,只需要三个模块:
Webhook接收器(HTTP Server) ──► 消息队列(channel) ──► 发送消费者(goroutine)
│ │
└── 收到消息立刻返回 {"ret": 200} 按队列串行调用发送接口
关键点只有一个:回调处理和消息发送必须分开。回调接口要快速响应,发送逻辑放进独立的消费者协程里慢慢跑。
三、获取登录二维码
首次登录时 appId 传空,服务端会分配一个新的应用 ID;regionId 填账号常用的省份代码(比如 440000 是广东)。
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
const (
baseURL = "https://wx.chuapi.com"
token = "你的X-finder-TOKEN"
)
// 通用 POST 请求
func post(path string, body map[string]interface{}) (map[string]interface{}, error) {
jsonBytes, _ := json.Marshal(body)
req, err := http.NewRequest("POST", baseURL+path, bytes.NewBuffer(jsonBytes))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-finder-TOKEN", token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result, nil
}
// 获取登录二维码
func getLoginQrCode(appId, regionId string) map[string]interface{} {
body := map[string]interface{}{
"appId": appId, // 首次登录传空
"regionId": regionId,
}
return post("/finder/v2/api/login/getLoginQrCode", body)
}
func main() {
result := getLoginQrCode("", "440000")
data := result["data"].(map[string]interface{})
fmt.Println("appId:", data["appId"]) // 记下来,之后所有请求都要用
fmt.Println("uuid:", data["uuid"])
fmt.Println("二维码地址:", data["qrImgUrl"])
}
返回的 data 里会带上 appId 和 uuid,用手机扫码即可。appId 一定要持久化保存,下次重登必须传同一个,否则会被当成新设备。
四、轮询确认登录
扫码后轮询 checkLogin,status 为 2 表示登录成功:
// 确认登录状态
func checkLogin(appId, uuid string) map[string]interface{} {
body := map[string]interface{}{
"appId": appId,
"uuid": uuid,
"autoSliding": true, // 遇到滑块验证自动处理
}
return post("/finder/v2/api/login/checkLogin", body)
}
ret 为 200、status 为 2 时登录完成,可以开始接收消息了。
五、接收 Webhook 回调
登录成功后配置回调地址,消息会以 POST 方式推送过来。回调体里常用的字段有 appId、msgType、fromUser、nickName、content、createTime、chatRoomId。
// 消息结构体,只解析需要的字段
type WxMessage struct {
AppId string `json:"appId"`
MsgType int `json:"msgType"`
FromUser string `json:"fromUser"`
NickName string `json:"nickName"`
Content string `json:"content"`
ChatRoomId string `json:"chatRoomId"`
CreateTime int64 `json:"createTime"`
}
// 回调入口
func callbackHandler(msgCh chan<- WxMessage) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var msg WxMessage
if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
w.Write([]byte(`{"ret": 200}`))
return
}
// 群消息时 chatRoomId 有值,私聊时 fromUser 就是发送人
select {
case msgCh <- msg:
default:
// 队列满了就丢弃,绝不阻塞回调响应
}
// 必须返回这个格式,否则平台会重推
w.Write([]byte(`{"ret": 200}`))
}
}
注意两点:
- 必须返回
{"ret": 200},返回别的格式平台会认为推送失败并重发 - 回调里不做任何耗时操作,消息塞进 channel 立刻返回
六、发送消息的消费者
用 goroutine 从 channel 里取消息,串行调用发送接口,中间加随机间隔模拟真人的打字节奏:
// 发送文本消息
func sendText(appId, toWxid, content string) map[string]interface{} {
body := map[string]interface{}{
"appId": appId,
"toWxid": toWxid,
"content": content,
}
return post("/finder/v2/api/message/postText", body)
}
// 消费者:串行发送,控制节奏
func sender(appId string, msgCh <-chan WxMessage) {
for msg := range msgCh {
toWxid := msg.FromUser
if msg.ChatRoomId != "" {
toWxid = msg.ChatRoomId // 群消息回复到群
}
reply := "收到,正在处理:" + msg.Content
result := sendText(appId, toWxid, reply)
if result["ret"].(float64) == 200 {
fmt.Printf("已回复 %s\n", toWxid)
}
// 随机休眠 1~5 秒,避免高频触发风控
time.Sleep(time.Duration(1000+rand.Intn(4000)) * time.Millisecond)
}
}
ret 为 200 表示发送成功。接口调用一旦返回非 200,别立刻重试,先记日志排查。
七、组装启动
把三个模块串起来:
func main() {
rand.Seed(time.Now().UnixNano())
appId := "从数据库/文件读取的appId"
msgCh := make(chan WxMessage, 1000)
// 后台消费者
go sender(appId, msgCh)
http.HandleFunc("/callback", callbackHandler(msgCh))
fmt.Println("服务启动,监听 :8080")
http.ListenAndServe(":8080", nil)
}
本地联调时回调地址需要公网可达,用 ngrok 做内网穿透即可:
ngrok http 8080
# 把生成的 https 地址 + /callback 配到平台后台
八、几个必须知道的坑
appId是账号身份:掉线重登必须传同一个appId,换了就会被识别为新设备登录- 新号第一晚容易掉线:这是正常现象,重新走一遍
getLoginQrCode→checkLogin即可,稳定期一般在 15 天之后 - 频率控制:一分钟内发送不要超过 40 条,用上面消费者里的随机休眠就能压住
regionId选真实地区:填账号所在地对应的省份代码,异地登录会增加风控风险- channel 缓冲要给够:消息高峰期回调会瞬间涌进来,缓冲太小会被
default分支丢掉
九、Go 版的优势总结
| 环节 | 实现方式 |
|---|---|
| 回调接收 | net/http 原生 Server,无第三方依赖 |
| 解耦 | 带 1000 缓冲的 channel |
| 发送节流 | 单消费者 + 随机休眠 |
| 部署 | 编译单文件,交叉编译一行命令 |
整套代码没有引入任何重框架,标准库就够用,这也是 Go 接这类 API 的典型做法。如果你不想自己处理协议层,直接对接现成的 HTTP API(比如 WTAPI 这类基于 HTTP + Webhook 的方案)再套上面的模板,半天内就能跑通一个稳定的自动回复机器人。
参考资料
接口定义与参数说明文档:weiti.apifox.cn
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)