🙉微信监控机器人设计
1、背景
此文是我在公司内部的技术分享,在其基础上稍微做了修改。源码地址
细节决定成败,完善的售后服务机制是在众多产品中脱颖而出的关键因素。面对公司众多的客服群,客服人员可能不足,也或许没有及时留意到客服群的问题反馈,有时可能会造成不良的印象,从而影响到公司品牌。
因此一套自动监控微信客服群的系统会大大提高工作效率,及时发现问题和把问题存档。于是,微信监控机器人因此诞生。
2、组件
微信监控机器人的组件如下:
特别说明的是:
鉴权模块主要是针对第三方应用调用web server
暴露的RESTful API
,并不是任何应由有权限调用,而是需要统一申请分配appId
,appKey
作为调用凭证。
监控模块主要是在wechat Bot
掉线,发生错误时发送邮件通知管理员进行处理。
3、架构
微信监控机器人的详细架构如下:
3.1、web wechat登陆流程
3.1.1、协议分析
- 1、getUuid()
。请求本次扫码登录的随机uuid
,每次登陆都会拥有一个新的uuid
,作为此次登录的uuid
- https://login.weixin.qq.com/jslogin
- 2、genQrCode()
。请求二维码
- https://login.weixin.qq.com/qrcode/ + uuid
- 3、wait4login()
。扫码登陆,得到重定向页面window.redirect_uri
- https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%d&uuid=%s&_=%s
- 4、login()
。登陆获取key,wxsid,wxuin,pass_ticket
,存cookie
- window.redirect_uri
- 5、init()
。初始化数据,得到访问数据秘钥syncKey,syncKeyStr
- window.redirect_uri + /webwxinit
- 6、statusNotify()
。通知微信服务端登陆ok
- wc.redirectUri[:strings.LastIndex(wc.redirectUri, "/")] + /webwxstatusnotify?lang=zh_CN&pass_ticket=%s
- 7、GetContact()
。拉取好友,群列表信息(pass_ticket,skey
在login()
请求得到)
- wc.redirectUri[:strings.LastIndex(wc.redirectUri, "/")] + /webwxgetcontact?lang=zh_CN&seq=%s&pass_ticket=%s&skey=%s&r=%s
- 8、procMsgLoop()
。响应调度循环
- syncCheck(),sync()心跳同步维持连接
- https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=%s&sid=%s&uin=%s&skey=%s&deviceid=%&synckey=%s&_=%s
- handleMsg()
。消息处理
- wc.redirectUri[:strings.LastIndex(wc.redirectUri, "/")] + /webwxsync?sid=%s&skey=%s&lang=en_US&pass_ticket=%s
3.1.2、协议示例
可以看到代码中wechat
登录流程实质是模拟web wechat
协议的。
4、核心模块
- webot模块
- web server模块
- 核心业务模块
4.1、webot模块
- 初始化
server
- 模拟
web wechat
登陆流程 - 存储
token
,存储用户信息到session
,存储key
凭证,供暴露接口查询和使用 - 初始化
web server
,关键goroutine
(文本处理,阿里云交互),消息队列和db
webot
模块是整个微信机器人的核心组件,主要是接收微信消息,解析消息,然后消息入库,并且push
到消息队列,至此,它的一次信息传递就结束了,剩下的工作是核心业务goroutine
来处理。
4.2、web server
keepalive
(防止长时间被踢掉线)RESTful api
暴露发送消息到好友,群接口
web server
初始化并启动:
g := gin.New()
gin.SetMode(config.Config.ServerConf.Mode)
g.Use(gin.Recovery())
g.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "The incorrect API route")
})
g.GET(config.Config.WxBot4gConf.HeartbeatURL, TextHandle)
v1 := g.Group("/v1/msg")
{
v1.GET("/text", TextHandle)
v1.POST("/image", ImageHandle)
}
go InitHeartbeatCron()
vslog.Error(http.ListenAndServe(":"+strconv.Itoa(config.Config.ServerConf.Port), g).Error())
项目中的web server
是使用了gin这款简洁轻量级,压测结果很优秀的web框架。gin
的核心是一个Engine
和Context
,Engine
是整个gin
框架的主控,Context
是把原生web请求的数据封装,并且带上一些重要的信息。
4.2.1、keepalive
通过定时任务,3分钟左右发送一次,目的是防止长时间无数据交互,被微信后台kill掉。
- http://ip:port/v1/msg/text?word=keepalive&appkey=dkjhst89353oinf092yr90hnj&to=文件传输助手
4.2.2、发送消息(文本/图片)
提供RESTful api,供外部调用,提供发送消息到指定好友,群的功能。
- http://ip:port/v1/msg/text?word=测试&appkey=dkjhst89353oinf092yr90hnj&to=测试群
- http://ip:port/v1/msg/image?imgUrl=http://xxxx&appkey=dkjhst89353oinf092yr90hnj&to=测试群
4.3、核心业务模块
- 文本处理和情感分析
goroutine
aliyun oss
交互goroutine
golang的经典语:
Don't communicate by sharing memory, share memory by communicating
goroutine
,chan
是Go的特色,goroutine
的优点就不必多说了,这两个业务通过goroutine
处理,通过nsq消息队列,文本处理和情感分析 goroutine
sub订阅文本处理topic
,aliyun oss
交互 goroutine
sub订阅图片msg
,主要wechat Bot
收到微信消息,并push
到nsq
消息队列,两个goroutine
就会得到并处理。
通过goroutine
和消息队列,把业务处理解耦,借助消息队列的堆积功能,把聊天msg
正常的积压在队列中,这样下游和上游不依赖,处理速度可以视下游goroutine
的处理能力来决定,避免出现因众多微信群的聊天记录同时到来而处理不过来的情况。
5、系统改良点:
- 群消息,非好友显示unknow。通过存储启动的用户信息到map,处理消息时通过暂时的UserName查询用户信息得到RemarkName或者DisplayName
- 通过启动web server的keepalive定时任务,维持长时间的web wechat不掉线
- 增加自动更新通信录
6、技术栈
6.1、Python版本
- python2.7
- 标准库queue,web server
- mysql
- aliyun api
- wechat 协议
6.2、Go版本
- go1.13
- mysql数据库
- nsq消息队列
- redis
- gin
- gorm
- aliyun api
- wechat 协议
7、成果展示:
8、总结
在逐步完善项目的同时,项目已正常跑在线上,实时监控众多客服群,并且提供给内部应用调用发送消息等能力。我认为,自动化的监控系统确实是可以大大提高生产力的,并且能够帮助人们更好的完成工作。