概述
由於大神採用Go語言編寫,平時我接觸不多,編寫Flutter時用過一段時間,那麼接下來的騷操作來了,我全程利用chatgpt調試修改代碼,在前文中,我們已經有了Py3接入IMMP平台的demo代碼,該代碼處理了通信和加解密,我們完全可以復用它,中間做一層生產者消費者程序對接就可以了,總體架構圖如下:
功能代碼
golang環境安裝
直接問chatgpt,主要涉及牆的操作,其他毫無壓力,環境搭建非常簡單,可在vscode中調試運行代碼。
編寫wxBot
直接在作者原始碼基礎上修改即可,以下是依賴文件:
module github.com/kongshanxuelin/wechatdemo
go 1.20
require github.com/eatmoreapple/openwechat v1.4.3
require (
github.com/gofrs/uuid v4.0.0+incompatible // indirect
golang.org/x/net v0.10.0 // indirect
)
編寫一個主程序,如下代碼:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"unsafe"
"github.com/eatmoreapple/openwechat"
)
type Result struct {
Errno int
Msg string
Res bool
}
func post(url string, data map[string]string) Result {
bytesData, _ := json.Marshal(data)
res, err := http.Post(url,
"application/json;charset=utf-8", bytes.NewBuffer([]byte(bytesData)))
if err != nil {
fmt.Println("Fatal error ", err.Error())
}
defer res.Body.Close()
content, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println("Fatal error ", err.Error())
}
//fmt.Println(string(content))
str := (*string)(unsafe.Pointer(&content))
var retValue Result
fmt.Println(*str)
if err := json.Unmarshal([]byte(*str), &retValue); err == nil {
return retValue
} else {
fmt.Println(err)
}
return retValue
}
func postMsg(msg *openwechat.Message) {
// 這個是原demo程序提供的rest api服務
url := "http://127.0.0.1:12222/api/sendmsg"
content := msg.Content
senderId := ""
senderName := ""
groupName := ""
groupId := ""
receiverId := ""
receiverName := ""
if msg.IsSendByGroup() {
sender, err := msg.Sender()
if err == nil {
groupId = sender.UserName
groupName = sender.NickName
}
} else {
sender, err := msg.Sender()
if err == nil {
senderId = sender.UserName
senderName = sender.NickName
}
}
receiver, err := msg.Receiver()
if err == nil {
receiverId = receiver.UserName
receiverName = receiver.NickName
}
msgParam := make(map[string]string)
msgParam["source"] = "wechatAgent"
msgParam["content"] = content
msgParam["groupName"] = groupName
msgParam["groupId"] = groupId
msgParam["senderId"] = senderId
msgParam["senderName"] = senderName
msgParam["robotId"] = "demo1"
msgParam["receiverId"] = receiverId
msgParam["receiverName"] = receiverName
aa := post(url, msgParam)
// 這裡可以根據返回結果,如果aa.res是true的直接ack掉這條消息,可以使用一個隊列+線程來解決
fmt.Println("獲取響應:", aa)
}
func main() {
bot := openwechat.DefaultBot(openwechat.Desktop) // 桌面模式
// 註冊消息處理函數
bot.MessageHandler = func(msg *openwechat.Message) {
fmt.Println("收到一套消息start")
fmt.Println(msg)
fmt.Println("收到一套消息emd")
postMsg(msg)
if msg.IsText() && msg.Content == "ping" {
msg.ReplyText("pong")
}
}
// 熱登錄,調試程序的時候啟動
reloadStorage := openwechat.NewFileHotReloadStorage("storage.json")
defer reloadStorage.Close()
err := bot.PushLogin(reloadStorage, openwechat.NewRetryLoginOption())
if err != nil {
fmt.Println("需要重新登錄了!")
// 註冊登陸二維碼回調
bot.UUIDCallback = openwechat.PrintlnQrcodeUrl
// 登陸
if err := bot.Login(); err != nil {
fmt.Println(err)
return
}
}
// 獲取登陸的用戶
self, err := bot.GetCurrentUser()
if err != nil {
fmt.Println(err)
return
}
// 獲取所有的好友
friends, err := self.Friends()
fmt.Println(friends, err)
// 獲取所有的群組
groups, err := self.Groups()
fmt.Println(groups, err)
// 阻塞主goroutine, 直到發生異常或者用戶主動退出
bot.Block()
}
想一想,如何改寫以上程序,讓其作為消費者消費實時收到的消息呢?就是定義一個通道即可,如下:
ch := make(chan *openwechat.Message, 100)
//開啟線程掃描
go func() {
defer wg.Done()
for data := range ch {
fmt.Printf("發起post請求,確認這條消息,失敗重新扔回隊列即可")
}
}()
測試代碼
接下去,我們在微信給這個帳號發一條消息,然後利用postman查看immp平台上是否有這個消息即可,如下即可從平台接收到這條消息,這個過程是完全實時的:
那麼,如何讓下遊程序調用發送消息接口呢?其實也非常簡單,這時這個go程序就需要開放一個接口讓demo遠程調用到即可(前文的demo程序和go程序肯定在一個區域網的,所以go程序無需對外提供接口),這個辦法就很多了,你可以為go程序開一個rest api服務,這樣demo程序收到發送消息請求後,會調用你並將必要的參數透傳給你,go程序去處理本地發送wx消息就可以了。其他和Bot有關的指令都可以在demo程序的@sio.on('dynamicApi')回調中處理調用go程序。如下圖,我們處理了最簡單的下游服務調用平台發送消息ping後,託管的wx會回復pong的應答,如下圖:
附圖,chatgpt給的編寫rest api服務的demo:
好了,通過以上文章,相信大家對消息中轉分發平台的作用有了更全面的了解,相比與傳統的hook wx,本文提供了另一種思路做wxBot,好處是無需考慮wx客戶端版本,當然缺點也有,就是你在使用Bot的同時,不能再用客戶端登錄wx,因為會互踢。本文僅作為技術研究探討,相關代碼切勿用於商業,應在合規基礎上正確使用wx服務。
作者:空山雪林
來源:微信公眾號:Sumslack團隊
出處:https://mp.weixin.qq.com/s/uJ1bKNCwF4WmhQQ5vjqo4w