From d84fbe16818e18cd5ccfde608b70415e5218a072 Mon Sep 17 00:00:00 2001 From: eatMoreApple <15055461510@163.com> Date: Tue, 27 Apr 2021 11:12:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=B9=E8=B1=A1=E7=9A=84?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E7=9A=84=E6=8E=92=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.go | 322 ++++++++++++++++++++++++++--------------------------- items.go | 220 ++++++++++++++++++------------------ message.go | 63 ++++++----- 3 files changed, 302 insertions(+), 303 deletions(-) diff --git a/bot.go b/bot.go index 377dd82..dc60fe4 100644 --- a/bot.go +++ b/bot.go @@ -1,223 +1,223 @@ package openwechat import ( - "errors" - "fmt" + "errors" + "fmt" ) type Bot struct { - Caller *Caller - self *Self - storage *Storage - ScanCallBack func(body []byte) - LoginCallBack func(body []byte) - UUIDCallback func(uuid string) - MessageHandler func(msg *Message) - err error - exit chan bool + ScanCallBack func(body []byte) + LoginCallBack func(body []byte) + UUIDCallback func(uuid string) + MessageHandler func(msg *Message) + err error + exit chan bool + Caller *Caller + self *Self + storage *Storage } // 判断当前用户是否正常在线 func (b *Bot) Alive() bool { - if b.self == nil { - return false - } - select { - case <-b.exit: - return false - default: - return true - } + if b.self == nil { + return false + } + select { + case <-b.exit: + return false + default: + return true + } } // 获取当前的用户 func (b *Bot) GetCurrentUser() (*Self, error) { - if b.self == nil { - return nil, errors.New("user not login") - } - return b.self, nil + if b.self == nil { + return nil, errors.New("user not login") + } + return b.self, nil } // 用户登录 // 该方法会一直阻塞,直到用户扫码登录,或者二维码过期 func (b *Bot) Login() error { - uuid, err := b.Caller.GetLoginUUID() - if err != nil { - return err - } - if b.UUIDCallback != nil { - b.UUIDCallback(uuid) - } - for { - resp, err := b.Caller.CheckLogin(uuid) - if err != nil { - return err - } - switch resp.Code { - case statusSuccess: - return b.login(resp.Raw) - case statusScanned: - if b.ScanCallBack != nil { - b.ScanCallBack(resp.Raw) - } - case statusTimeout: - return errors.New("login time out") - case statusWait: - continue - } - } + uuid, err := b.Caller.GetLoginUUID() + if err != nil { + return err + } + if b.UUIDCallback != nil { + b.UUIDCallback(uuid) + } + for { + resp, err := b.Caller.CheckLogin(uuid) + if err != nil { + return err + } + switch resp.Code { + case statusSuccess: + return b.login(resp.Raw) + case statusScanned: + if b.ScanCallBack != nil { + b.ScanCallBack(resp.Raw) + } + case statusTimeout: + return errors.New("login time out") + case statusWait: + continue + } + } } func (b *Bot) Logout() error { - if b.Alive() { - info := b.storage.LoginInfo - if err := b.Caller.Logout(info); err != nil { - return err - } - b.stopAsyncCALL(errors.New("logout")) - return nil - } - return errors.New("user not login") + if b.Alive() { + info := b.storage.LoginInfo + if err := b.Caller.Logout(info); err != nil { + return err + } + b.stopAsyncCALL(errors.New("logout")) + return nil + } + return errors.New("user not login") } // 登录逻辑 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 - } + // 判断是否有登录回调,如果有执行它 + if b.LoginCallBack != nil { + b.LoginCallBack(data) + } + // 获取登录的一些基本的信息 + info, err := b.Caller.GetLoginInfo(data) + if err != nil { + return err + } - // 将LoginInfo存到storage里面 - b.storage.LoginInfo = info + // 将LoginInfo存到storage里面 + b.storage.LoginInfo = info - // 构建BaseRequest - request := &BaseRequest{ - Uin: info.WxUin, - Sid: info.WxSid, - Skey: info.SKey, - DeviceID: GetRandomDeviceId(), - } + // 构建BaseRequest + request := &BaseRequest{ + Uin: info.WxUin, + Sid: info.WxSid, + Skey: info.SKey, + DeviceID: GetRandomDeviceId(), + } - // 将BaseRequest存到storage里面方便后续调用 - b.storage.Request = request - // 获取初始化的用户信息和一些必要的参数 - resp, err := b.Caller.WebInit(request) - if err != nil { - return err - } - // 设置当前的用户 - b.self = &Self{Bot: b, User: &resp.User} - b.self.Self = b.self - b.storage.Response = resp + // 将BaseRequest存到storage里面方便后续调用 + b.storage.Request = request + // 获取初始化的用户信息和一些必要的参数 + resp, err := b.Caller.WebInit(request) + if err != nil { + return err + } + // 设置当前的用户 + b.self = &Self{Bot: b, User: &resp.User} + b.self.Self = b.self + b.storage.Response = resp - // 通知手机客户端已经登录 - if err = b.Caller.WebWxStatusNotify(request, resp, info); err != nil { - return err - } - // 开启协程,轮训获取是否有新的消息返回 - go func() { - b.stopAsyncCALL(b.asyncCall()) - }() - return nil + // 通知手机客户端已经登录 + 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 - resp *SyncCheckResponse - ) - for b.Alive() { - info := b.storage.LoginInfo - response := b.storage.Response - resp, err = b.Caller.SyncCheck(info, response) - 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 - } - } - } - return err + var ( + err error + resp *SyncCheckResponse + ) + for b.Alive() { + info := b.storage.LoginInfo + response := b.storage.Response + resp, err = b.Caller.SyncCheck(info, response) + 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 + } + } + } + return err } func (b *Bot) stopAsyncCALL(err error) { - b.exit <- true - b.err = err - b.self = nil + b.exit <- true + b.err = err + b.self = nil } // 获取新的消息 func (b *Bot) getMessage() error { - resp, err := b.Caller.WebWxSync(b.storage.Request, b.storage.Response, b.storage.LoginInfo) - if err != nil { - return err - } - // 更新SyncKey并且重新存入storage - b.storage.Response.SyncKey = resp.SyncKey - // 遍历所有的新的消息,依次处理 - for _, message := range resp.AddMsgList { - // 根据不同的消息类型来进行处理,方便后续统一调用 - message.init(b) - // 调用自定义的处理方法 - if handler := b.MessageHandler; handler != nil { - handler(message) - } - } - return nil + resp, err := b.Caller.WebWxSync(b.storage.Request, b.storage.Response, b.storage.LoginInfo) + if err != nil { + return err + } + // 更新SyncKey并且重新存入storage + b.storage.Response.SyncKey = resp.SyncKey + // 遍历所有的新的消息,依次处理 + for _, message := range resp.AddMsgList { + // 根据不同的消息类型来进行处理,方便后续统一调用 + message.init(b) + // 调用自定义的处理方法 + if handler := b.MessageHandler; handler != nil { + handler(message) + } + } + return nil } // 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞 func (b *Bot) Block() error { - if b.self == nil { - return errors.New("`Block` must be called after user login") - } - if _, closed := <-b.exit; !closed { - return errors.New("can not call `Block` after user logout") - } - close(b.exit) - return nil + if b.self == nil { + return errors.New("`Block` must be called after user login") + } + if _, closed := <-b.exit; !closed { + return errors.New("can not call `Block` after user logout") + } + close(b.exit) + return nil } func NewBot(caller *Caller) *Bot { - return &Bot{Caller: caller, storage: &Storage{}, exit: make(chan bool)} + return &Bot{Caller: caller, storage: &Storage{}, exit: make(chan bool)} } func DefaultBot(modes ...mode) *Bot { - var m mode - if len(modes) == 0 { - m = Normal - } else { - m = modes[0] - } - urlManager := GetUrlManagerByMode(m) - return NewBot(DefaultCaller(urlManager)) + var m mode + if len(modes) == 0 { + m = Normal + } else { + m = modes[0] + } + urlManager := GetUrlManagerByMode(m) + return NewBot(DefaultCaller(urlManager)) } type Storage struct { - LoginInfo *LoginInfo - Request *BaseRequest - Response *WebInitResponse + LoginInfo *LoginInfo + Request *BaseRequest + Response *WebInitResponse } func GetQrcodeUrl(uuid string) string { - return qrcodeUrl + uuid + return qrcodeUrl + uuid } func PrintlnQrcodeUrl(uuid string) { - println("访问下面网址扫描二维码登录") - println(GetQrcodeUrl(uuid)) + println("访问下面网址扫描二维码登录") + println(GetQrcodeUrl(uuid)) } diff --git a/items.go b/items.go index ccc4a49..83fb99b 100644 --- a/items.go +++ b/items.go @@ -8,173 +8,173 @@ import "fmt" // 登录信息 type LoginInfo struct { - Ret int `xml:"ret"` - Message string `xml:"message"` - SKey string `xml:"skey"` - WxSid string `xml:"wxsid"` - WxUin int `xml:"wxuin"` - PassTicket string `xml:"pass_ticket"` - IsGrayScale int `xml:"isgrayscale"` + Ret int `xml:"ret"` + WxUin int `xml:"wxuin"` + IsGrayScale int `xml:"isgrayscale"` + Message string `xml:"message"` + SKey string `xml:"skey"` + WxSid string `xml:"wxsid"` + PassTicket string `xml:"pass_ticket"` } // 初始的请求信息 // 几乎所有的请求都要携带该参数 type BaseRequest struct { - Uin int - Sid, Skey, DeviceID string + Uin int + Sid, Skey, DeviceID string } // 大部分返回对象都携带该信息 type BaseResponse struct { - ErrMsg string - Ret int + Ret int + ErrMsg string } func (b BaseResponse) Ok() bool { - return b.Ret == 0 + return b.Ret == 0 } func (b BaseResponse) Error() string { - switch b.Ret { - case 0: - return "" - case 1: - return "param error" - case -14: - return "ticket error" - case 1100: - return "not login warn" - case 1101: - return "not login check" - case 1102: - return "cookie invalid error" - case 1203: - return "login env error" - case 1205: - return "opt too often" - default: - return fmt.Sprintf("base response ret code %d", b.Ret) - } + switch b.Ret { + case 0: + return "" + case 1: + return "param error" + case -14: + return "ticket error" + case 1100: + return "not login warn" + case 1101: + return "not login check" + case 1102: + return "cookie invalid error" + case 1203: + return "login env error" + case 1205: + return "opt too often" + default: + return fmt.Sprintf("base response ret code %d", b.Ret) + } } type SyncKey struct { - Count int - List []struct{ Key, Val int64 } + Count int + List []struct{ Key, Val int64 } } // 初始化的相应信息 type WebInitResponse struct { - BaseResponse BaseResponse - Count int - ChatSet string - SKey string - SyncKey SyncKey - User User - ClientVersion int - SystemTime int64 - GrayScale int - InviteStartCount int - MPSubscribeMsgCount int - MPSubscribeMsgList []MPSubscribeMsg - ClickReportInterval int - ContactList []User + Count int + ClientVersion int + GrayScale int + InviteStartCount int + MPSubscribeMsgCount int + ClickReportInterval int + SystemTime int64 + ChatSet string + SKey string + BaseResponse BaseResponse + SyncKey SyncKey + User User + MPSubscribeMsgList []MPSubscribeMsg + ContactList []User } // 公众号的订阅信息 type MPSubscribeMsg struct { - UserName string - Time int64 - NickName string - MPArticleCount int - MPArticleList []struct { - Title string - Cover string - Digest string - Url string - } + MPArticleCount int + Time int64 + UserName string + NickName string + MPArticleList []struct { + Title string + Cover string + Digest string + Url string + } } type UserDetailItem struct { - UserName string - EncryChatRoomId string + UserName string + EncryChatRoomId string } type UserDetailItemList []UserDetailItem func NewUserDetailItemList(members Members) UserDetailItemList { - var list UserDetailItemList - for _, member := range members { - item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId} - list = append(list, item) - } - return list + var list UserDetailItemList + for _, member := range members { + item := UserDetailItem{UserName: member.UserName, EncryChatRoomId: member.EncryChatRoomId} + list = append(list, item) + } + return list } type SyncCheckResponse struct { - RetCode string - Selector string + RetCode string + Selector string } func (s *SyncCheckResponse) Success() bool { - return s.RetCode == "0" + return s.RetCode == "0" } func (s *SyncCheckResponse) NorMal() bool { - return s.Success() && s.Selector == "0" + return s.Success() && s.Selector == "0" } // 实现error接口 func (s *SyncCheckResponse) Error() string { - switch s.RetCode { - case "0": - return "" - case "1": - return "param error" - case "-14": - return "ticker error" - case "1100": - return "not login warn" - case "1101": - return "not login check" - case "1102": - return "cookie invalid error" - case "1203": - return "login env error" - case "1205": - return "opt too often" - default: - return fmt.Sprintf("sync check response error code %s", s.RetCode) - } + switch s.RetCode { + case "0": + return "" + case "1": + return "param error" + case "-14": + return "ticker error" + case "1100": + return "not login warn" + case "1101": + return "not login check" + case "1102": + return "cookie invalid error" + case "1203": + return "login env error" + case "1205": + return "opt too often" + default: + return fmt.Sprintf("sync check response error code %s", s.RetCode) + } } type WebWxSyncResponse struct { - AddMsgCount int - AddMsgList []*Message - BaseResponse BaseResponse - ContinueFlag int - DelContactCount int - ModChatRoomMemberCount int - ModChatRoomMemberList Members - ModContactCount int - Skey string - SyncCheckKey SyncKey - SyncKey SyncKey + AddMsgCount int + ContinueFlag int + DelContactCount int + ModChatRoomMemberCount int + ModContactCount int + Skey string + SyncCheckKey SyncKey + SyncKey SyncKey + BaseResponse BaseResponse + ModChatRoomMemberList Members + AddMsgList []*Message } type WebWxContactResponse struct { - BaseResponse BaseResponse - MemberCount int - MemberList []*User - Seq int + MemberCount int + Seq int + BaseResponse BaseResponse + MemberList []*User } type WebWxBatchContactResponse struct { - BaseResponse BaseResponse - ContactList []*User - Count int + Count int + BaseResponse BaseResponse + ContactList []*User } type CheckLoginResponse struct { - Code string - Raw []byte + Code string + Raw []byte } diff --git a/message.go b/message.go index ad1caeb..72b7e79 100644 --- a/message.go +++ b/message.go @@ -13,44 +13,43 @@ import ( ) type Message struct { + IsAt bool AppInfo struct { - AppID string Type int + AppID string } - AppMsgType int - Content string - CreateTime int64 - EncryFileName string - FileName string - FileSize string - ForwardFlag int - FromUserName string - HasProductId int - ImgHeight int - ImgStatus int - ImgWidth int - MediaId string - MsgId string - MsgType int - NewMsgId int64 - OriContent string - PlayLength int64 - RecommendInfo RecommendInfo - Status int - StatusNotifyCode int - StatusNotifyUserName string - SubMsgType int - Ticket string - ToUserName string - Url string - VoiceLength int - - IsAt bool - Bot *Bot + AppMsgType int + HasProductId int + ImgHeight int + ImgStatus int + ImgWidth int + ForwardFlag int + MsgType int + Status int + StatusNotifyCode int + SubMsgType int + VoiceLength int + CreateTime int64 + NewMsgId int64 + PlayLength int64 + MediaId string + MsgId string + EncryFileName string + FileName string + FileSize string + Content string + FromUserName string + OriContent string + StatusNotifyUserName string + Ticket string + ToUserName string + Url string senderInGroupUserName string + RecommendInfo RecommendInfo + Bot *Bot mu sync.RWMutex - item map[string]interface{} Context context.Context + item map[string]interface{} } // 获取消息的发送者