就要下班了,兵長開啟手機,看到彈出的某微信聊天機器人廣告便點了進去,於是有了如下故事…
最近兵長在看微信的時候突發奇想的去玩了一下某微信機器人,能夠像智慧語音助手一下和自己聊天
兵長就在想,這機器人是咋做的,我們們是做伺服器開發的,我們用go語言快速實現一下給自己玩玩,實現一個定製化的聊天機器人可好
胖sir聽到兵長自言自語的不明所以,便走上前說,咋開始玩起聊天了,不來峽谷遊了嗎?
上次帶你本來是想帶你成為winer的,沒想到,每一把都是loser,我打算最近收收手,控制一下情緒,找機器人撫慰一下我手上的心靈
你是說微信聊天機器人嗎?哪些不都是千篇一律的嘛
那麼你能弄個定製化的嘛?把我情緒弄好了,我帶你來大亂鬥吧
~~(偷笑),小夥子,還好我留了一手,我先給你說說微信後臺伺服器如何初步開發一個簡單的你問我答功能吧,授人以漁,不如授人以魚
是不
開發一個微信後臺伺服器作為被動回覆機器人
,大致分為如下幾步:
- 開通公眾號,註冊微信公眾號開發平臺,這裡可以是註冊
訂閱號
,詳細的後面給你說 - 配置許可權,配置微信後臺開發者許可權
- 流程介紹
- 接入微信後臺
- 功能實現
- 兵長亂鬥帶胖sir飛
開通公眾號
註冊微信公眾號開發平臺,這裡可以是註冊訂閱號
,按照提示進行註冊輸入資訊即可,相信你一看就會
註冊地址:https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN&token=
我整理了一個表格來介紹一下訂閱號和伺服器號的區別,你也可以先初步瞭解一下區別,後面可以慢慢琢磨
訂閱號 | 服務號 | |
---|---|---|
用途 | 主要目的 為大家傳播諮詢;類似報紙,雜誌,個人輸出 | 偏向企業或組織的互動,如銀行,商場,餐廳等;自助服務的服務號,用於企業 |
群發次數 | 一天可以傳送一次;所有的訂閱號推送訊息,會被統一收納到訂閱號欄目中 | 一個月傳送4條群發訊息 |
展示位置 | 全部收錄在 訂閱號的 資訊欄中 | 展示在好友訊息列表之中;關注一個服務號,即相當與加了一個朋友 |
微信支付 | 不可開通支付功能 | 認證後 可以開通微信支付功能 |
自定義選單 | 相對簡單 | 相對高階,微信有介面,可以自行開發 |
配置許可權
配置微信後臺開發者許可權
- 進入公眾號管理頁面,下拉左邊側,進入基本配置
URL
:填寫自己的外網伺服器URL
,如果沒有可以買一個雲伺服器,現在買雲伺服器還是很便宜的Token
:自定義Token
,用於製作簽名,這個非常重要,需要保密EncodingAESKey
:隨機生成即可訊息加解密方式
:為了演示方便,我們這裡使用明文模式
- 微信公眾號後臺介面許可權
普通使用者只要是接收訊息和自動回覆訊息的許可權
流程介紹
開發被動回覆訊息流程介紹,簡單來說,可以是這樣的
功能實現必備知識點
http服務
進行通訊- Token機制
- 微信後臺開發
xml
的資料序列化
http服務
做上述被動回覆訊息的功能,此處僅需要後臺伺服器實現get
方法和post
方法即可
get方法
主要是用於,我們在微信後臺設定token的時候,微信後臺會向我們的伺服器傳送get請求,判斷我們服務是否有正確的資料
post方法
主要是用於,粉絲在我們的微信後臺傳送訊息的時候,是以post的方式傳送給我們的後臺伺服器的
Token機制
引數 | 描述 |
---|---|
signature | 微信加密簽名,signature結合了開發者填寫的token引數和請求中的timestamp引數、nonce引數。 |
timestamp | 時間戳 |
nonce | 隨機數 |
echostr | 隨機字串 |
開發者通過檢驗signature對請求進行校驗(下面有校驗方式)。若確認此次GET請求來自微信伺服器,請原樣返回echostr
引數內容,則接入生效,成為開發者成功,否則接入失敗。加密/校驗流程如下:
1)將token、timestamp、nonce三個引數進行字典序
排序
2)將三個引數字串拼接成一個字串進行sha1加密
3)開發者獲得加密後的字串可與signature
對比,標識該請求來源於微信
token演算法流程圖
驗證方法(get)
- 1.伺服器端獲取token、nonce、timestamp組成列表
- 2.列表排成字典序
- 3.排序後的元素進行摘要
- 4.摘要比對signature
- 5.響應
echostr
參考微信後臺開發文件連線:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Getting_Started_Guide.html
xml資料解析
兵長,不知道你有沒有用過xml來做資料的序列化,此處你肯定會問,為什麼用xml,而不用json,不用protobuf
來我給你簡單介紹一下xml:
XML是可擴充套件標記語言,其中標記指的是計算機中所能理解的資訊符號,通過標記計算機之間可以處理包含各種資訊的資源,我們可以通過通用的標記語言來進行標記
用人話來說就是,xml是資料序列化的其中一種方式,微信定下來使用該方式來對資料進行序列化,我們需要對此進行開發,因此也需要遵循微信的規則
例如 微信後臺的 text訊息型別 請求 xml格式如下
文字訊息,微信公眾平臺請求微信後臺伺服器會帶的欄位有:FromUserName
,ToUserName
,CreateTime
,MsgType
,Content
,MsgId
我們開發的微信後臺伺服器,需要按照如下資料格式做回覆:xml 帶上如下欄位ToUserName
,FromUserName
,CreateTime
,MsgType
,Content
兵長,再給你回顧一下,實現微信後臺伺服器被動回覆訊息的服務,需要用到上述說到的3點,http服務、token機制、xml解析,記住咯,我要開始擼程式碼了
具體實現
main.go
- 啟動http伺服器,開始監聽80埠
package main
import (
"fmt"
"github.com/wonderivan/logger"
"net/http"
"time"
)
const (
port = 80 //後臺伺服器埠號80,自己的公網伺服器 需要設定防火牆,開啟80埠
token = "XXXXXXX"
)
// 需要自己修改token,以適應自己公眾號的token
func main() {
logger.SetLogger("./log.json")
logger.Info(" ------------ start main ------------")
server := http.Server{
Addr: fmt.Sprintf(":%d", port), // 設定監聽地址, ip:port
Handler: &httpHandler{}, // 具體使用哪個handler來處理
ReadTimeout: 5 * time.Second, // 讀寫超時 微信給出來 5秒
WriteTimeout: 5 * time.Second,
MaxHeaderBytes: 0,
}
logger.Info(fmt.Sprintf("Listen: %d", port))
logger.Fatal(server.ListenAndServe())
}
route.go
- 路由功能 , 具體實現get 和post方法
- 定義Handler 以及 ServeHTTP 介面的實現
package main
import (
"fmt"
"github.com/wonderivan/logger"
"io"
"net/http"
"regexp"
"time"
"wechat/wx"
)
type WebController struct {
Function func(http.ResponseWriter, *http.Request)
Method string
Pattern string
}
var mux []WebController // 自己定義的路由
// ^ 匹配輸入字串的開始位置
func init() {
mux = append(mux, WebController{post, "POST", "^/"})
mux = append(mux, WebController{get, "GET", "^/"})
}
type httpHandler struct{}
//httpHandler 自己實現了 ServeHTTP 介面,可以通過原始碼看到,建立httpHandler 物件的時候,實際原始碼邏輯是會呼叫ServeHTTP進行處理
func (*httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
t := time.Now()
for _, webController := range mux { // 遍歷路由
// 匹配請求的 r.URL.Path -> webController.Pattern
if m, _ := regexp.MatchString(webController.Pattern, r.URL.Path); m { // 匹配URL
logger.Info("webController.Pattern == ", webController.Pattern)
logger.Info("r.URL.Path == ", r.URL.Path)
if r.Method == webController.Method { // 匹配方法
logger.Info("webController.Method == ", webController.Method)
webController.Function(w, r) // 呼叫對應的處理函式
d := time.Now().Sub(t)
l := fmt.Sprintf("[ACCESS] | % -10s | % -40s | % -16s", r.Method, r.URL.Path, d.String())
logger.Info(l)
return
}
}
}
d := time.Now().Sub(t)
l := fmt.Sprintf("[ACCESS] | % -10s | % -40s | % -16s", r.Method, r.URL.Path, d.String())
logger.Info(l)
io.WriteString(w, "")
return
}
// 處理token的認證
func get(w http.ResponseWriter, r *http.Request) {
client, err := wx.NewClient(r, w, token)
if err != nil {
logger.Info(err)
w.WriteHeader(403) // 校驗失敗
return
}
if len(client.Query.Echostr) > 0 {
logger.Info("Echostr == ", client.Query.Echostr)
w.Write([]byte(client.Query.Echostr)) // 校驗成功返回的是Echostr
return
}
w.WriteHeader(403)
return
}
// 微信平臺過來訊息, 處理 ,然後返回微信平臺
func post(w http.ResponseWriter, r *http.Request) {
client, err := wx.NewClient(r, w, token)
if err != nil {
logger.Info(err)
w.WriteHeader(403)
return
}
// 到這一步簽名已經驗證通過了
client.Run()
return
}
具體程式碼實現還有1個定義結構檔案和核心實現檔案:
structs.go
xml序列化訊息對應的結構體的定義,包含基本訊息欄位,
FromUserName
,ToUserName
,MsgType
,CreateTime
,以及文字訊息,圖片訊息,錄音訊息,音樂訊息,地理位置訊息,視訊訊息等需要哪一些欄位,都可以參考微信後臺給出的規則wx.go
token + 隨機數 + 時間戳 排成字典序,並使用sha1加密後生成 signature
NewClient的具體實現
文字訊息,圖片訊息等的訊息處理以及被動回覆功能
上述2個核心檔案若是感興趣,自己有想法並且期望自己實現的小夥伴,可以新增我的微信,可以給大家共享一下
當然,微信後臺開發涉及的功能還很多,今天給大家分享到的還只是冰山一角,沿途的風景還是需要大家一步一個腳印去感受,可以在微信的開發文件中盡情實戰,如下圖,感興趣的可以多多交流。
技術是開放的,我們共享技術,心態也應是開放的,我們擁抱變化,你願意不斷摸索前進,你必將收穫你期望的東西。
以上是本期全部內容,堅持原創,實踐自己的想法,我們下期見
作者:小魔童哪吒
本作品採用《CC 協議》,轉載必須註明作者和本文連結