提高bot的稳定性

This commit is contained in:
eatmoreapple 2021-11-23 17:31:32 +08:00
parent 2f52315f21
commit 396a4f2343

103
bot.go
View File

@ -12,22 +12,22 @@ import (
) )
type Bot struct { type Bot struct {
ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像 ScanCallBack func(body []byte) // 扫码回调,可获取扫码用户的头像
LoginCallBack func(body []byte) // 登陆回调 LoginCallBack func(body []byte) // 登陆回调
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
GetMessageErrorHandler func(err error) // 获取消息发生错误的handle MessageErrorHandler func(err error) bool // 获取消息发生错误的handle, 返回true则尝试继续监听
isHot bool // 是否为热登录模式 isHot bool // 是否为热登录模式
once sync.Once once sync.Once
err error err error
context context.Context context context.Context
cancel context.CancelFunc cancel context.CancelFunc
Caller *Caller Caller *Caller
self *Self self *Self
Storage *Storage Storage *Storage
HotReloadStorage HotReloadStorage HotReloadStorage HotReloadStorage
} }
// Alive 判断当前用户是否正常在线 // Alive 判断当前用户是否正常在线
@ -154,7 +154,7 @@ func (b *Bot) Logout() error {
if err := b.Caller.Logout(info); err != nil { if err := b.Caller.Logout(info); err != nil {
return err return err
} }
b.stopAsyncCALL(errors.New("logout")) b.stopSyncCheck(errors.New("logout"))
return nil return nil
} }
return errors.New("user not login") return errors.New("user not login")
@ -214,11 +214,18 @@ func (b *Bot) WebInit() error {
// FIX: 当bot在线的情况下执行热登录,会开启多次事件监听 // FIX: 当bot在线的情况下执行热登录,会开启多次事件监听
go b.once.Do(func() { go b.once.Do(func() {
if b.GetMessageErrorHandler == nil { if b.MessageErrorHandler == nil {
b.GetMessageErrorHandler = b.stopAsyncCALL b.MessageErrorHandler = b.stopSyncCheck
} }
if err = b.asyncCall(); err != nil { for {
b.GetMessageErrorHandler(err) err := b.syncCheck()
if err == nil {
continue
}
// 判断是否继续, 如果不继续则退出
if goon := b.MessageErrorHandler(err); !goon {
break
}
} }
}) })
return nil return nil
@ -226,7 +233,7 @@ func (b *Bot) WebInit() error {
// 轮询请求 // 轮询请求
// 根据状态码判断是否有新的请求 // 根据状态码判断是否有新的请求
func (b *Bot) asyncCall() error { func (b *Bot) syncCheck() error {
var ( var (
err error err error
resp *SyncCheckResponse resp *SyncCheckResponse
@ -247,38 +254,47 @@ func (b *Bot) asyncCall() error {
} }
// 如果Selector不为0则获取消息 // 如果Selector不为0则获取消息
if !resp.NorMal() { if !resp.NorMal() {
if err = b.getNewWechatMessage(); err != nil { messages, err := b.syncMessage()
if err != nil {
return err return err
} }
if b.MessageHandler == nil {
continue
}
for _, message := range messages {
message.init(b)
// 默认同步调用
// 如果异步调用则需自行处理
// 如配合 openwechat.MessageMatchDispatcher 使用
b.MessageHandler(message)
}
} }
} }
return err return err
} }
// 当获取消息发生错误时, 默认的错误处理行为 // 当获取消息发生错误时, 默认的错误处理行为
func (b *Bot) stopAsyncCALL(err error) { func (b *Bot) stopSyncCheck(err error) bool {
if IsNetworkError(err) { if IsNetworkError(err) {
log.Println(err) log.Println(err)
return // 继续监听
return true
} }
b.err = err b.err = err
b.Exit() b.Exit()
log.Printf("exit with : %s", err.Error()) log.Printf("exit with : %s", err.Error())
return false
} }
// 获取新的消息 // 获取新的消息
func (b *Bot) getNewWechatMessage() error { func (b *Bot) syncMessage() ([]*Message, error) {
resp, err := b.Caller.WebWxSync(b.Storage.Request, b.Storage.Response, b.Storage.LoginInfo) resp, err := b.Caller.WebWxSync(b.Storage.Request, b.Storage.Response, b.Storage.LoginInfo)
if err != nil { if err != nil {
return err return nil, err
} }
// 更新SyncKey并且重新存入storage // 更新SyncKey并且重新存入storage
b.Storage.Response.SyncKey = resp.SyncKey b.Storage.Response.SyncKey = resp.SyncKey
// 避免单个消息处理函数阻塞,让其他的消息得不到处理 return resp.AddMsgList, nil
if b.MessageHandler != nil {
b.handleMessage(resp.AddMsgList)
}
return nil
} }
// Block 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞 // Block 当消息同步发生了错误或者用户主动在手机上退出,该方法会立即返回,否则会一直阻塞
@ -307,8 +323,8 @@ func (b *Bot) MessageOnSuccess(h func(msg *Message)) {
} }
// MessageOnError setter for Bot.GetMessageErrorHandler // MessageOnError setter for Bot.GetMessageErrorHandler
func (b *Bot) MessageOnError(h func(err error)) { func (b *Bot) MessageOnError(h func(err error) bool) {
b.GetMessageErrorHandler = h b.MessageErrorHandler = h
} }
// DumpHotReloadStorage 写入HotReloadStorage // DumpHotReloadStorage 写入HotReloadStorage
@ -349,29 +365,35 @@ func (b *Bot) OnLogout(f func(bot *Bot)) {
b.LogoutCallBack = f b.LogoutCallBack = f
} }
// NewBot Bot的构造方法需要自己传入Caller // NewBot Bot的构造方法
func NewBot() *Bot { func NewBot() *Bot {
caller := DefaultCaller() caller := DefaultCaller()
// 默认行为为桌面模式
caller.Client.SetMode(Desktop) caller.Client.SetMode(Desktop)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
return &Bot{Caller: caller, Storage: &Storage{}, context: ctx, cancel: cancel} return &Bot{Caller: caller, Storage: &Storage{}, context: ctx, cancel: cancel}
} }
// DefaultBot 默认的Bot的构造方法, // DefaultBot 默认的Bot的构造方法,
// mode不传入默认为openwechat.Normal,详情见mode // mode不传入默认为 openwechat.Desktop,详情见mode
// bot := openwechat.DefaultBot(openwechat.Desktop) // bot := openwechat.DefaultBot(openwechat.Desktop)
func DefaultBot(modes ...Mode) *Bot { func DefaultBot(modes ...Mode) *Bot {
bot := NewBot() bot := NewBot()
if len(modes) > 0 { if len(modes) > 0 {
bot.Caller.Client.SetMode(modes[0]) bot.Caller.Client.SetMode(modes[0])
} }
// 获取二维码回调
bot.UUIDCallback = PrintlnQrcodeUrl bot.UUIDCallback = PrintlnQrcodeUrl
// 扫码回调
bot.ScanCallBack = func(body []byte) { bot.ScanCallBack = func(body []byte) {
log.Println("扫码成功,请在手机上确认登录") log.Println("扫码成功,请在手机上确认登录")
} }
// 登录回调
bot.LoginCallBack = func(body []byte) { bot.LoginCallBack = func(body []byte) {
log.Println("登录成功") log.Println("登录成功")
} }
// 心跳回调函数
// 默认的行为打印SyncCheckResponse
bot.SyncCheckCallback = func(resp SyncCheckResponse) { bot.SyncCheckCallback = func(resp SyncCheckResponse) {
log.Printf("RetCode:%s Selector:%s", resp.RetCode, resp.Selector) log.Printf("RetCode:%s Selector:%s", resp.RetCode, resp.Selector)
} }
@ -413,15 +435,6 @@ func open(url string) error {
return exec.Command(cmd, args...).Start() return exec.Command(cmd, args...).Start()
} }
func (b *Bot) handleMessage(messageList []*Message) {
for _, message := range messageList {
// 根据不同的消息类型来进行处理,方便后续统一调用
message.init(b)
// 调用自定义的处理方法
b.MessageHandler(message)
}
}
// IsHot returns true if is hot login otherwise false // IsHot returns true if is hot login otherwise false
func (b *Bot) IsHot() bool { func (b *Bot) IsHot() bool {
return b.isHot return b.isHot