add code notes

This commit is contained in:
Ivy1996-encode 2021-02-18 19:26:21 +08:00
parent 61a1313bd9
commit 6475fe6bfc
8 changed files with 98 additions and 6 deletions

24
bot.go
View File

@ -17,6 +17,7 @@ type Bot struct {
exit chan bool
}
// 判断当前用户是否正常在线
func (b *Bot) Alive() bool {
if b.self == nil {
return false
@ -29,6 +30,7 @@ func (b *Bot) Alive() bool {
}
}
// 获取当前的用户
func (b *Bot) GetCurrentUser() (*Self, error) {
if b.self == nil {
return nil, errors.New("user not login")
@ -36,6 +38,8 @@ func (b *Bot) GetCurrentUser() (*Self, error) {
return b.self, nil
}
// 用户登录
// 该方法会一直阻塞,直到用户扫码登录,或者二维码过期
func (b *Bot) Login() error {
b.prepare()
uuid, err := b.Caller.GetLoginUUID()
@ -65,17 +69,22 @@ func (b *Bot) Login() error {
}
}
// 登录逻辑
func (b *Bot) login(data []byte) error {
// 判断是否有登录回调,如果有执行它
if b.LoginCallBack != nil {
b.LoginCallBack(data)
}
// 获取登录的一些基本的信息
info, err := b.Caller.GetLoginInfo(data)
if err != nil {
return err
}
// 将LoginInfo存到storage里面
b.storage.SetLoginInfo(*info)
// 构建BaseRequest
request := BaseRequest{
Uin: info.WxUin,
Sid: info.WxSid,
@ -83,23 +92,30 @@ func (b *Bot) login(data []byte) error {
DeviceID: GetRandomDeviceId(),
}
// 将BaseRequest存到storage里面方便后续调用
b.storage.SetBaseRequest(request)
// 获取初始化的用户信息和一些必要的参数
resp, err := b.Caller.WebInit(request)
if err != nil {
return err
}
// 设置当前的用户
b.self = &Self{Bot: b, User: &resp.User}
b.storage.SetWebInitResponse(*resp)
// 通知手机客户端已经登录
if err = b.Caller.WebWxStatusNotify(request, *resp, *info); err != nil {
return err
}
// 开启协程,轮训获取是否有新的消息返回
go func() {
b.stopAsyncCALL(b.asyncCall())
}()
return nil
}
// 轮训请求
// 根据状态码判断是否有新的请求
func (b *Bot) asyncCall() error {
var (
err error
@ -112,9 +128,11 @@ func (b *Bot) asyncCall() error {
if err != nil {
return err
}
// 如果不是正常的状态码返回,发生了错误,直接退出
if !resp.Success() {
return fmt.Errorf("unknow code got %s", resp.RetCode)
}
// 如果Selector不为0则获取消息
if !resp.NorMal() {
if err = b.getMessage(); err != nil {
return err
@ -130,6 +148,8 @@ func (b *Bot) stopAsyncCALL(err error) {
b.err = err
}
}
// 获取新的消息
func (b *Bot) getMessage() error {
info := b.storage.GetLoginInfo()
response := b.storage.GetWebInitResponse()
@ -138,10 +158,14 @@ func (b *Bot) getMessage() error {
if err != nil {
return err
}
// 更新SyncKey并且重新存入storage
response.SyncKey = resp.SyncKey
b.storage.SetWebInitResponse(response)
// 遍历所有的新的消息,依次处理
for _, message := range resp.AddMsgList {
// 根据不同的消息类型来进行处理,方便后续统一调用
processMessage(message, b)
// 调用自定义的处理方法
b.messageHandlerGroups.ProcessMessage(message)
}
return nil

View File

@ -2,22 +2,26 @@ package openwechat
import (
"errors"
"fmt"
"os"
)
// 调用请求和解析请求
// 上层模块可以直接获取封装后的请求结果
type Caller struct {
Client *Client
}
// Constructor for Caller
func NewCaller(client *Client) *Caller {
return &Caller{Client: client}
}
// Default Constructor for Caller
func DefaultCaller() *Caller {
return NewCaller(DefaultClient())
}
// 获取登录的uuid
func (c *Caller) GetLoginUUID() (string, error) {
resp := NewReturnResponse(c.Client.GetLoginUUID())
if resp.Err() != nil {
@ -28,13 +32,16 @@ func (c *Caller) GetLoginUUID() (string, error) {
if err != nil {
return "", err
}
// 正则匹配uuid字符串
results := uuidRegexp.FindSubmatch(data)
if len(results) != 2 {
// 如果没有匹配到,可能微信的接口做了修改或者当前机器的ip被加入了黑名单
return "", errors.New("uuid does not match")
}
return string(results[1]), nil
}
// 检查是否登录成功
func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) {
resp := NewReturnResponse(c.Client.CheckLogin(uuid))
if resp.Err() != nil {
@ -45,6 +52,8 @@ func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) {
if err != nil {
return nil, err
}
// 正则匹配检测的code
// 具体code参考
results := statusCodeRegexp.FindSubmatch(data)
if len(results) != 2 {
return nil, nil
@ -53,7 +62,9 @@ func (c *Caller) CheckLogin(uuid string) (*CheckLoginResponse, error) {
return &CheckLoginResponse{Code: code, Raw: data}, nil
}
// 获取登录信息
func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
// 从响应体里面获取需要跳转的url
results := redirectUriRegexp.FindSubmatch(body)
if len(results) != 2 {
return nil, errors.New("redirect url does not match")
@ -65,12 +76,14 @@ func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
}
defer resp.Body.Close()
var loginInfo LoginInfo
// xml结构体序列化储存
if err := resp.ScanXML(&loginInfo); err != nil {
return nil, err
}
return &loginInfo, nil
}
// 获取初始化信息
func (c *Caller) WebInit(request BaseRequest) (*WebInitResponse, error) {
resp := NewReturnResponse(c.Client.WebInit(request))
if resp.Err() != nil {
@ -84,6 +97,7 @@ func (c *Caller) WebInit(request BaseRequest) (*WebInitResponse, error) {
return &webInitResponse, nil
}
// 通知手机已登录
func (c *Caller) WebWxStatusNotify(request BaseRequest, response WebInitResponse, info LoginInfo) error {
resp := NewReturnResponse(c.Client.WebWxStatusNotify(request, response, info))
if resp.Err() != nil {
@ -100,6 +114,7 @@ func (c *Caller) WebWxStatusNotify(request BaseRequest, response WebInitResponse
return nil
}
// 异步获取是否有新的消息
func (c *Caller) SyncCheck(info LoginInfo, response WebInitResponse) (*SyncCheckResponse, error) {
resp := NewReturnResponse(c.Client.SyncCheck(info, response))
if resp.Err() != nil {
@ -107,7 +122,6 @@ func (c *Caller) SyncCheck(info LoginInfo, response WebInitResponse) (*SyncCheck
}
defer resp.Body.Close()
data, err := resp.ReadAll()
fmt.Println(string(data))
if err != nil {
return nil, err
}
@ -120,6 +134,7 @@ func (c *Caller) SyncCheck(info LoginInfo, response WebInitResponse) (*SyncCheck
return syncCheckResponse, nil
}
// 获取所有的联系人
func (c *Caller) WebWxGetContact(info LoginInfo) (Members, error) {
resp := NewReturnResponse(c.Client.WebWxGetContact(info))
if resp.Err() != nil {
@ -136,6 +151,8 @@ func (c *Caller) WebWxGetContact(info LoginInfo) (Members, error) {
return item.MemberList, nil
}
// 获取联系人的详情
// 注: Members参数的长度不要大于50
func (c *Caller) WebWxBatchGetContact(members Members, request BaseRequest) (Members, error) {
resp := NewReturnResponse(c.Client.WebWxBatchGetContact(members, request))
if resp.Err() != nil {
@ -152,6 +169,7 @@ func (c *Caller) WebWxBatchGetContact(members Members, request BaseRequest) (Mem
return item.ContactList, nil
}
// 获取新的消息接口
func (c *Caller) WebWxSync(request BaseRequest, response WebInitResponse, info LoginInfo) (*WebWxSyncResponse, error) {
resp := NewReturnResponse(c.Client.WebWxSync(request, response, info))
if resp.Err() != nil {
@ -165,18 +183,23 @@ func (c *Caller) WebWxSync(request BaseRequest, response WebInitResponse, info L
return &webWxSyncResponse, nil
}
// 发送消息接口
func (c *Caller) WebWxSendMsg(msg *SendMessage, info LoginInfo, request BaseRequest) error {
resp := NewReturnResponse(c.Client.WebWxSendMsg(msg, info, request))
return parseBaseResponseError(resp)
}
// 修改用户备注接口
func (c *Caller) WebWxOplog(request BaseRequest, remarkName, toUserName string) error {
resp := NewReturnResponse(c.Client.WebWxOplog(request, remarkName, toUserName))
return parseBaseResponseError(resp)
}
// 发送图片消息接口
func (c *Caller) WebWxSendImageMsg(file *os.File, request BaseRequest, info LoginInfo, fromUserName, toUserName string) error {
// 首先尝试上传图片
resp := NewReturnResponse(c.Client.WebWxUploadMedia(file, request, info, fromUserName, toUserName, "image/jpeg", "pic"))
// 无错误上传成功之后获取请求结果,判断结果是否正常
if resp.Err() != nil {
return resp.Err()
}
@ -191,11 +214,14 @@ func (c *Caller) WebWxSendImageMsg(file *os.File, request BaseRequest, info Logi
if !item.BaseResponse.Ok() {
return item.BaseResponse
}
// 构造新的图片类型的信息
msg := NewMediaSendMessage(ImageMessage, fromUserName, toUserName, item.MediaId)
// 发送图片信息
resp = NewReturnResponse(c.Client.WebWxSendMsgImg(msg, request, info))
return parseBaseResponseError(resp)
}
// 处理响应返回的结果是否正常
func parseBaseResponseError(resp *ReturnResponse) error {
if resp.Err() != nil {
return resp.Err()

View File

@ -16,14 +16,17 @@ import (
"time"
)
type Client struct {
*http.Client
}
// http请求客户端
// 客户端需要维持Session会话
// 并且客户端不允许跳转
type Client struct{ *http.Client }
func NewClient(client *http.Client) *Client {
return &Client{Client: client}
}
// 自动存储cookie
// 设置客户端不自动跳转
func DefaultClient() *Client {
jar, _ := cookiejar.New(nil)
client := &http.Client{
@ -35,6 +38,7 @@ func DefaultClient() *Client {
return NewClient(client)
}
// 获取登录的uuid
func (c *Client) GetLoginUUID() (*http.Response, error) {
path, _ := url.Parse(jsLoginUrl)
params := url.Values{}
@ -47,11 +51,13 @@ func (c *Client) GetLoginUUID() (*http.Response, error) {
return c.Get(path.String())
}
// 获取登录的二维吗
func (c *Client) GetLoginQrcode(uuid string) (*http.Response, error) {
path := qrcodeUrl + uuid
return c.Get(path)
}
// 检查是否登录
func (c *Client) CheckLogin(uuid string) (*http.Response, error) {
path, _ := url.Parse(loginUrl)
now := time.Now().Unix()
@ -65,10 +71,12 @@ func (c *Client) CheckLogin(uuid string) (*http.Response, error) {
return c.Get(path.String())
}
// 请求获取LoginInfo
func (c *Client) GetLoginInfo(path string) (*http.Response, error) {
return c.Get(path)
}
// 请求获取初始化信息
func (c *Client) WebInit(request BaseRequest) (*http.Response, error) {
path, _ := url.Parse(webWxInitUrl)
params := url.Values{}
@ -82,6 +90,7 @@ func (c *Client) WebInit(request BaseRequest) (*http.Response, error) {
return c.Post(path.String(), jsonContentType, body)
}
// 通知手机已登录
func (c *Client) WebWxStatusNotify(request BaseRequest, response WebInitResponse, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxStatusNotifyUrl)
params := url.Values{}
@ -102,6 +111,7 @@ func (c *Client) WebWxStatusNotify(request BaseRequest, response WebInitResponse
return c.Do(req)
}
// 异步检查是否有新的消息返回
func (c *Client) SyncCheck(info LoginInfo, response WebInitResponse) (*http.Response, error) {
path, _ := url.Parse(syncCheckUrl)
params := url.Values{}
@ -124,6 +134,7 @@ func (c *Client) SyncCheck(info LoginInfo, response WebInitResponse) (*http.Resp
return c.Do(req)
}
// 获取联系人信息
func (c *Client) WebWxGetContact(info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxGetContactUrl)
params := url.Values{}
@ -134,6 +145,7 @@ func (c *Client) WebWxGetContact(info LoginInfo) (*http.Response, error) {
return c.Get(path.String())
}
// 获取联系人详情
func (c *Client) WebWxBatchGetContact(members Members, request BaseRequest) (*http.Response, error) {
path, _ := url.Parse(webWxBatchGetContactUrl)
params := url.Values{}
@ -152,6 +164,7 @@ func (c *Client) WebWxBatchGetContact(members Members, request BaseRequest) (*ht
return c.Do(req)
}
// 获取消息接口
func (c *Client) WebWxSync(request BaseRequest, response WebInitResponse, info LoginInfo) (*http.Response, error) {
path, _ := url.Parse(webWxSyncUrl)
params := url.Values{}
@ -171,6 +184,7 @@ func (c *Client) WebWxSync(request BaseRequest, response WebInitResponse, info L
return c.Do(req)
}
// 发送消息
func (c *Client) sendMessage(request BaseRequest, url string, msg *SendMessage) (*http.Response, error) {
content := map[string]interface{}{
"BaseRequest": request,
@ -183,6 +197,7 @@ func (c *Client) sendMessage(request BaseRequest, url string, msg *SendMessage)
return c.Do(req)
}
// 发送文本消息
func (c *Client) WebWxSendMsg(msg *SendMessage, info LoginInfo, request BaseRequest) (*http.Response, error) {
msg.Type = TextMessage
path, _ := url.Parse(webWxSendMsgUrl)
@ -193,11 +208,13 @@ func (c *Client) WebWxSendMsg(msg *SendMessage, info LoginInfo, request BaseRequ
return c.sendMessage(request, path.String(), msg)
}
// 获取用户的头像
func (c *Client) WebWxGetHeadImg(headImageUrl string) (*http.Response, error) {
path := baseUrl + headImageUrl
return c.Get(path)
}
// 上传文件
func (c *Client) WebWxUploadMedia(file *os.File, request BaseRequest, info LoginInfo, forUserName, toUserName, contentType, mediaType string) (*http.Response, error) {
path, _ := url.Parse(webWxUpLoadMediaUrl)
params := url.Values{}
@ -261,6 +278,9 @@ func (c *Client) WebWxUploadMedia(file *os.File, request BaseRequest, info Login
return c.Post(path.String(), ct, body)
}
// 发送图片
// 这个接口依赖上传文件的接口
// 发送的图片必须是已经成功上传的图片
func (c *Client) WebWxSendMsgImg(msg *SendMessage, request BaseRequest, info LoginInfo) (*http.Response, error) {
msg.Type = ImageMessage
path, _ := url.Parse(webWxSendMsgImgUrl)
@ -273,6 +293,7 @@ func (c *Client) WebWxSendMsgImg(msg *SendMessage, request BaseRequest, info Log
return c.sendMessage(request, path.String(), msg)
}
// 发送文件信息
func (c *Client) WebWxSendAppMsg(msg *SendMessage, request BaseRequest) (*http.Response, error) {
msg.Type = AppMessage
path, _ := url.Parse(webWxSendAppMsgUrl)
@ -283,6 +304,7 @@ func (c *Client) WebWxSendAppMsg(msg *SendMessage, request BaseRequest) (*http.R
return c.sendMessage(request, path.String(), msg)
}
// 用户重命名接口
func (c *Client) WebWxOplog(request BaseRequest, remarkName, userName string, ) (*http.Response, error) {
path, _ := url.Parse(webWxOplogUrl)
params := url.Values{}
@ -300,6 +322,7 @@ func (c *Client) WebWxOplog(request BaseRequest, remarkName, userName string, )
return c.Do(req)
}
// 添加用户为好友接口
func (c *Client) WebWxVerifyUser(storage WechatStorage, info RecommendInfo, verifyContent string) (*http.Response, error) {
loginInfo := storage.GetLoginInfo()
path, _ := url.Parse(webWxVerifyUserUrl)

View File

@ -33,12 +33,14 @@ const (
jsonContentType = "application/json; charset=utf-8"
)
// 消息类型
const (
TextMessage = 1
ImageMessage = 3
AppMessage = 6
)
// 登录状态
const (
statusSuccess = "200"
statusScanned = "201"

View File

@ -7,19 +7,23 @@ import (
"net/http"
)
// Http请求的响应结构体封装
type ReturnResponse struct {
*http.Response
err error
}
// Constructor for ReturnResponse
func NewReturnResponse(response *http.Response, err error) *ReturnResponse {
return &ReturnResponse{Response: response, err: err}
}
// 获取当前请求的错误
func (r *ReturnResponse) Err() error {
return r.err
}
// json序列化
func (r *ReturnResponse) ScanJSON(v interface{}) error {
if data, err := r.ReadAll(); err != nil {
return err
@ -28,6 +32,7 @@ func (r *ReturnResponse) ScanJSON(v interface{}) error {
}
}
// xml序列化
func (r *ReturnResponse) ScanXML(v interface{}) error {
if data, err := r.ReadAll(); err != nil {
return err
@ -36,6 +41,7 @@ func (r *ReturnResponse) ScanXML(v interface{}) error {
}
}
// 读取请求体
func (r *ReturnResponse) ReadAll() ([]byte, error) {
if r.Err() != nil {
return nil, r.Err()

View File

@ -26,6 +26,7 @@ func (b BaseResponse) Ok() bool {
return b.Ret == 0
}
// 实现error接口
func (b BaseResponse) Error() string {
switch b.Ret {
case 0:

View File

@ -43,6 +43,7 @@ type Message struct {
senderInGroupUserName string
}
// 获取消息的发送者
func (m *Message) Sender() (*User, error) {
members, err := m.Bot.self.Members(true)
if err != nil {
@ -59,6 +60,7 @@ func (m *Message) Sender() (*User, error) {
return nil, errors.New("no such user found")
}
// 获取消息在群里面的发送者
func (m *Message) SenderInGroup() (*User, error) {
if !m.IsSendByGroup() {
return nil, errors.New("message is not from group")
@ -79,19 +81,22 @@ func (m *Message) SenderInGroup() (*User, error) {
return nil, errors.New("no such user found")
}
//
// 判断消息是否由自己发送
func (m *Message) IsSendBySelf() bool {
return m.FromUserName == m.Bot.self.User.UserName
}
// 判断消息是否由好友发送
func (m *Message) IsSendByFriend() bool {
return !m.IsSendByGroup() && strings.HasPrefix(m.FromUserName, "@")
}
// 判断消息是否由群组发送
func (m *Message) IsSendByGroup() bool {
return strings.HasPrefix(m.FromUserName, "@@")
}
// 回复消息
func (m *Message) Reply(msgType int, content, mediaId string) error {
msg := NewSendMessage(msgType, content, m.Bot.self.User.UserName, m.FromUserName, mediaId)
info := m.Bot.storage.GetLoginInfo()
@ -99,10 +104,12 @@ func (m *Message) Reply(msgType int, content, mediaId string) error {
return m.Bot.Caller.WebWxSendMsg(msg, info, request)
}
// 回复文本消息
func (m *Message) ReplyText(content string) error {
return m.Reply(TextMessage, content, "")
}
// 回复图片消息
func (m *Message) ReplyImage(file *os.File) error {
info := m.Bot.storage.GetLoginInfo()
request := m.Bot.storage.GetBaseRequest()

View File

@ -1,5 +1,7 @@
package openwechat
// WechatStorage
// 可以根据自己的情况来实现该接口
type WechatStorage interface {
SetLoginInfo(loginInfo LoginInfo)
SetBaseRequest(baseRequest BaseRequest)
@ -10,6 +12,7 @@ type WechatStorage interface {
}
// implement WechatStorage
// WechatStorage接口的实现
type SimpleWechatStorage struct {
loginInfo LoginInfo
baseRequest BaseRequest