首頁 > 軟體

GO製作微信機器人的流程分析

2022-08-01 14:00:22

這些天在學習Go,也寫了幾篇關於閱讀Gin後端專案程式碼的部落格。但程式設計這種,一定要實際上手練習,要不然都是紙上談兵。於是就想上手自己實際寫一些程式碼來練練手。思來想去,不知道能寫些什麼來練手。後來突然想到,之前寫過用Python做微信聊天機器人(部落格傳送門),當時程式碼沒有放到git上,後來重置了伺服器導致程式碼全部沒了。現在正好苦於不知道做什麼專案練手,可以用Go也實現一套微信聊天機器人。

說幹就幹,照著之前自己寫的部落格,看了下當時Python的程式碼。轉而用Go優化了下並實現。

0.回顧流程

根據之前Python寫的自動發訊息的機器人可知,要想發訊息就需要三個引數:company_id、secret、angent_id。 對於這三個引數如何獲取,可參考文章開頭的傳送門。整個傳送訊息過程就是 首先通過company_id和secret來呼叫介面獲取token,再通過token和angent_id來給對應介面傳送post請求,就可以把post請求體中的資訊傳送到微信上。

1.專案基礎設定

由於目前對Go的專案佈局學習的還不是特別熟練,而且對於專案基礎部分如果從頭開始做的話,需要耗費大量時間。因此我使用了基於開源gin專案進行二次開發的方法,實現這個機器人。

前幾天在學習Gin時,發現了一位老哥封裝了個Gin腳手架,可以達到開箱即用目的。專案地址: github傳送門。 裡邊把讀取組態檔,編寫路由,連線資料庫等多個操作均進行了實現。因此可以基於這個專案來進行二次開發,做微信機器人。

在把專案clone下來後,可以先看下整個專案的佈局,主要的業務核心程式碼都放在了internal 下面。如果我們要實現一個主動給微信發訊息的功能,那麼多說了就是寫一個傳送訊息的方法,讓後端呼叫這個方法即可。

要想基於此專案來開發微信機器人,首先就要將三個引數設定上。專案中,對於各種引數均在config.yaml中設定,因為可以在這個組態檔中增加這三個引數的設定:

然後在程式碼的config/autoload目錄下新增一個weCaht.go 檔案,接收組態檔中的設定。

package autoload
type WeChatConfig struct {
AgentId string ini:"wechat" yaml:"agent_id"
Secret string ini:"wechat" yaml:"secret"
CompanyId string ini:"wechat" yaml:"company_id"
}
var WeChat = WeChatConfig{}

並且,將此設定加入到專案的設定集合中。在config/config.go中新增如下程式碼:

這樣操作,就可以通過程式碼來讀取組態檔了。在其他包中,可以通過如下方式來存取對應的值

config.Config.WeChat.CompanyId //yaml中的company_id欄位

2. Redis封裝

因為要給微信傳送訊息,首先要獲取到token,而官方介紹此token的有效時長為2小時。在之前Python的專案中,是直接將token寫到了檔案中,通過檔案來讀取。在此專案中,我想直接使用redis來儲存。因為使用redis來儲存的話,可以設定key值時長,過了這個時長就自動清除,這樣就方便了許多。

而我們基於這個gin-layout專案中,已經對redis做了一層封裝,具體程式碼可檢視data/redis.go,主要是通過對外暴露一個Rdb的結構體,來操作redis

而目前我們這邊使用redis,只會用到對應的set和get方法。因此我對這個專案中的redis又做了一層封裝。只對外暴露set,get,del方法。
首先將Rdb變數名改為小寫,這樣就代表不對外暴露,然後在此檔案中新增如下程式碼

func SetRedis(key string, value string, t int64) bool {
expire := time.Duration(t) * time.Second
if err := rdb.Set(ctx, key, value, expire).Err(); err != nil {
return false
}
return true
}

func GetRedis(key string) string {
result, err := rdb.Get(ctx, key).Result()
if err != nil {
return 「」
}
return result
}

func DelRedis(key string) bool {
_, err := rdb.Del(ctx, key).Result()
if err != nil {
return false
}
return true
}

這樣,後續使用redis時候,只需要呼叫data.SetRedis(xxx) 即可。
然後就是修改組態檔,啟用redis,這裡根據實際的redis設定來寫即可。

3.訊息體封裝

在最終給微信伺服器傳送post請求時,對應的請求體格式如下:

{
「touser」: 「@all」,
「msgtype」: 「text」,
「agentid」: 「xxxxx」,
「text」: {「content」: 「xxxx」}
}

因此,接下來可以對這個結構體做一個封裝。在model包下,新建一個send_msg.go檔案

package model
type wcSendcontent struct {
Content string json:"content"
}
type WcSendMsg struct {
ToUser string json:"touser"
MsgType string json:"msgtype"
AgentId string json:"agentid"
Text wcSendcontent json:"text"
}
func (t *WcSendMsg) SetMessage(message string) {
t.Text.Content = message
}

這裡針對message資訊,專門對外暴露了一個方法來進行設定。

4.核心程式碼

在設定好redis,訊息體封裝後,就可以編寫核心的程式碼了。主要就是通過傳送http請求,獲取token,再通過token傳送post請求來傳送訊息。我們可以在service包下新建一個weChat.go的檔案,裡邊新建一個SendWeChat方法來進行訊息傳送操作。

package service

import (
「bytes」
「encoding/json」
「errors」
「fmt」
c "github.com/wannanbigpig/gin-layout/config"
"github.com/wannanbigpig/gin-layout/data"
"github.com/wannanbigpig/gin-layout/internal/model"
log "github.com/wannanbigpig/gin-layout/internal/pkg/logger"
"github.com/wannanbigpig/gin-layout/pkg/utils"
"go.uber.org/zap"
)

/**

@description: 給企微傳送訊息
@param {string} message 訊息內容
@param {string} msgType 訊息型別
@return {*}
*/
func SendWeChat(message string, msgType string) error {
redis_key := 「access_token」
// 嘗試從redis中讀取token
accessToken := data.GetRedis(redis_key)
http := &utils.HttpRequest{}
// 若redis中的token已過期,則重新請求api獲取token
if accessToken == 「」 {
log.Logger.Info(「access token is null, will recall」)
getTokenUrl := fmt.Sprintf(「https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s」,
c.Config.WeChat.CompanyId, c.Config.WeChat.Secret)
log.Logger.Info(「token_url」, zap.String(「url」, getTokenUrl))
http.Request(「GET」, getTokenUrl, nil)
ret := make(map[string]interface{})
if err := http.ParseJson(&ret); err != nil {
return err
}
marshal, _ := json.Marshal(ret)
log.Logger.Info(string(marshal))
accessToken = fmt.Sprintf("%v", ret[「access_token」])
// 寫入redis 有效期2小時
data.SetRedis(redis_key, accessToken, 7200)
}
msg := &model.WcSendMsg{
ToUser: 「@all」,
MsgType: msgType,
AgentId: c.Config.WeChat.AgentId,
}
msg.SetMessage(message)
sendMsgUrl := fmt.Sprintf(「https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%v」, accessToken)
log.Logger.Info(「sendMsgUrl = " + string(sendMsgUrl))
header := map[string]string{「Content-Type」: 「application/json」}
bytesData, _ := json.Marshal(msg)
http.Request(「POST」, sendMsgUrl, bytes.NewReader(bytesData), header)
log.Logger.Info(「bytes data = " + string(bytesData))
ret := make(map[string]interface{})
err := http.ParseJson(&ret)
if err != nil {
return err
}
if ret[「errcode」].(float64) != 0 {
errmsg := fmt.Sprintf(」%v」, ret[「errmsg」])
return errors.New(errmsg)
}
return nil
}

從上面程式碼中可以看出,首先是通過redis來獲取token,若沒有則請求api獲取token,並將其寫入到redis中,有效期為2小時。然後生成一個之前封裝的訊息的結構體,將AgentId和message進行填充後,通過傳送post請求,已達到發訊息的目的。

5.本地測試

若想驗證這個方法,可以通過對外提供一個介面,存取此介面後呼叫傳送訊息的方法。
可以在controller目錄下新建一個weChat.go,在裡邊實現一個get請求的方法,獲取請求中的msg引數,然後呼叫剛才實現的傳送企微的方法。

package wechat

import (
「github.com/gin-gonic/gin」
「github.com/wannanbigpig/gin-layout/internal/pkg/error_code」
log 「github.com/wannanbigpig/gin-layout/internal/pkg/logger」
r 「github.com/wannanbigpig/gin-layout/internal/pkg/response」
「github.com/wannanbigpig/gin-layout/internal/service」
)

func SendMsg(c *gin.Context) {
msg, ok := c.GetQuery(「msg」)
if !ok {
msg = 「please input message」
}
log.Logger.Info("send wechat message: " + msg)
err := service.SendWeChat(msg, 「text」)
if err != nil {
r.Resp().FailCode(c, error_code.FAILURE, err.Error())
return
}
r.Success(c, 「success」)
}

寫好後,將此方法系結到路由上。在routers包下新建一個weChatRouter.go檔案

package routers

import (
「github.com/gin-gonic/gin」
w 「github.com/wannanbigpig/gin-layout/internal/controller/wechat」
)

func setWeChatRouter(r *gin.Engine) {
// version 1
v1 := r.Group(「wechat」)
{
v1.GET("/send", w.SendMsg)
}
}

這樣,後續可以通過wechat/send的url來請求這個介面。最後就是呼叫此係結路由的方法,在routers/router.go中新增一行程式碼即可

接下來啟動專案,比如傳送一個msg=Hello,Golang 的請求

curl --location --request GET 「http:// I P : {IP}: IP:{PORT}/wechat/send?msg=Hello,Golang」

執行這個命令,就可以得到本文開頭的截圖。

當然,這個api介面主要是為了讓我們驗證,實際專案執行時,建議不要這麼搞。因為這介面沒有任何鑑權的措施,如果對外暴露了出去,那麼別人也可以肆意的呼叫這個介面給你的企微傳送訊息。

到此這篇關於利用go製作微信機器人的文章就介紹到這了,更多相關go微信機器人內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com