Prevent sending duplicate messages and improve username determination

master
Hugo Thunnissen 4 years ago
parent eb0a653195
commit 3bccb1cf61

@ -6,32 +6,25 @@ import (
)
type BridgeContext struct {
WhappConn *whatsapp.Conn
DCContext *deltachat.Context
DB *Database
DCUserID uint32
DCUserChatID uint32
WhappConn *whatsapp.Conn
DCContext *deltachat.Context
DB *Database
MessageTracker *MessageTracker
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) {
func (b *BridgeContext) GetOrCreateDCIDForJID(JID string) (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
}
chat, ok := b.WhappConn.Store.Chats[JID]
if ok {
chatName = chat.Name
}
DCID := b.DCContext.CreateGroupChat(true, chatName)

@ -15,12 +15,14 @@ const (
JID_TO_DCID_INT uint8 = iota
DCID_TO_JID_INT
KEY_VALUE_INT
ID_WAS_SENT_INT
)
var (
JID_TO_DCID = []byte{JID_TO_DCID_INT}
DCID_TO_JID = []byte{DCID_TO_JID_INT}
KEY_VALUE = []byte{KEY_VALUE_INT}
ID_WAS_SENT = []byte{ID_WAS_SENT_INT}
)
func (d *Database) Init() error {
@ -42,6 +44,12 @@ func (d *Database) Init() error {
return err
}
_, err = tx.CreateBucketIfNotExists(ID_WAS_SENT)
if err != nil {
return err
}
_, err = tx.CreateBucketIfNotExists(KEY_VALUE)
return err
@ -136,3 +144,35 @@ func (d *Database) Get(key []byte) []byte {
return value
}
func (d *Database) MarkWhappMessagesSent(IDs []*string) error {
return d.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(ID_WAS_SENT)
for _, ID := range IDs {
if ID != nil {
bucket.Put([]byte(*ID), []byte{uint8(1)})
}
}
return nil
})
}
func (d *Database) WhappMessageWasSent(ID string) (bool, error) {
var wasSent bool = false
err := d.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(ID_WAS_SENT)
rawWasSent := bucket.Get([]byte(ID))
if len(rawWasSent) > 0 && uint8(rawWasSent[0]) == uint8(1) {
wasSent = true
}
return nil
})
return wasSent, err
}

@ -43,13 +43,26 @@ func DcClientFromConfig(databasePath string, config map[string]string) *deltacha
return client
}
// Note: this manipulates the BridgeContext.
func BootstrapDcClientFromConfig(config Config, ctx *BridgeContext) (*deltachat.Client, error) {
dcClient := DcClientFromConfig(config.App.DataFolder+"/deltachat.db", config.Deltachat)
DCCtx := dcClient.Context()
log.Println("Waiting for deltachat client to be configured")
for !DCCtx.IsConfigured() {
}
userName := "user"
dcUserID := DCCtx.CreateContact(&userName, &config.App.UserAddress)
// Send a message in a 1:1 chat first, this will let the user's client know that the
// crypto setup has changed if it has
DCCtx.SendTextMessage(
DCCtx.CreateChatByContactID(dcUserID),
"Hi, Whapp-Deltachat is initiallizing",
)
userChatIDRaw := ctx.DB.Get([]byte("user-chat-id"))
var (
userChatID uint32

@ -36,16 +36,25 @@ func main() {
err = db.Init()
messageTracker := &MessageTracker{
DB: db,
}
defer messageTracker.Flush()
if err != nil {
log.Fatal(err)
}
bridgeCtx := &BridgeContext{
DB: db,
DB: db,
MessageTracker: messageTracker,
}
dcClient, err := BootstrapDcClientFromConfig(*config, bridgeCtx)
bridgeCtx.SendLog("Whapp-Deltachat started.")
if err != nil {
log.Fatal(err)
}

@ -18,7 +18,19 @@ func MakeTextMessageAction(b *BridgeContext, m whatsapp.TextMessage) MessageActi
return func() error {
JID := m.Info.RemoteJid
DCID, err := b.GetOrCreateDCIDForJID(JID, m.Info.RemoteJid != m.Info.SenderJid)
wasSent, err := b.MessageTracker.WasSent(m.Info.Id)
if err != nil {
log.Println(err)
b.SendLog(err.Error())
}
// Messgae has already been sent
if wasSent == true {
return nil
}
DCID, err := b.GetOrCreateDCIDForJID(JID)
if err != nil {
log.Println(err)
@ -26,6 +38,16 @@ func MakeTextMessageAction(b *BridgeContext, m whatsapp.TextMessage) MessageActi
}
senderName := m.Info.Source.GetParticipant()
// No participant probably means that this isn't a group chat.
if senderName == "" {
senderName = m.Info.RemoteJid
}
if m.Info.FromMe == true {
senderName = b.DCContext.GetContact(b.DCUserID).GetDisplayName()
}
contact, ok := b.WhappConn.Store.Contacts[senderName]
if ok {
senderName = contact.Name
@ -36,6 +58,6 @@ func MakeTextMessageAction(b *BridgeContext, m whatsapp.TextMessage) MessageActi
fmt.Sprintf("%s:\n%s", senderName, m.Text),
)
return nil
return b.MessageTracker.MarkSent(&JID)
}
}

@ -0,0 +1,55 @@
package main
import (
"sync"
)
// 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
// up to date answer.
type MessageTracker struct {
DB *Database
delivered [80]*string
deliveredMutex sync.RWMutex
deliveredIdx int
}
func (t *MessageTracker) MarkSent(ID *string) error {
t.deliveredMutex.Lock()
defer t.deliveredMutex.Unlock()
t.delivered[t.deliveredIdx] = ID
if t.deliveredIdx == len(t.delivered)-1 {
err := t.flush()
if err != nil {
return err
}
}
t.deliveredIdx += 1
return nil
}
// Flush without lock
func (t *MessageTracker) flush() error {
err := t.DB.MarkWhappMessagesSent(t.delivered[:])
t.deliveredIdx = 0
return err
}
// Flush with lock
func (t *MessageTracker) Flush() error {
t.deliveredMutex.Lock()
defer t.deliveredMutex.Unlock()
return t.flush()
}
func (t *MessageTracker) WasSent(ID string) (bool, error) {
return t.DB.WhappMessageWasSent(ID)
}
Loading…
Cancel
Save