[perf]: 优化登录检查 (#203)

This commit is contained in:
多吃点苹果 2023-01-13 19:44:15 +08:00 committed by GitHub
parent 1eadbf8be6
commit 53478bad19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 42 deletions

23
bot.go
View File

@ -6,19 +6,20 @@ import (
"errors" "errors"
"io" "io"
"log" "log"
"net/url"
"os/exec" "os/exec"
"runtime" "runtime"
"sync" "sync"
) )
type Bot struct { type Bot struct {
ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像 ScanCallBack func(body CheckLoginResponse) // 扫码回调,可获取扫码用户的头像
LoginCallBack func(body []byte) // 登陆回调 LoginCallBack func(body CheckLoginResponse) // 登陆回调
LogoutCallBack func(bot *Bot) // 退出回调 LogoutCallBack func(bot *Bot) // 退出回调
UUIDCallback func(uuid string) // 获取UUID的回调函数 UUIDCallback func(uuid string) // 获取UUID的回调函数
SyncCheckCallback func(resp SyncCheckResponse) // 心跳回调 SyncCheckCallback func(resp SyncCheckResponse) // 心跳回调
MessageHandler MessageHandler // 获取消息成功的handle MessageHandler MessageHandler // 获取消息成功的handle
MessageErrorHandler func(err error) bool // 获取消息发生错误的handle, 返回true则尝试继续监听 MessageErrorHandler func(err error) bool // 获取消息发生错误的handle, 返回true则尝试继续监听
once sync.Once once sync.Once
err error err error
context context.Context context context.Context
@ -121,9 +122,9 @@ func (b *Bot) Logout() error {
} }
// HandleLogin 登录逻辑 // HandleLogin 登录逻辑
func (b *Bot) HandleLogin(data []byte) error { func (b *Bot) HandleLogin(path *url.URL) error {
// 获取登录的一些基本的信息 // 获取登录的一些基本的信息
info, err := b.Caller.GetLoginInfo(data) info, err := b.Caller.GetLoginInfo(path)
if err != nil { if err != nil {
return err return err
} }
@ -346,11 +347,11 @@ func DefaultBot(prepares ...BotPreparer) *Bot {
// 获取二维码回调 // 获取二维码回调
bot.UUIDCallback = PrintlnQrcodeUrl bot.UUIDCallback = PrintlnQrcodeUrl
// 扫码回调 // 扫码回调
bot.ScanCallBack = func(body []byte) { bot.ScanCallBack = func(_ CheckLoginResponse) {
log.Println("扫码成功,请在手机上确认登录") log.Println("扫码成功,请在手机上确认登录")
} }
// 登录回调 // 登录回调
bot.LoginCallBack = func(body []byte) { bot.LoginCallBack = func(_ CheckLoginResponse) {
log.Println("登录成功") log.Println("登录成功")
} }
// 心跳回调函数 // 心跳回调函数

View File

@ -225,8 +225,8 @@ type LoginChecker struct {
Bot *Bot Bot *Bot
Tip string Tip string
UUIDCallback func(uuid string) UUIDCallback func(uuid string)
LoginCallBack func(body []byte) LoginCallBack func(body CheckLoginResponse)
ScanCallBack func(body []byte) ScanCallBack func(body CheckLoginResponse)
} }
func (l *LoginChecker) CheckLogin() error { func (l *LoginChecker) CheckLogin() error {
@ -242,23 +242,31 @@ func (l *LoginChecker) CheckLogin() error {
if err != nil { if err != nil {
return err return err
} }
code, err := resp.Code()
if err != nil {
return err
}
if tip == "1" { if tip == "1" {
tip = "0" tip = "0"
} }
switch resp.Code { switch code {
case StatusSuccess: case StatusSuccess:
// 判断是否有登录回调,如果有执行它 // 判断是否有登录回调,如果有执行它
if err = l.Bot.HandleLogin(resp.Raw); err != nil { redirectURL, err := resp.RedirectURL()
if err != nil {
return err
}
if err = l.Bot.HandleLogin(redirectURL); err != nil {
return err return err
} }
if cb := l.LoginCallBack; cb != nil { if cb := l.LoginCallBack; cb != nil {
cb(resp.Raw) cb(resp)
} }
return nil return nil
case StatusScanned: case StatusScanned:
// 执行扫码回调 // 执行扫码回调
if cb := l.ScanCallBack; cb != nil { if cb := l.ScanCallBack; cb != nil {
cb(resp.Raw) cb(resp)
} }
case StatusTimeout: case StatusTimeout:
return ErrLoginTimeout return ErrLoginTimeout

View File

@ -50,7 +50,7 @@ func (c *Caller) GetLoginUUID() (string, error) {
} }
// CheckLogin 检查是否登录成功 // CheckLogin 检查是否登录成功
func (c *Caller) CheckLogin(uuid, tip string) (*CheckLoginResponse, error) { func (c *Caller) CheckLogin(uuid, tip string) (CheckLoginResponse, error) {
resp, err := c.Client.CheckLogin(uuid, tip) resp, err := c.Client.CheckLogin(uuid, tip)
if err != nil { if err != nil {
return nil, err return nil, err
@ -61,29 +61,13 @@ func (c *Caller) CheckLogin(uuid, tip string) (*CheckLoginResponse, error) {
if _, err := buffer.ReadFrom(resp.Body); err != nil { if _, err := buffer.ReadFrom(resp.Body); err != nil {
return nil, err return nil, err
} }
// 正则匹配检测的code return buffer.Bytes(), nil
// 具体code参考global.go
results := statusCodeRegexp.FindSubmatch(buffer.Bytes())
if len(results) != 2 {
return nil, errors.New("error status code match")
}
code := string(results[1])
return &CheckLoginResponse{Code: code, Raw: buffer.Bytes()}, nil
} }
// GetLoginInfo 获取登录信息 // GetLoginInfo 获取登录信息
func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) { func (c *Caller) GetLoginInfo(path *url.URL) (*LoginInfo, error) {
// 从响应体里面获取需要跳转的url // 从响应体里面获取需要跳转的url
results := redirectUriRegexp.FindSubmatch(body) resp, err := c.Client.GetLoginInfo(path)
if len(results) != 2 {
return nil, errors.New("redirect url does not match")
}
path, err := url.Parse(string(results[1]))
if err != nil {
return nil, err
}
c.Client.Domain = WechatDomain(path.Host)
resp, err := c.Client.GetLoginInfo(path.String())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -133,8 +133,9 @@ func (c *Client) CheckLogin(uuid, tip string) (*http.Response, error) {
} }
// GetLoginInfo 请求获取LoginInfo // GetLoginInfo 请求获取LoginInfo
func (c *Client) GetLoginInfo(path string) (*http.Response, error) { func (c *Client) GetLoginInfo(path *url.URL) (*http.Response, error) {
return c.mode.GetLoginInfo(c, path) c.Domain = WechatDomain(path.Host)
return c.mode.GetLoginInfo(c, path.String())
} }
// WebInit 请求获取初始化信息 // WebInit 请求获取初始化信息

View File

@ -7,6 +7,7 @@ import (
var ( var (
uuidRegexp = regexp.MustCompile(`uuid = "(.*?)";`) uuidRegexp = regexp.MustCompile(`uuid = "(.*?)";`)
statusCodeRegexp = regexp.MustCompile(`window.code=(\d+);`) statusCodeRegexp = regexp.MustCompile(`window.code=(\d+);`)
avatarRegexp = regexp.MustCompile(`window.userAvatar = '(.*)';`)
syncCheckRegexp = regexp.MustCompile(`window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}`) syncCheckRegexp = regexp.MustCompile(`window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}`)
redirectUriRegexp = regexp.MustCompile(`window.redirect_uri="(.*?)"`) redirectUriRegexp = regexp.MustCompile(`window.redirect_uri="(.*?)"`)
) )

View File

@ -2,6 +2,8 @@ package openwechat
import ( import (
"errors" "errors"
"fmt"
"net/url"
) )
/* /*
@ -117,9 +119,49 @@ type WebWxBatchContactResponse struct {
ContactList []*User ContactList []*User
} }
type CheckLoginResponse struct { // CheckLoginResponse 检查登录状态的响应body
Code string type CheckLoginResponse []byte
Raw []byte
// RedirectURL 重定向的URL
func (c CheckLoginResponse) RedirectURL() (*url.URL, error) {
code, err := c.Code()
if err != nil {
return nil, err
}
if code != StatusSuccess {
return nil, fmt.Errorf("expect status code %s, but got %s", StatusSuccess, code)
}
results := redirectUriRegexp.FindSubmatch(c)
if len(results) != 2 {
return nil, errors.New("redirect url does not match")
}
return url.Parse(string(results[1]))
}
// Code 获取当前的登录检查状态的代码
func (c CheckLoginResponse) Code() (string, error) {
results := statusCodeRegexp.FindSubmatch(c)
if len(results) != 2 {
return "", errors.New("error status code match")
}
code := string(results[1])
return code, nil
}
// Avatar 获取扫码后的用户头像, base64编码
func (c CheckLoginResponse) Avatar() (string, error) {
code, err := c.Code()
if err != nil {
return "", err
}
if code != StatusScanned {
return "", nil
}
results := avatarRegexp.FindSubmatch(c)
if len(results) != 2 {
return "", errors.New("avatar does not match")
}
return string(results[1]), nil
} }
type MessageResponse struct { type MessageResponse struct {