From 24a43987e3601d49651708a530e021aa68002ec0 Mon Sep 17 00:00:00 2001 From: eatMoreApple <15055461510@163.com> Date: Fri, 30 Apr 2021 14:50:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=86=97=E4=BD=99=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.go | 430 ++++++++++++++++++++++++++--------------------------- client.go | 2 - global.go | 2 + message.go | 2 +- 4 files changed, 218 insertions(+), 218 deletions(-) diff --git a/bot.go b/bot.go index 0f31c2f..efad5c3 100644 --- a/bot.go +++ b/bot.go @@ -1,37 +1,39 @@ package openwechat import ( - "errors" - "log" - "net/url" + "context" + "errors" + "log" + "net/url" ) type Bot struct { - ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像 - LoginCallBack func(body []byte) // 登陆回调 - UUIDCallback func(uuid string) // 获取UUID的回调函数 - MessageHandler func(msg *Message) // 获取消息成功的handle - GetMessageErrorHandler func(err error) // 获取消息发生错误的handle - isHot bool - err error - exit chan bool - Caller *Caller - self *Self - storage *Storage - hotReloadStorage HotReloadStorage + ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像 + LoginCallBack func(body []byte) // 登陆回调 + UUIDCallback func(uuid string) // 获取UUID的回调函数 + MessageHandler func(msg *Message) // 获取消息成功的handle + GetMessageErrorHandler func(err error) // 获取消息发生错误的handle + isHot bool + err error + context context.Context + cancel context.CancelFunc + Caller *Caller + self *Self + storage *Storage + hotReloadStorage HotReloadStorage } // 判断当前用户是否正常在线 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.context.Done(): + return false + default: + return true + } } // 获取当前的用户 @@ -41,10 +43,10 @@ func (b *Bot) Alive() bool { // } // fmt.Println(self.NickName) 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 } // 热登录,可实现重复登录, @@ -53,268 +55,266 @@ func (b *Bot) GetCurrentUser() (*Self, error) { // err := bot.HotLogin(storage, true) // fmt.Println(err) func (b *Bot) HotLogin(storage HotReloadStorage, retry ...bool) error { - b.isHot = true - b.hotReloadStorage = storage + b.isHot = true + b.hotReloadStorage = storage - var err error + var err error - // 如果load出错了,就执行正常登陆逻辑 - // 第一次没有数据load都会出错的 - if err = storage.Load(); err != nil { - return b.Login() - } + // 如果load出错了,就执行正常登陆逻辑 + // 第一次没有数据load都会出错的 + if err = storage.Load(); err != nil { + return b.Login() + } - if err = b.hotLoginInit(); err != nil { - return err - } + if err = b.hotLoginInit(); err != nil { + return err + } - // 如果webInit出错,则说明可能身份信息已经失效 - // 如果retry为True的话,则进行正常登陆 - if err = b.webInit(); err != nil { - if len(retry) > 0 { - if retry[0] { - return b.Login() - } - } - } - return err + // 如果webInit出错,则说明可能身份信息已经失效 + // 如果retry为True的话,则进行正常登陆 + if err = b.webInit(); err != nil { + if len(retry) > 0 { + if retry[0] { + return b.Login() + } + } + } + return err } // 热登陆初始化 func (b *Bot) hotLoginInit() error { - cookies := b.hotReloadStorage.GetCookie() - for u, ck := range cookies { - path, err := url.Parse(u) - if err != nil { - return err - } - b.Caller.Client.Jar.SetCookies(path, ck) - } - b.storage.LoginInfo = b.hotReloadStorage.GetLoginInfo() - b.storage.Request = b.hotReloadStorage.GetBaseRequest() - return nil + cookies := b.hotReloadStorage.GetCookie() + for u, ck := range cookies { + path, err := url.Parse(u) + if err != nil { + return err + } + b.Caller.Client.Jar.SetCookies(path, ck) + } + b.storage.LoginInfo = b.hotReloadStorage.GetLoginInfo() + b.storage.Request = b.hotReloadStorage.GetBaseRequest() + return 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: - // 判断是否有登录回调,如果有执行它 - if b.LoginCallBack != nil { - b.LoginCallBack(resp.Raw) - } - return b.handleLogin(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: + // 判断是否有登录回调,如果有执行它 + if b.LoginCallBack != nil { + b.LoginCallBack(resp.Raw) + } + return b.handleLogin(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) handleLogin(data []byte) error { - // 获取登录的一些基本的信息 - info, err := b.Caller.GetLoginInfo(data) - if err != nil { - return err - } - // 将LoginInfo存到storage里面 - b.storage.LoginInfo = info + // 获取登录的一些基本的信息 + info, err := b.Caller.GetLoginInfo(data) + if err != nil { + return err + } + // 将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 + // 将BaseRequest存到storage里面方便后续调用 + b.storage.Request = request - // 如果是热登陆,则将当前的重要信息写入hotReloadStorage - if b.isHot { - cookies := b.Caller.Client.GetCookieMap() - if err := b.hotReloadStorage.Dump(cookies, request, info); err != nil { - return err - } - } + // 如果是热登陆,则将当前的重要信息写入hotReloadStorage + if b.isHot { + cookies := b.Caller.Client.GetCookieMap() + if err := b.hotReloadStorage.Dump(cookies, request, info); err != nil { + return err + } + } - return b.webInit() + return b.webInit() } // 根据有效凭证获取和初始化用户信息 func (b *Bot) webInit() error { - req := b.storage.Request - info := b.storage.LoginInfo - // 获取初始化的用户信息和一些必要的参数 - resp, err := b.Caller.WebInit(req) - if err != nil { - return err - } - // 设置当前的用户 - b.self = &Self{Bot: b, User: &resp.User} - b.self.Self = b.self - b.storage.Response = resp + req := b.storage.Request + info := b.storage.LoginInfo + // 获取初始化的用户信息和一些必要的参数 + resp, err := b.Caller.WebInit(req) + 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(req, resp, info); err != nil { - return err - } - // 开启协程,轮训获取是否有新的消息返回 - go func() { - if b.GetMessageErrorHandler == nil { - b.GetMessageErrorHandler = b.stopAsyncCALL - } - if err := b.asyncCall(); err != nil { - b.GetMessageErrorHandler(err) - } - }() - return nil + // 通知手机客户端已经登录 + if err = b.Caller.WebWxStatusNotify(req, resp, info); err != nil { + return err + } + // 开启协程,轮训获取是否有新的消息返回 + go func() { + if b.GetMessageErrorHandler == nil { + b.GetMessageErrorHandler = b.stopAsyncCALL + } + if err := b.asyncCall(); err != nil { + b.GetMessageErrorHandler(err) + } + }() + return nil } // 轮训请求 // 根据状态码判断是否有新的请求 func (b *Bot) asyncCall() error { - var ( - err error - resp *SyncCheckResponse - ) - for b.Alive() { - // 长轮训检查是否有消息返回 - resp, err = b.Caller.SyncCheck(b.storage.LoginInfo, b.storage.Response) - if err != nil { - return err - } - // 如果不是正常的状态码返回,发生了错误,直接退出 - if !resp.Success() { - return resp - } - // 如果Selector不为0,则获取消息 - if !resp.NorMal() { - if err = b.getNewWechatMessage(); err != nil { - return err - } - } - } - return err + var ( + err error + resp *SyncCheckResponse + ) + for b.Alive() { + // 长轮训检查是否有消息返回 + resp, err = b.Caller.SyncCheck(b.storage.LoginInfo, b.storage.Response) + if err != nil { + return err + } + // 如果不是正常的状态码返回,发生了错误,直接退出 + if !resp.Success() { + return resp + } + // 如果Selector不为0,则获取消息 + if !resp.NorMal() { + if err = b.getNewWechatMessage(); err != nil { + return err + } + } + } + return err } // 当获取消息发生错误时, 默认的错误处理行为 func (b *Bot) stopAsyncCALL(err error) { - b.exit <- true - b.err = err - b.self = nil - log.Printf("exit with : %s", err.Error()) + b.cancel() + b.err = err + b.self = nil + log.Printf("exit with : %s", err.Error()) } // 获取新的消息 func (b *Bot) getNewWechatMessage() 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 _, notClose := <-b.exit; !notClose { - 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") + } + <-b.context.Done() + return nil } // 获取当前Bot崩溃的原因 func (b *Bot) CrashReason() error { - return b.err + return b.err } // setter for Bot.MessageHandler func (b *Bot) MessageOnSuccess(h func(msg *Message)) { - b.MessageHandler = h + b.MessageHandler = h } // setter for Bot.GetMessageErrorHandler func (b *Bot) MessageOnError(h func(err error)) { - b.GetMessageErrorHandler = h + b.GetMessageErrorHandler = h } // Bot的构造方法,需要自己传入Caller func NewBot(caller *Caller) *Bot { - return &Bot{Caller: caller, storage: &Storage{}, exit: make(chan bool)} + ctx, cancel := context.WithCancel(context.Background()) + return &Bot{Caller: caller, storage: &Storage{}, context: ctx, cancel: cancel} } // 默认的Bot的构造方法, // mode不传入默认为openwechat.Normal,详情见mode // bot := openwechat.DefaultBot(openwechat.Desktop) 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)) } // 通过uuid获取登录二维码的url 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/client.go b/client.go index 23d975a..ec1a799 100644 --- a/client.go +++ b/client.go @@ -230,8 +230,6 @@ func (c *Client) sendMessage(request *BaseRequest, url string, msg *SendMessage) body, _ := ToBuffer(content) req, _ := http.NewRequest(http.MethodPost, url, body) req.Header.Add("Content-Type", jsonContentType) - fmt.Println(6666) - fmt.Println(body.String()) return c.Do(req) } diff --git a/global.go b/global.go index ac7e7a7..8287fcc 100644 --- a/global.go +++ b/global.go @@ -124,3 +124,5 @@ var imageType = map[string]bool{ } var videoType = "mp4" + +const appMessageAppId = "wxeb7ec651dd0aefa9" diff --git a/message.go b/message.go index 37d5c80..b9b1045 100644 --- a/message.go +++ b/message.go @@ -495,7 +495,7 @@ func (f appmsg) XmlByte() ([]byte, error) { } func NewFileAppMessage(stat os.FileInfo, attachId string) *appmsg { - m := &appmsg{AppId: "wxeb7ec651dd0aefa9", Title: stat.Name()} + m := &appmsg{AppId: appMessageAppId, Title: stat.Name()} m.AppAttach.AttachId = attachId m.AppAttach.TotalLen = stat.Size() m.Type = 6