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
import (
"encoding/binary"
"errors"
"fmt"
"log"
@ -42,17 +43,37 @@ func DcClientFromConfig(databasePath string, config map[string]string) *deltacha
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)
context := dcClient.Context()
DCCtx := dcClient.Context()
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

@ -1,13 +1,11 @@
package main
import (
"encoding/binary"
"fmt"
"log"
"os"
"os/signal"
"github.com/Rhymen/go-whatsapp"
"github.com/hugot/go-deltachat/deltabot"
"github.com/hugot/go-deltachat/deltachat"
"github.com/hugot/whapp-deltachat/botcommands"
@ -32,12 +30,6 @@ func main() {
ensureDirectoryOrDie(config.App.DataFolder)
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{
dbPath: config.App.DataFolder + "/app.db",
}
@ -48,37 +40,24 @@ func main() {
log.Fatal(err)
}
ctx := dcClient.Context()
userName := "user"
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)
bridgeCtx := &BridgeContext{
DB: db,
}
userChatIDbs := make([]byte, 4)
binary.LittleEndian.PutUint32(userChatIDbs, userChatID)
err = db.Put([]byte("user-chat-id"), userChatIDbs)
dcClient, err := BootstrapDcClientFromConfig(*config, bridgeCtx)
if err != nil {
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++ {
wac, err = CreateAndLoginWhappConnection(
err = CreateAndLoginWhappConnection(
config.App.DataFolder,
dcClient.Context(),
dcUserID,
bridgeCtx,
)
if err == nil {
@ -90,18 +69,19 @@ func main() {
log.Fatal(err)
}
wac.AddHandler(&WhappHandler{
dcContext: dcClient.Context(),
db: db,
dcUserID: dcUserID,
wac: wac,
messageWorker := NewMessageWorker()
messageWorker.Start()
bridgeCtx.WhappConn.AddHandler(&WhappHandler{
BridgeContext: bridgeCtx,
MessageWorker: messageWorker,
})
bot := &deltabot.Bot{}
bot.AddCommand(&botcommands.Echo{})
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)

@ -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
import (
"fmt"
"log"
"github.com/Rhymen/go-whatsapp"
"github.com/hugot/go-deltachat/deltachat"
)
type WhappHandler struct {
dcContext *deltachat.Context
db *Database
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
BridgeContext *BridgeContext
MessageWorker *MessageWorker
}
func (h *WhappHandler) HandleError(err error) {
@ -54,28 +16,10 @@ func (h *WhappHandler) HandleError(err error) {
}
func (h *WhappHandler) HandleTextMessage(m whatsapp.TextMessage) {
JID := m.Info.RemoteJid
DCID, err := h.getOrCreateDCIDForJID(JID, m.Info.RemoteJid != m.Info.SenderJid)
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
handler := MessageHandler{
Jid: m.Info.RemoteJid,
Action: MakeTextMessageAction(h.BridgeContext, m),
}
h.dcContext.SendTextMessage(
DCID,
fmt.Sprintf("%s:\n%s", senderName, m.Text),
)
h.MessageWorker.HandleMessage(handler)
}

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

Loading…
Cancel
Save