From e392242c79534f31994e07aff3b1ff7ed06b2278 Mon Sep 17 00:00:00 2001 From: Hugo Thunnissen Date: Tue, 14 Jan 2020 11:48:12 +0100 Subject: [PATCH] Move most of the initialization steps from main.go to packages Because it: - Declutters main.go - Makes code reusable --- botcommands/whapp-bridge.go | 43 +++++++----------- main.go | 68 ++-------------------------- whappdc-bridge/bridge.go | 41 +++++++++++++++++ whappdc-core/bridge_context.go | 79 +++++++++++++++++++++++++++++++++ whappdc-core/database.go | 4 ++ whappdc-core/deltachat.go | 1 + whappdc-core/message_tracker.go | 10 +++++ whappdc/whapp_handler.go | 7 +++ 8 files changed, 163 insertions(+), 90 deletions(-) create mode 100644 whappdc-bridge/bridge.go diff --git a/botcommands/whapp-bridge.go b/botcommands/whapp-bridge.go index 518fd53..27be819 100644 --- a/botcommands/whapp-bridge.go +++ b/botcommands/whapp-bridge.go @@ -6,38 +6,30 @@ import ( "github.com/Rhymen/go-whatsapp" "github.com/hugot/go-deltachat/deltachat" + core "github.com/hugot/whapp-deltachat/whappdc-core" ) -type Database interface { - GetWhappJIDForDCID(DCID uint32) (*string, error) -} - -func NewWhappBridge( - wac *whatsapp.Conn, - db Database, - UserChatID uint32, -) *WhappBridge { +func NewWhappBridge(bridgeContext *core.BridgeContext) *WhappBridge { return &WhappBridge{ - wac: wac, - db: db, - UserChatID: UserChatID, + bridgeCtx: bridgeContext, } } type WhappBridge struct { - wac *whatsapp.Conn - db Database - UserChatID uint32 + bridgeCtx *core.BridgeContext } func (b *WhappBridge) Accepts(c *deltachat.Chat, m *deltachat.Message) bool { chatID := c.GetID() - chatJID, err := b.db.GetWhappJIDForDCID(chatID) + chatJID, err := b.bridgeCtx.DB.GetWhappJIDForDCID(chatID) if err != nil { - // The database is failing, time to die :( - log.Fatal(err) + // The database is failing, very much an edge case. + log.Println(err) + b.bridgeCtx.SendLog(err.Error()) + + return false } return chatJID != nil @@ -48,18 +40,18 @@ func (b *WhappBridge) Execute( chat *deltachat.Chat, m *deltachat.Message, ) { - JID, err := b.db.GetWhappJIDForDCID(chat.GetID()) + JID, err := b.bridgeCtx.DB.GetWhappJIDForDCID(chat.GetID()) if err != nil { - c.SendTextMessage( - b.UserChatID, + log.Println(err) + b.bridgeCtx.SendLog( fmt.Sprintf( - "Whapp bridge dying: %s", + "Database error in Whapp bridge: %s", err.Error(), ), ) - log.Fatal(err) + return } text := whatsapp.TextMessage{ @@ -69,11 +61,10 @@ func (b *WhappBridge) Execute( Text: m.GetText(), } - _, err = b.wac.Send(text) + _, err = b.bridgeCtx.WhappConn.Send(text) if err != nil { - c.SendTextMessage( - b.UserChatID, + b.bridgeCtx.SendLog( fmt.Sprintf( "Error sending message to %s. \nMessage contents: %s\nError: %s", *JID, diff --git a/main.go b/main.go index 6ecd371..65c2042 100644 --- a/main.go +++ b/main.go @@ -6,12 +6,8 @@ import ( "os" "os/signal" "syscall" - "time" - "github.com/hugot/go-deltachat/deltabot" - "github.com/hugot/go-deltachat/deltachat" - "github.com/hugot/whapp-deltachat/botcommands" - "github.com/hugot/whapp-deltachat/whappdc" + bridge "github.com/hugot/whapp-deltachat/whappdc-bridge" core "github.com/hugot/whapp-deltachat/whappdc-core" ) @@ -34,70 +30,14 @@ func main() { ensureDirectoryOrDie(config.App.DataFolder) ensureDirectoryOrDie(config.App.DataFolder + "/tmp") - db := core.NewDatabase(config.App.DataFolder + "/app.db") - - err = db.Init() - - messageTracker := &core.MessageTracker{ - DB: db, - } - messageTracker.FlushWithInterval(15 * time.Minute) - - defer messageTracker.Flush() + bridge := &bridge.Bridge{} + err = bridge.Init(config) if err != nil { log.Fatal(err) } - bridgeCtx := &core.BridgeContext{ - Config: config, - DB: db, - MessageTracker: messageTracker, - } - - dcClient, err := core.BootstrapDcClientFromConfig(*config, bridgeCtx) - - bridgeCtx.SendLog("Whapp-Deltachat started.") - - if err != nil { - log.Fatal(err) - } - - // Give dc an opportunity to perform some close-down logic - // and close it's db etc. - defer dcClient.Close() - - for i := 0; i < 10; i++ { - err = core.CreateAndLoginWhappConnection( - config.App.DataFolder, - bridgeCtx, - ) - - if err == nil { - break - } - } - - if err != nil { - log.Fatal(err) - } - - messageWorker := whappdc.NewMessageWorker() - messageWorker.Start() - - bridgeCtx.WhappConn.AddHandler(&whappdc.WhappHandler{ - BridgeContext: bridgeCtx, - MessageWorker: messageWorker, - }) - - bot := &deltabot.Bot{} - - bot.AddCommand(&botcommands.Echo{}) - bot.AddCommand(botcommands.NewWhappBridge( - bridgeCtx.WhappConn, bridgeCtx.DB, bridgeCtx.DCUserChatID, - )) - - dcClient.On(deltachat.DC_EVENT_INCOMING_MSG, bot.HandleMessage) + defer bridge.Close() wait := make(chan os.Signal, 1) signal.Notify(wait, os.Interrupt, syscall.SIGTERM) diff --git a/whappdc-bridge/bridge.go b/whappdc-bridge/bridge.go new file mode 100644 index 0000000..45e624c --- /dev/null +++ b/whappdc-bridge/bridge.go @@ -0,0 +1,41 @@ +package bridge + +import ( + "time" + + "github.com/hugot/go-deltachat/deltabot" + "github.com/hugot/whapp-deltachat/botcommands" + "github.com/hugot/whapp-deltachat/whappdc" + core "github.com/hugot/whapp-deltachat/whappdc-core" +) + +type Bridge struct { + Ctx *core.BridgeContext +} + +func (b *Bridge) Init(config *core.Config) error { + messageWorker := whappdc.NewMessageWorker() + ctx := core.NewBridgeContext(config, 15*time.Minute) + + err := ctx.Init( + whappdc.NewWhappHandler(ctx, messageWorker), + []deltabot.Command{ + &botcommands.Echo{}, + botcommands.NewWhappBridge(ctx), + }, + ) + + if err != nil { + return err + } + + messageWorker.Start() + + b.Ctx = ctx + + return nil +} + +func (b *Bridge) Close() error { + return b.Ctx.Close() +} diff --git a/whappdc-core/bridge_context.go b/whappdc-core/bridge_context.go index 6f73209..f136815 100644 --- a/whappdc-core/bridge_context.go +++ b/whappdc-core/bridge_context.go @@ -2,8 +2,10 @@ package core import ( "log" + "time" "github.com/Rhymen/go-whatsapp" + "github.com/hugot/go-deltachat/deltabot" "github.com/hugot/go-deltachat/deltachat" ) @@ -11,12 +13,89 @@ type BridgeContext struct { Config *Config WhappConn *whatsapp.Conn DCContext *deltachat.Context + DCClient *deltachat.Client DB *Database MessageTracker *MessageTracker DCUserID uint32 DCUserChatID uint32 } +func NewBridgeContext(config *Config, msgTrackerFlushInterval time.Duration) *BridgeContext { + db := NewDatabase(config.App.DataFolder + "/app.db") + messageTracker := NewMessageTracker(db, msgTrackerFlushInterval) + + return &BridgeContext{ + Config: config, + DB: db, + MessageTracker: messageTracker, + } +} + +// Do all the initialization stuff like intializing databases & services, configuring +// clients, connecting to remote servers, logging in etc. +func (b *BridgeContext) Init( + whappHandler whatsapp.Handler, + botCommands []deltabot.Command, +) error { + err := b.DB.Init() + + if err != nil { + return err + } + + dcClient, err := BootstrapDcClientFromConfig(*b.Config, b) + + b.SendLog("Whapp-Deltachat started.") + + if err != nil { + return err + } + + for i := 0; i < 10; i++ { + err = CreateAndLoginWhappConnection(b.Config.App.DataFolder, b) + + if err == nil { + break + } + } + + if err != nil { + return err + } + + b.WhappConn.AddHandler(whappHandler) + + bot := &deltabot.Bot{} + + for _, command := range botCommands { + bot.AddCommand(command) + } + + dcClient.On(deltachat.DC_EVENT_INCOMING_MSG, bot.HandleMessage) + + return nil +} + +func (b *BridgeContext) Close() error { + _, err := b.WhappConn.Disconnect() + + if err != nil { + return err + } + + err = b.MessageTracker.Flush() + + if err != nil { + return err + } + + b.DCClient.Close() + + err = b.DB.Close() + + return err +} + // Find or create a deltachat verified group chat for a whatsapp JID and return it's ID. func (b *BridgeContext) GetOrCreateDCIDForJID(JID string) (uint32, error) { if DCID, _ := b.DB.GetDCIDForWhappJID(JID); DCID != nil { diff --git a/whappdc-core/database.go b/whappdc-core/database.go index df7c1d8..5f212eb 100644 --- a/whappdc-core/database.go +++ b/whappdc-core/database.go @@ -66,6 +66,10 @@ func (d *Database) Init() error { return nil } +func (d *Database) Close() error { + return d.db.Close() +} + func (d *Database) GetDCIDForWhappJID(JID string) (*uint32, error) { var DCID *uint32 diff --git a/whappdc-core/deltachat.go b/whappdc-core/deltachat.go index b9d7823..1387ff6 100644 --- a/whappdc-core/deltachat.go +++ b/whappdc-core/deltachat.go @@ -85,6 +85,7 @@ func BootstrapDcClientFromConfig(config Config, ctx *BridgeContext) (*deltachat. ctx.DCUserID = dcUserID ctx.DCUserChatID = userChatID ctx.DCContext = DCCtx + ctx.DCClient = dcClient return dcClient, err } diff --git a/whappdc-core/message_tracker.go b/whappdc-core/message_tracker.go index 71f3095..edc712b 100644 --- a/whappdc-core/message_tracker.go +++ b/whappdc-core/message_tracker.go @@ -6,6 +6,16 @@ import ( "time" ) +func NewMessageTracker(DB *Database, flushInterval time.Duration) *MessageTracker { + tracker := &MessageTracker{ + DB: DB, + } + + tracker.FlushWithInterval(flushInterval) + + return tracker +} + // MessageTracker will keep track of encountered whatsapp messages to prevent sending them // twice. It's storage is buffered to prevent continuous locks on the database. This means // that calling WasSent immediately after calling MarkSent will most likely not return an diff --git a/whappdc/whapp_handler.go b/whappdc/whapp_handler.go index ddcf679..4946360 100644 --- a/whappdc/whapp_handler.go +++ b/whappdc/whapp_handler.go @@ -13,6 +13,13 @@ type WhappHandler struct { MessageWorker *MessageWorker } +func NewWhappHandler(bridgeCtx *core.BridgeContext, messageWorker *MessageWorker) *WhappHandler { + return &WhappHandler{ + BridgeContext: bridgeCtx, + MessageWorker: messageWorker, + } +} + func (h *WhappHandler) HandleError(err error) { // If connection to the whapp servers failed for some reason, just retry. if _, connectionFailed := err.(*whatsapp.ErrConnectionFailed); connectionFailed {