使用Go語言實現wxBot集成到消息分發平台

閃念基因 發佈 2023-06-11T17:49:27.584321+00:00

概述由於大神採用Go語言編寫,平時我接觸不多,編寫Flutter時用過一段時間,那麼接下來的騷操作來了,我全程利用chatgpt調試修改代碼,在前文中,我們已經有了Py3接入IMMP平台的demo代碼,該代碼處理了通信和加解密,我們完全可以復用它,中間做一層生產者消費者程序對接就

概述

由於大神採用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

關鍵字: