diff --git a/telegram/client.go b/telegram/client.go index 61d46aa..5b0217a 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -2,6 +2,7 @@ package telegram import ( "github.com/pkg/errors" + "hash/maphash" "path/filepath" "strconv" "sync" @@ -60,6 +61,9 @@ type Client struct { DelayedStatuses map[int64]*DelayedStatus DelayedStatusesLock sync.Mutex + lastMsgHashes map[int64]uint64 + msgHashSeed maphash.Seed + locks clientLocks SendMessageLock sync.Mutex } @@ -69,6 +73,7 @@ type clientLocks struct { chatMessageLocks map[int64]*sync.Mutex resourcesLock sync.Mutex outboxLock sync.Mutex + lastMsgHashesLock sync.Mutex } // NewClient instantiates a Telegram App @@ -129,6 +134,8 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component cache: cache.NewCache(), options: options, DelayedStatuses: make(map[int64]*DelayedStatus), + lastMsgHashes: make(map[int64]uint64), + msgHashSeed: maphash.MakeSeed(), locks: clientLocks{ chatMessageLocks: make(map[int64]*sync.Mutex), }, diff --git a/telegram/handlers.go b/telegram/handlers.go index abc1f5d..cc6e635 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -226,6 +226,8 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { }).Warn("New message from chat") c.ProcessIncomingMessage(chatId, update.Message, ignoredResource) + + c.updateLastMessageHash(update.Message.ChatId, update.Message.Id, update.Message.Content) }() } @@ -233,6 +235,8 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { markupFunction := formatter.EntityToXEP0393 + defer c.updateLastMessageHash(update.ChatId, update.MessageId, update.NewContent) + c.SendMessageLock.Lock() c.SendMessageLock.Unlock() xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, update.ChatId, update.MessageId) @@ -250,7 +254,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { return } - if update.NewContent.MessageContentType() == client.TypeMessageText { + if update.NewContent.MessageContentType() == client.TypeMessageText && c.hasLastMessageHashChanged(update.ChatId, update.MessageId, update.NewContent) { textContent := update.NewContent.(*client.MessageText) var editChar string if c.Session.AsciiArrows { diff --git a/telegram/utils.go b/telegram/utils.go index 9486349..4835696 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -2,8 +2,10 @@ package telegram import ( "crypto/sha1" + "encoding/binary" "fmt" "github.com/pkg/errors" + "hash/maphash" "io" "io/ioutil" "net/http" @@ -1384,3 +1386,58 @@ func (c *Client) getCarbonFullJids(isOutgoing bool, ignoredResource string) []st } return jids } + +func (c *Client) calculateMessageHash(messageId int64, content client.MessageContent) uint64 { + var h maphash.Hash + h.SetSeed(c.msgHashSeed) + + buf8 := make([]byte, 8) + binary.BigEndian.PutUint64(buf8, uint64(messageId)) + h.Write(buf8) + + if content != nil && content.MessageContentType() == client.TypeMessageText { + textContent, ok := content.(*client.MessageText) + if !ok { + uhOh() + } + + if textContent.Text != nil { + h.WriteString(textContent.Text.Text) + for _, entity := range textContent.Text.Entities { + buf4 := make([]byte, 4) + binary.BigEndian.PutUint32(buf4, uint32(entity.Offset)) + h.Write(buf4) + binary.BigEndian.PutUint32(buf4, uint32(entity.Length)) + h.Write(buf4) + h.WriteString(entity.Type.TextEntityTypeType()) + } + } + } + + return h.Sum64() +} + +func (c *Client) updateLastMessageHash(chatId, messageId int64, content client.MessageContent) { + c.locks.lastMsgHashesLock.Lock() + defer c.locks.lastMsgHashesLock.Unlock() + + c.lastMsgHashes[chatId] = c.calculateMessageHash(messageId, content) +} + +func (c *Client) hasLastMessageHashChanged(chatId, messageId int64, content client.MessageContent) bool { + c.locks.lastMsgHashesLock.Lock() + defer c.locks.lastMsgHashesLock.Unlock() + + oldHash, ok := c.lastMsgHashes[chatId] + newHash := c.calculateMessageHash(messageId, content) + + if !ok { + log.Warnf("Last message hash for chat %v does not exist", chatId) + } + log.WithFields(log.Fields{ + "old hash": oldHash, + "new hash": newHash, + }).Info("Message hashes") + + return !ok || oldHash != newHash +}