Track updated message ids for more reliable message edit check

This commit is contained in:
Bohdan Horbeshko 2025-10-31 13:16:56 -04:00
parent 5650850be9
commit eee277e36e
3 changed files with 158 additions and 47 deletions

View file

@ -27,6 +27,41 @@ type HashedAvatar struct {
File int32
}
// NewId stores message ids and timestamps of their additions so old ones can be truncated to save memory
type newId struct {
Id int64
Ts int64
lock sync.Mutex
ownLock sync.Mutex
locked bool
fired bool
}
func newNewId() *newId {
return &newId{Ts: time.Now().Unix()}
}
func (i *newId) Lock() {
i.ownLock.Lock()
if i.fired {
i.ownLock.Unlock()
return
}
i.locked = true
i.ownLock.Unlock()
i.lock.Lock()
}
func (i *newId) Unlock() {
i.ownLock.Lock()
if i.locked {
i.lock.Unlock()
i.locked = false
i.fired = true
}
i.ownLock.Unlock()
}
// Client stores the metadata for lazily invoked TDlib instance
type Client struct {
client *client.Client
@ -64,6 +99,9 @@ type Client struct {
AvatarHashes map[int64]*HashedAvatar
AvatarHashesLock sync.Mutex
MessageIdChanges map[int64]map[int64]*newId
MessageIdChangesLock sync.Mutex
locks clientLocks
SendMessageLock sync.Mutex
}
@ -149,6 +187,7 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component
lastMsgIds: make(map[int64]string),
XmppClientFeatures: make(map[string]*[]string),
AvatarHashes: make(map[int64]*HashedAvatar),
MessageIdChanges: make(map[int64]map[int64]*newId),
locks: clientLocks{
chatMessageLocks: make(map[int64]*sync.Mutex),
},

View file

@ -312,21 +312,48 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
sId := strconv.FormatInt(update.MessageId, 10)
var isCarbon bool
go func() {
message, messageErr := c.client.GetMessage(&client.GetMessageRequest{
ChatId: update.ChatId,
MessageId: update.MessageId,
})
if messageErr != nil {
// odnako za vremya puti
// sobaka mogla podrasti
c.MessageIdChangesLock.Lock()
idsMap, idsMapOk := c.MessageIdChanges[update.ChatId]
hadNoId := false
if idsMapOk {
newId, newIdOk := idsMap[update.MessageId]
if newIdOk {
if newId.Id == 0 {
hadNoId = true
c.MessageIdChangesLock.Unlock()
newId.Lock()
}
log.Infof("falling back to updated message id: %v/%v->%v", update.ChatId, update.MessageId, newId.Id)
message, messageErr = c.client.GetMessage(&client.GetMessageRequest{
ChatId: update.ChatId,
MessageId: newId.Id,
})
}
}
if !hadNoId {
c.MessageIdChangesLock.Unlock()
}
}
var prefix string
if messageErr == nil {
if message.EditDate == 0 {
return
}
log.Debugf("editDate: %v", message.EditDate)
isCarbon = c.isCarbonsEnabled() && message.IsOutgoing
// reply correction support in clients is suboptimal yet, so cut them out for now
prefix, _ = c.messageToPrefix(message, "", "", true)
} else {
log.Errorf("No message %v/%v found, cannot reliably determine if it is a carbon and if it is edited", update.ChatId, update.MessageId)
log.Errorf("No message %v/%v found, cannot reliably determine if it is a carbon and if it is edited: %v", update.ChatId, update.MessageId, messageErr.Error())
}
// use XEP-0308 edits only if the last message is edited for sure, fallback otherwise
@ -367,12 +394,17 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
for _, jid := range jids {
gateway.SendMessage(jid, sChatId, text.String(), "e"+sId, c.xmpp, nil, replaceId, isCarbon, false)
}
}()
}
}
// message(s) deleted
func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) {
if update.IsPermanent {
for _, deleteId := range update.MessageIds {
c.tryUnlockMessageId(update.ChatId, deleteId)
}
if c.Session.IsChatIgnored(update.ChatId) {
return
}
@ -411,6 +443,20 @@ func (c *Client) updateMessageSendSucceeded(update *client.UpdateMessageSendSucc
log.Errorf("failed to replace %v with %v: %v", update.OldMessageId, update.Message.Id, err.Error())
}
c.MessageIdChangesLock.Lock()
idsMap, ok := c.MessageIdChanges[update.Message.ChatId]
if !ok {
idsMap = make(map[int64]*newId)
c.MessageIdChanges[update.Message.ChatId] = idsMap
}
id, ok := idsMap[update.OldMessageId]
if !ok {
id = newNewId()
idsMap[update.OldMessageId] = id
}
id.Id = update.Message.Id
c.MessageIdChangesLock.Unlock()
c.sendMarker(update.Message.ChatId, update.Message.Id, gateway.MarkerTypeReceived)
// clean uploaded files
@ -420,6 +466,8 @@ func (c *Client) updateMessageSendSucceeded(update *client.UpdateMessageSendSucc
}
}
func (c *Client) updateMessageSendFailed(update *client.UpdateMessageSendFailed) {
c.tryUnlockMessageId(update.Message.ChatId, update.OldMessageId)
// clean uploaded files
file, _ := c.contentToFile(update.Message.Content)
if file != nil && file.Local != nil {
@ -446,3 +494,15 @@ func (c *Client) updateChatTitle(update *client.UpdateChatTitle) {
func (c *Client) updateChatReadOutbox(update *client.UpdateChatReadOutbox) {
c.sendMarker(update.ChatId, update.LastReadOutboxMessageId, gateway.MarkerTypeDisplayed)
}
func (c *Client) tryUnlockMessageId(chatId, messageId int64) {
c.MessageIdChangesLock.Lock()
idsMap, ok := c.MessageIdChanges[chatId]
if ok {
id, ok := idsMap[messageId]
if ok {
id.Unlock()
}
}
c.MessageIdChangesLock.Unlock()
}

View file

@ -148,6 +148,18 @@ func heartbeat(component *xmpp.Component) {
}
}
session.DelayedStatusesLock.Unlock()
// shrink message id maps
session.MessageIdChangesLock.Lock()
for _, idsMap := range session.MessageIdChanges {
for oldMessageId, newId := range idsMap {
if newId.Ts < now - 60 {
newId.Unlock()
delete(idsMap, oldMessageId)
}
}
}
session.MessageIdChangesLock.Unlock()
}
sessionLock.Unlock()