From 6475fe6bfc44f5146f21a6d1b10a28563215d75f Mon Sep 17 00:00:00 2001 From: Ivy1996-encode Date: Thu, 18 Feb 2021 19:26:21 +0800 Subject: [PATCH] add code notes --- bot.go | 24 ++++++++++++++++++++++++ caller.go | 30 ++++++++++++++++++++++++++++-- client.go | 29 ++++++++++++++++++++++++++--- global.go | 2 ++ http.go | 6 ++++++ items.go | 1 + message.go | 9 ++++++++- stroage.go | 3 +++ 8 files changed, 98 insertions(+), 6 deletions(-) diff --git a/bot.go b/bot.go index 8408dd5..f6bde96 100644 --- a/bot.go +++ b/bot.go @@ -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 diff --git a/caller.go b/caller.go index 2508d09..0606acf 100644 --- a/caller.go +++ b/caller.go @@ -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() diff --git a/client.go b/client.go index 24eb42e..5decddd 100644 --- a/client.go +++ b/client.go @@ -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) diff --git a/global.go b/global.go index ee6b5ef..5140599 100644 --- a/global.go +++ b/global.go @@ -33,12 +33,14 @@ const ( jsonContentType = "application/json; charset=utf-8" ) +// 消息类型 const ( TextMessage = 1 ImageMessage = 3 AppMessage = 6 ) +// 登录状态 const ( statusSuccess = "200" statusScanned = "201" diff --git a/http.go b/http.go index 5cef057..f90ed78 100644 --- a/http.go +++ b/http.go @@ -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() diff --git a/items.go b/items.go index 8a5fbeb..0e80981 100644 --- a/items.go +++ b/items.go @@ -26,6 +26,7 @@ func (b BaseResponse) Ok() bool { return b.Ret == 0 } +// 实现error接口 func (b BaseResponse) Error() string { switch b.Ret { case 0: diff --git a/message.go b/message.go index e087919..bff49aa 100644 --- a/message.go +++ b/message.go @@ -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() diff --git a/stroage.go b/stroage.go index bddcba1..3a9d8e0 100644 --- a/stroage.go +++ b/stroage.go @@ -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