[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"
"io"
"log"
"net/url"
"os/exec"
"runtime"
"sync"
)
type Bot struct {
ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像
LoginCallBack func(body []byte) // 登陆回调
LogoutCallBack func(bot *Bot) // 退出回调
UUIDCallback func(uuid string) // 获取UUID的回调函数
SyncCheckCallback func(resp SyncCheckResponse) // 心跳回调
MessageHandler MessageHandler // 获取消息成功的handle
MessageErrorHandler func(err error) bool // 获取消息发生错误的handle, 返回true则尝试继续监听
ScanCallBack func(body CheckLoginResponse) // 扫码回调,可获取扫码用户的头像
LoginCallBack func(body CheckLoginResponse) // 登陆回调
LogoutCallBack func(bot *Bot) // 退出回调
UUIDCallback func(uuid string) // 获取UUID的回调函数
SyncCheckCallback func(resp SyncCheckResponse) // 心跳回调
MessageHandler MessageHandler // 获取消息成功的handle
MessageErrorHandler func(err error) bool // 获取消息发生错误的handle, 返回true则尝试继续监听
once sync.Once
err error
context context.Context
@ -121,9 +122,9 @@ func (b *Bot) Logout() error {
}
// 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 {
return err
}
@ -346,11 +347,11 @@ func DefaultBot(prepares ...BotPreparer) *Bot {
// 获取二维码回调
bot.UUIDCallback = PrintlnQrcodeUrl
// 扫码回调
bot.ScanCallBack = func(body []byte) {
bot.ScanCallBack = func(_ CheckLoginResponse) {
log.Println("扫码成功,请在手机上确认登录")
}
// 登录回调
bot.LoginCallBack = func(body []byte) {
bot.LoginCallBack = func(_ CheckLoginResponse) {
log.Println("登录成功")
}
// 心跳回调函数

View File

@ -225,8 +225,8 @@ type LoginChecker struct {
Bot *Bot
Tip string
UUIDCallback func(uuid string)
LoginCallBack func(body []byte)
ScanCallBack func(body []byte)
LoginCallBack func(body CheckLoginResponse)
ScanCallBack func(body CheckLoginResponse)
}
func (l *LoginChecker) CheckLogin() error {
@ -242,23 +242,31 @@ func (l *LoginChecker) CheckLogin() error {
if err != nil {
return err
}
code, err := resp.Code()
if err != nil {
return err
}
if tip == "1" {
tip = "0"
}
switch resp.Code {
switch code {
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
}
if cb := l.LoginCallBack; cb != nil {
cb(resp.Raw)
cb(resp)
}
return nil
case StatusScanned:
// 执行扫码回调
if cb := l.ScanCallBack; cb != nil {
cb(resp.Raw)
cb(resp)
}
case StatusTimeout:
return ErrLoginTimeout

View File

@ -50,7 +50,7 @@ func (c *Caller) GetLoginUUID() (string, error) {
}
// 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)
if err != nil {
return nil, err
@ -61,29 +61,13 @@ func (c *Caller) CheckLogin(uuid, tip string) (*CheckLoginResponse, error) {
if _, err := buffer.ReadFrom(resp.Body); err != nil {
return nil, err
}
// 正则匹配检测的code
// 具体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
return buffer.Bytes(), nil
}
// GetLoginInfo 获取登录信息
func (c *Caller) GetLoginInfo(body []byte) (*LoginInfo, error) {
func (c *Caller) GetLoginInfo(path *url.URL) (*LoginInfo, error) {
// 从响应体里面获取需要跳转的url
results := redirectUriRegexp.FindSubmatch(body)
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())
resp, err := c.Client.GetLoginInfo(path)
if err != nil {
return nil, err
}

View File

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

View File

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

View File

@ -2,6 +2,8 @@ package openwechat
import (
"errors"
"fmt"
"net/url"
)
/*
@ -117,9 +119,49 @@ type WebWxBatchContactResponse struct {
ContactList []*User
}
type CheckLoginResponse struct {
Code string
Raw []byte
// CheckLoginResponse 检查登录状态的响应body
type CheckLoginResponse []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 {