diff --git a/bot.go b/bot.go index 5adfec6..0940540 100644 --- a/bot.go +++ b/bot.go @@ -248,7 +248,7 @@ func (b *Bot) Block() error { if b.self == nil { return errors.New("`Block` must be called after user login") } - <-b.context.Done() + <-b.Context().Done() return nil } @@ -298,6 +298,11 @@ func (b *Bot) UUID() string { return b.uuid } +// Context returns current context of bot +func (b *Bot) Context() context.Context { + return b.context +} + func (b *Bot) reload() error { if b.hotReloadStorage == nil { return errors.New("hotReloadStorage is nil") diff --git a/bot_login.go b/bot_login.go index 5020e13..8dff482 100644 --- a/bot_login.go +++ b/bot_login.go @@ -1,5 +1,9 @@ package openwechat +import ( + "time" +) + // BotLogin 定义了一个Login的接口 type BotLogin interface { Login(bot *Bot) error @@ -28,8 +32,9 @@ func (s *SacnLogin) checkLogin(bot *Bot, uuid string) error { } type hotLoginOption struct { - withRetry bool - _ struct{} + withRetry bool + syncDuration time.Duration + _ struct{} } type HotLoginOptionFunc func(o *hotLoginOption) @@ -40,6 +45,13 @@ func HotLoginWithRetry(flag bool) HotLoginOptionFunc { } } +// HotLoginWithSyncReloadData 定时同步 HotLogin 的数据 +func HotLoginWithSyncReloadData(duration time.Duration) HotLoginOptionFunc { + return func(o *hotLoginOption) { + o.syncDuration = duration + } +} + // HotLogin 热登录模式 type HotLogin struct { storage HotReloadStorage @@ -48,6 +60,17 @@ type HotLogin struct { // Login 实现了 BotLogin 接口 func (h *HotLogin) Login(bot *Bot) error { + if err := h.loginWrapper(bot); err != nil { + return err + } + if h.opt.syncDuration > 0 { + syncer := NewHotReloadStorageSyncer(bot, h.opt.syncDuration) + go func() { _ = syncer.Sync() }() + } + return nil +} + +func (h *HotLogin) loginWrapper(bot *Bot) error { err := h.login(bot) if err != nil && h.opt.withRetry { scanLogin := SacnLogin{} @@ -73,6 +96,7 @@ type pushLoginOption struct { withoutScanCallback bool withoutLoginCallback bool withRetry bool + syncDuration time.Duration } type PushLoginOptionFunc func(o *pushLoginOption) @@ -105,6 +129,13 @@ func PushLoginWithRetry(flag bool) PushLoginOptionFunc { } } +// PushLoginWithSyncReloadData 定时同步 PushLogin 的数据 +func PushLoginWithSyncReloadData(duration time.Duration) PushLoginOptionFunc { + return func(o *pushLoginOption) { + o.syncDuration = duration + } +} + // defaultPushLoginOpts 默认的 PushLogin var defaultPushLoginOpts = [...]PushLoginOptionFunc{ PushLoginWithoutUUIDCallback(true), @@ -118,7 +149,18 @@ type PushLogin struct { } // Login 实现了 BotLogin 接口 -func (p PushLogin) Login(bot *Bot) error { +func (p *PushLogin) Login(bot *Bot) error { + if err := p.loginWrapper(bot); err != nil { + return err + } + if p.opt.syncDuration > 0 { + syncer := NewHotReloadStorageSyncer(bot, p.opt.syncDuration) + go func() { _ = syncer.Sync() }() + } + return nil +} + +func (p *PushLogin) loginWrapper(bot *Bot) error { err := p.login(bot) if err != nil && p.opt.withRetry { scanLogin := SacnLogin{} @@ -127,7 +169,7 @@ func (p PushLogin) Login(bot *Bot) error { return err } -func (p PushLogin) login(bot *Bot) error { +func (p *PushLogin) login(bot *Bot) error { if err := p.pushLoginInit(bot); err != nil { return err } @@ -141,13 +183,13 @@ func (p PushLogin) login(bot *Bot) error { return p.checkLogin(bot, resp.UUID) } -func (p PushLogin) pushLoginInit(bot *Bot) error { +func (p *PushLogin) pushLoginInit(bot *Bot) error { bot.hotReloadStorage = p.storage return bot.reload() } // checkLogin 登录检查 -func (p PushLogin) checkLogin(bot *Bot, uuid string) error { +func (p *PushLogin) checkLogin(bot *Bot, uuid string) error { bot.uuid = uuid loginChecker := &LoginChecker{ Bot: bot, diff --git a/stroage.go b/stroage.go index 1c7df10..0e82cee 100644 --- a/stroage.go +++ b/stroage.go @@ -3,6 +3,7 @@ package openwechat import ( "io" "os" + "time" ) // Storage 身份信息, 维持整个登陆的Session会话 @@ -75,3 +76,30 @@ func NewJsonFileHotReloadStorage(filename string) io.ReadWriteCloser { } var _ HotReloadStorage = (*jsonFileHotReloadStorage)(nil) + +type HotReloadStorageSyncer struct { + duration time.Duration + bot *Bot +} + +// Sync 定时同步数据到登陆存储中 +func (h *HotReloadStorageSyncer) Sync() error { + // 定时器 + ticker := time.NewTicker(h.duration) + for { + select { + case <-ticker.C: + // 每隔一段时间, 将数据同步到storage中 + if err := h.bot.DumpHotReloadStorage(); err != nil { + return err + } + case <-h.bot.Context().Done(): + // 当Bot关闭的时候, 退出循环 + return nil + } + } +} + +func NewHotReloadStorageSyncer(bot *Bot, duration time.Duration) *HotReloadStorageSyncer { + return &HotReloadStorageSyncer{duration: duration, bot: bot} +}