Stop creating a new chat for each incoming message. Still spammy on restart though...

master
Hugo Thunnissen 4 years ago
parent cd5696954c
commit eb0a653195

@ -0,0 +1,52 @@
package main
import (
"github.com/Rhymen/go-whatsapp"
"github.com/hugot/go-deltachat/deltachat"
)
type BridgeContext struct {
WhappConn *whatsapp.Conn
DCContext *deltachat.Context
DB *Database
DCUserID uint32
DCUserChatID uint32
}
// Find or create a deltachat verified group chat for a whatsapp JID and return it's ID.
func (b *BridgeContext) GetOrCreateDCIDForJID(JID string, isGroup bool) (uint32, error) {
if DCID, _ := b.DB.GetDCIDForWhappJID(JID); DCID != nil {
return *DCID, nil
}
chatName := JID
if isGroup {
chat, ok := b.WhappConn.Store.Chats[JID]
if ok {
chatName = chat.Name
}
} else {
contact, ok := b.WhappConn.Store.Contacts[JID]
if ok {
chatName = contact.Name
}
}
DCID := b.DCContext.CreateGroupChat(true, chatName)
err := b.DB.StoreDCIDForJID(JID, DCID)
if err != nil {
return DCID, err
}
b.DCContext.AddContactToChat(DCID, b.DCUserID)
return DCID, err
}
func (b *BridgeContext) SendLog(logString string) {
b.DCContext.SendTextMessage(b.DCUserChatID, logString)
}

@ -0,0 +1,23 @@
package main
import "log"
type ChatWorker struct {
incomingHandlers chan MessageHandler
}
func (w *ChatWorker) Start() {
go func() {
for {
select {
case handler := <-w.incomingHandlers:
log.Println("Chat worker executing action")
err := handler.Action()
if err != nil {
log.Println(err)
}
}
}
}()
}

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@ -42,17 +43,37 @@ func DcClientFromConfig(databasePath string, config map[string]string) *deltacha
return client return client
} }
func BootstrapDcClientFromConfig(config Config) *deltachat.Client { func BootstrapDcClientFromConfig(config Config, ctx *BridgeContext) (*deltachat.Client, error) {
dcClient := DcClientFromConfig(config.App.DataFolder+"/deltachat.db", config.Deltachat) dcClient := DcClientFromConfig(config.App.DataFolder+"/deltachat.db", config.Deltachat)
context := dcClient.Context() DCCtx := dcClient.Context()
userName := "user" userName := "user"
dcUserID := DCCtx.CreateContact(&userName, &config.App.UserAddress)
userID := context.CreateContact(&userName, &config.App.UserAddress) userChatIDRaw := ctx.DB.Get([]byte("user-chat-id"))
var (
userChatID uint32
err error
)
if userChatIDRaw == nil {
userChatID, err = AddUserAsVerifiedContact(dcUserID, dcClient)
if err != nil {
return nil, err
}
} else {
userChatID = binary.LittleEndian.Uint32(userChatIDRaw)
}
userChatIDbs := make([]byte, 4)
binary.LittleEndian.PutUint32(userChatIDbs, userChatID)
err = ctx.DB.Put([]byte("user-chat-id"), userChatIDbs)
context.SendTextMessage(context.CreateChatByContactID(userID), "Whapp-Deltachat initialized") ctx.DCUserID = dcUserID
ctx.DCUserChatID = userChatID
ctx.DCContext = DCCtx
return dcClient return dcClient, err
} }
// Add a user as verified contact to the deltachat context. This is necessary to be able // Add a user as verified contact to the deltachat context. This is necessary to be able

@ -1,13 +1,11 @@
package main package main
import ( import (
"encoding/binary"
"fmt" "fmt"
"log" "log"
"os" "os"
"os/signal" "os/signal"
"github.com/Rhymen/go-whatsapp"
"github.com/hugot/go-deltachat/deltabot" "github.com/hugot/go-deltachat/deltabot"
"github.com/hugot/go-deltachat/deltachat" "github.com/hugot/go-deltachat/deltachat"
"github.com/hugot/whapp-deltachat/botcommands" "github.com/hugot/whapp-deltachat/botcommands"
@ -32,12 +30,6 @@ func main() {
ensureDirectoryOrDie(config.App.DataFolder) ensureDirectoryOrDie(config.App.DataFolder)
ensureDirectoryOrDie(config.App.DataFolder + "/tmp") ensureDirectoryOrDie(config.App.DataFolder + "/tmp")
dcClient := BootstrapDcClientFromConfig(*config)
// Give dc an opportunity to perform some close-down logic
// and close it's db etc.
defer dcClient.Close()
db := &Database{ db := &Database{
dbPath: config.App.DataFolder + "/app.db", dbPath: config.App.DataFolder + "/app.db",
} }
@ -48,37 +40,24 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
ctx := dcClient.Context() bridgeCtx := &BridgeContext{
userName := "user" DB: db,
dcUserID := ctx.CreateContact(&userName, &config.App.UserAddress)
userChatIDRaw := db.Get([]byte("user-chat-id"))
var userChatID uint32
if userChatIDRaw == nil {
userChatID, err = AddUserAsVerifiedContact(dcUserID, dcClient)
if err != nil {
log.Fatal(err)
}
} else {
userChatID = binary.LittleEndian.Uint32(userChatIDRaw)
} }
userChatIDbs := make([]byte, 4) dcClient, err := BootstrapDcClientFromConfig(*config, bridgeCtx)
binary.LittleEndian.PutUint32(userChatIDbs, userChatID)
err = db.Put([]byte("user-chat-id"), userChatIDbs)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
var wac *whatsapp.Conn // 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++ { for i := 0; i < 10; i++ {
wac, err = CreateAndLoginWhappConnection( err = CreateAndLoginWhappConnection(
config.App.DataFolder, config.App.DataFolder,
dcClient.Context(), bridgeCtx,
dcUserID,
) )
if err == nil { if err == nil {
@ -90,18 +69,19 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
wac.AddHandler(&WhappHandler{ messageWorker := NewMessageWorker()
dcContext: dcClient.Context(), messageWorker.Start()
db: db,
dcUserID: dcUserID, bridgeCtx.WhappConn.AddHandler(&WhappHandler{
wac: wac, BridgeContext: bridgeCtx,
MessageWorker: messageWorker,
}) })
bot := &deltabot.Bot{} bot := &deltabot.Bot{}
bot.AddCommand(&botcommands.Echo{}) bot.AddCommand(&botcommands.Echo{})
bot.AddCommand(botcommands.NewWhappBridge( bot.AddCommand(botcommands.NewWhappBridge(
wac, db, dcClient.Context().GetChatIDByContactID(dcUserID), bridgeCtx.WhappConn, bridgeCtx.DB, bridgeCtx.DCUserChatID,
)) ))
dcClient.On(deltachat.DC_EVENT_INCOMING_MSG, bot.HandleMessage) dcClient.On(deltachat.DC_EVENT_INCOMING_MSG, bot.HandleMessage)

@ -0,0 +1,41 @@
package main
import (
"fmt"
"log"
"github.com/Rhymen/go-whatsapp"
)
type MessageHandler struct {
Action MessageAction
Jid string
}
type MessageAction func() error
func MakeTextMessageAction(b *BridgeContext, m whatsapp.TextMessage) MessageAction {
return func() error {
JID := m.Info.RemoteJid
DCID, err := b.GetOrCreateDCIDForJID(JID, m.Info.RemoteJid != m.Info.SenderJid)
if err != nil {
log.Println(err)
b.SendLog(err.Error())
}
senderName := m.Info.Source.GetParticipant()
contact, ok := b.WhappConn.Store.Contacts[senderName]
if ok {
senderName = contact.Name
}
b.DCContext.SendTextMessage(
DCID,
fmt.Sprintf("%s:\n%s", senderName, m.Text),
)
return nil
}
}

@ -0,0 +1,44 @@
package main
import "log"
type MessageWorker struct {
incomingHandlers chan MessageHandler
chatWorkers map[string]chan MessageHandler
}
func NewMessageWorker() *MessageWorker {
return &MessageWorker{
incomingHandlers: make(chan MessageHandler),
chatWorkers: make(map[string]chan MessageHandler),
}
}
func (w *MessageWorker) HandleMessage(m MessageHandler) {
w.incomingHandlers <- m
}
func (w *MessageWorker) Start() {
go func() {
for {
select {
case handler := <-w.incomingHandlers:
log.Println("Got Handler for " + handler.Jid)
workerChan, ok := w.chatWorkers[handler.Jid]
if !ok {
workerChan = make(chan MessageHandler)
worker := &ChatWorker{
incomingHandlers: workerChan,
}
worker.Start()
w.chatWorkers[handler.Jid] = workerChan
}
workerChan <- handler
}
}
}()
}

@ -1,52 +1,14 @@
package main package main
import ( import (
"fmt"
"log" "log"
"github.com/Rhymen/go-whatsapp" "github.com/Rhymen/go-whatsapp"
"github.com/hugot/go-deltachat/deltachat"
) )
type WhappHandler struct { type WhappHandler struct {
dcContext *deltachat.Context BridgeContext *BridgeContext
db *Database MessageWorker *MessageWorker
dcUserID uint32
wac *whatsapp.Conn
}
// Find or create a deltachat verified group chat for a whatsapp JID and return it's ID.
func (h *WhappHandler) getOrCreateDCIDForJID(JID string, isGroup bool) (uint32, error) {
if DCID, _ := h.db.GetDCIDForWhappJID(JID); DCID != nil {
return *DCID, nil
}
chatName := JID
if isGroup {
chat, ok := h.wac.Store.Chats[JID]
if ok {
chatName = chat.Name
}
} else {
contact, ok := h.wac.Store.Contacts[JID]
if ok {
chatName = contact.Name
}
}
DCID := h.dcContext.CreateGroupChat(true, chatName)
err := h.db.StoreDCIDForJID(JID, DCID)
if err != nil {
return DCID, err
}
h.dcContext.AddContactToChat(DCID, h.dcUserID)
return DCID, err
} }
func (h *WhappHandler) HandleError(err error) { func (h *WhappHandler) HandleError(err error) {
@ -54,28 +16,10 @@ func (h *WhappHandler) HandleError(err error) {
} }
func (h *WhappHandler) HandleTextMessage(m whatsapp.TextMessage) { func (h *WhappHandler) HandleTextMessage(m whatsapp.TextMessage) {
JID := m.Info.RemoteJid handler := MessageHandler{
Jid: m.Info.RemoteJid,
DCID, err := h.getOrCreateDCIDForJID(JID, m.Info.RemoteJid != m.Info.SenderJid) Action: MakeTextMessageAction(h.BridgeContext, m),
if err != nil {
log.Println(err)
chatID := h.dcContext.GetChatIDByContactID(h.dcUserID)
h.dcContext.SendTextMessage(
chatID,
err.Error(),
)
}
senderName := m.Info.SenderJid
contact, ok := h.wac.Store.Contacts[m.Info.SenderJid]
if ok {
senderName = contact.Name
} }
h.dcContext.SendTextMessage( h.MessageWorker.HandleMessage(handler)
DCID,
fmt.Sprintf("%s:\n%s", senderName, m.Text),
)
} }

@ -15,23 +15,24 @@ import (
func CreateAndLoginWhappConnection( func CreateAndLoginWhappConnection(
storageDir string, storageDir string,
dcContext *deltachat.Context, ctx *BridgeContext,
dcUserID uint32, ) error {
) (*whatsapp.Conn, error) { wac, err := whatsapp.NewConn(300 * time.Second)
wac, err := whatsapp.NewConn(20 * time.Second)
if err != nil { if err != nil {
return wac, err return err
} }
ctx.WhappConn = wac
var session whatsapp.Session var session whatsapp.Session
sessionFile := storageDir + "/whapp-session.json" sessionFile := storageDir + "/whapp-session.json"
if _, err := os.Stat(sessionFile); os.IsNotExist(err) { if _, err := os.Stat(sessionFile); os.IsNotExist(err) {
session, err = WhappQrLogin(storageDir, wac, dcContext, dcUserID) session, err = WhappQrLogin(storageDir, ctx)
if err != nil { if err != nil {
return wac, err return err
} }
} else { } else {
session = whatsapp.Session{} session = whatsapp.Session{}
@ -41,26 +42,24 @@ func CreateAndLoginWhappConnection(
err = json.Unmarshal(sessionJson, &session) err = json.Unmarshal(sessionJson, &session)
if err != nil { if err != nil {
return wac, err return err
} }
session, err = wac.RestoreWithSession(session) session, err = wac.RestoreWithSession(session)
if err != nil { if err != nil {
return wac, err return err
} }
} }
err = StoreWhappSession(session, storageDir) err = StoreWhappSession(session, storageDir)
return wac, err return err
} }
func WhappQrLogin( func WhappQrLogin(
storageDir string, storageDir string,
wac *whatsapp.Conn, ctx *BridgeContext,
dcContext *deltachat.Context,
dcUserID uint32,
) (whatsapp.Session, error) { ) (whatsapp.Session, error) {
qrChan := make(chan string) qrChan := make(chan string)
@ -79,21 +78,21 @@ func WhappQrLogin(
log.Fatal(err) log.Fatal(err)
} }
message := dcContext.NewMessage(deltachat.DC_MSG_IMAGE) message := ctx.DCContext.NewMessage(deltachat.DC_MSG_IMAGE)
log.Println("MIME: " + mime.TypeByExtension("png")) log.Println("MIME: " + mime.TypeByExtension(".png"))
message.SetFile(tmpFile.Name(), "image/png") message.SetFile(tmpFile.Name(), "image/png")
message.SetText("Scan this QR code from whatsapp") message.SetText("Scan this QR code from whatsapp")
dcContext.SendMessage( ctx.DCContext.SendMessage(
dcContext.GetChatIDByContactID(dcUserID), ctx.DCUserChatID,
message, message,
) )
}() }()
session, err := wac.Login(qrChan) session, err := ctx.WhappConn.Login(qrChan)
return session, err return session, err
} }

Loading…
Cancel
Save