Handle edit first & then message

This commit is contained in:
Andrea Maria Piana 2021-06-08 08:07:45 +02:00
parent 075ee9c29f
commit ee06809ff8
7 changed files with 163 additions and 58 deletions

View File

@ -48,7 +48,7 @@ func (m *Messenger) HandleMembershipUpdate(messageState *ReceivedMessageState, c
return err return err
} }
allowed, err := m.isMessageAllowedFrom(messageState.AllContacts, messageState.CurrentMessageState.Contact.ID, chat) allowed, err := m.isMessageAllowedFrom(messageState.CurrentMessageState.Contact.ID, chat)
if err != nil { if err != nil {
return err return err
} }
@ -189,12 +189,12 @@ func (m *Messenger) handleCommandMessage(state *ReceivedMessageState, message *c
if err := message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey)); err != nil { if err := message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey)); err != nil {
return fmt.Errorf("failed to prepare content: %v", err) return fmt.Errorf("failed to prepare content: %v", err)
} }
chat, err := m.matchChatEntity(message, state.AllChats, state.AllContacts, state.Timesource) chat, err := m.matchChatEntity(message)
if err != nil { if err != nil {
return err return err
} }
allowed, err := m.isMessageAllowedFrom(state.AllContacts, state.CurrentMessageState.Contact.ID, chat) allowed, err := m.isMessageAllowedFrom(state.CurrentMessageState.Contact.ID, chat)
if err != nil { if err != nil {
return err return err
} }
@ -320,7 +320,7 @@ func (m *Messenger) HandlePinMessage(state *ReceivedMessageState, message protob
Alias: state.CurrentMessageState.Contact.Alias, Alias: state.CurrentMessageState.Contact.Alias,
} }
chat, err := m.matchChatEntity(pinMessage, state.AllChats, state.AllContacts, state.Timesource) chat, err := m.matchChatEntity(pinMessage)
if err != nil { if err != nil {
return err // matchChatEntity returns a descriptive error message return err // matchChatEntity returns a descriptive error message
} }
@ -363,7 +363,7 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro
contact := state.CurrentMessageState.Contact contact := state.CurrentMessageState.Contact
chat, ok := state.AllChats.Load(contact.ID) chat, ok := state.AllChats.Load(contact.ID)
allowed, err := m.isMessageAllowedFrom(state.AllContacts, state.CurrentMessageState.Contact.ID, chat) allowed, err := m.isMessageAllowedFrom(state.CurrentMessageState.Contact.ID, chat)
if err != nil { if err != nil {
return err return err
} }
@ -497,7 +497,7 @@ func (m *Messenger) HandleEditMessage(response *MessengerResponse, editMessage E
var err error var err error
originalMessage, err = m.persistence.MessageByID(messageID) originalMessage, err = m.persistence.MessageByID(messageID)
if err != nil { if err != nil && err != common.ErrRecordNotFound {
return err return err
} }
} }
@ -554,12 +554,12 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
if err != nil { if err != nil {
return fmt.Errorf("failed to prepare message content: %v", err) return fmt.Errorf("failed to prepare message content: %v", err)
} }
chat, err := m.matchChatEntity(receivedMessage, state.AllChats, state.AllContacts, state.Timesource) chat, err := m.matchChatEntity(receivedMessage)
if err != nil { if err != nil {
return err // matchChatEntity returns a descriptive error message return err // matchChatEntity returns a descriptive error message
} }
allowed, err := m.isMessageAllowedFrom(state.AllContacts, state.CurrentMessageState.Contact.ID, chat) allowed, err := m.isMessageAllowedFrom(state.CurrentMessageState.Contact.ID, chat)
if err != nil { if err != nil {
return err return err
} }
@ -587,7 +587,7 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
// Set the LocalChatID for the message // Set the LocalChatID for the message
receivedMessage.LocalChatID = chat.ID receivedMessage.LocalChatID = chat.ID
if c, ok := state.AllChats.Load(chat.ID); ok { if c, ok := m.allChats.Load(chat.ID); ok {
chat = c chat = c
} }
@ -602,7 +602,12 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
receivedMessage.OutgoingStatus = common.OutgoingStatusSent receivedMessage.OutgoingStatus = common.OutgoingStatusSent
} }
err = chat.UpdateFromMessage(receivedMessage, state.Timesource) err = m.checkForEdits(receivedMessage)
if err != nil {
return err
}
err = chat.UpdateFromMessage(receivedMessage, m.getTimesource())
if err != nil { if err != nil {
return err return err
} }
@ -615,7 +620,7 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error {
// Set in the modified maps chat // Set in the modified maps chat
state.Response.AddChat(chat) state.Response.AddChat(chat)
// TODO(samyoul) remove storing of an updated reference pointer? // TODO(samyoul) remove storing of an updated reference pointer?
state.AllChats.Store(chat.ID, chat) m.allChats.Store(chat.ID, chat)
contact := state.CurrentMessageState.Contact contact := state.CurrentMessageState.Contact
if receivedMessage.EnsName != "" { if receivedMessage.EnsName != "" {
@ -861,7 +866,7 @@ func (m *Messenger) HandleDeclineRequestTransaction(messageState *ReceivedMessag
return m.handleCommandMessage(messageState, oldMessage) return m.handleCommandMessage(messageState, oldMessage)
} }
func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity, chats *chatMap, contacts *contactMap, timesource common.TimeSource) (*Chat, error) { func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity) (*Chat, error) {
if chatEntity.GetSigPubKey() == nil { if chatEntity.GetSigPubKey() == nil {
m.logger.Error("public key can't be empty") m.logger.Error("public key can't be empty")
return nil, errors.New("received a chatEntity with empty public key") return nil, errors.New("received a chatEntity with empty public key")
@ -872,7 +877,7 @@ func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity, chats *chatMap
// For public messages, all outgoing and incoming messages have the same chatID // For public messages, all outgoing and incoming messages have the same chatID
// equal to a public chat name. // equal to a public chat name.
chatID := chatEntity.GetChatId() chatID := chatEntity.GetChatId()
chat, ok := chats.Load(chatID) chat, ok := m.allChats.Load(chatID)
if !ok { if !ok {
return nil, errors.New("received a public chatEntity from non-existing chat") return nil, errors.New("received a public chatEntity from non-existing chat")
} }
@ -881,7 +886,7 @@ func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity, chats *chatMap
// It's a private message coming from us so we rely on Message.ChatID // It's a private message coming from us so we rely on Message.ChatID
// If chat does not exist, it should be created to support multidevice synchronization. // If chat does not exist, it should be created to support multidevice synchronization.
chatID := chatEntity.GetChatId() chatID := chatEntity.GetChatId()
chat, ok := chats.Load(chatID) chat, ok := m.allChats.Load(chatID)
if !ok { if !ok {
if len(chatID) != PubKeyStringLength { if len(chatID) != PubKeyStringLength {
return nil, errors.New("invalid pubkey length") return nil, errors.New("invalid pubkey length")
@ -896,27 +901,27 @@ func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity, chats *chatMap
return nil, errors.Wrap(err, "failed to decode pubkey") return nil, errors.Wrap(err, "failed to decode pubkey")
} }
chat = CreateOneToOneChat(chatID[:8], pubKey, timesource) chat = CreateOneToOneChat(chatID[:8], pubKey, m.getTimesource())
} }
return chat, nil return chat, nil
case chatEntity.GetMessageType() == protobuf.MessageType_ONE_TO_ONE: case chatEntity.GetMessageType() == protobuf.MessageType_ONE_TO_ONE:
// It's an incoming private chatEntity. ChatID is calculated from the signature. // It's an incoming private chatEntity. ChatID is calculated from the signature.
// If a chat does not exist, a new one is created and saved. // If a chat does not exist, a new one is created and saved.
chatID := contactIDFromPublicKey(chatEntity.GetSigPubKey()) chatID := contactIDFromPublicKey(chatEntity.GetSigPubKey())
chat, ok := chats.Load(chatID) chat, ok := m.allChats.Load(chatID)
if !ok { if !ok {
// TODO: this should be a three-word name used in the mobile client // TODO: this should be a three-word name used in the mobile client
chat = CreateOneToOneChat(chatID[:8], chatEntity.GetSigPubKey(), timesource) chat = CreateOneToOneChat(chatID[:8], chatEntity.GetSigPubKey(), m.getTimesource())
chat.Active = false chat.Active = false
} }
// We set the chat as inactive and will create a notification // We set the chat as inactive and will create a notification
// if it's not coming from a contact // if it's not coming from a contact
contact, ok := contacts.Load(chatID) contact, ok := m.allContacts.Load(chatID)
chat.Active = chat.Active || (ok && contact.IsAdded()) chat.Active = chat.Active || (ok && contact.IsAdded())
return chat, nil return chat, nil
case chatEntity.GetMessageType() == protobuf.MessageType_COMMUNITY_CHAT: case chatEntity.GetMessageType() == protobuf.MessageType_COMMUNITY_CHAT:
chatID := chatEntity.GetChatId() chatID := chatEntity.GetChatId()
chat, ok := chats.Load(chatID) chat, ok := m.allChats.Load(chatID)
if !ok { if !ok {
return nil, errors.New("received community chat chatEntity for non-existing chat") return nil, errors.New("received community chat chatEntity for non-existing chat")
} }
@ -945,7 +950,7 @@ func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity, chats *chatMap
// In the case of a group chatEntity, ChatID is the same for all messages belonging to a group. // In the case of a group chatEntity, ChatID is the same for all messages belonging to a group.
// It needs to be verified if the signature public key belongs to the chat. // It needs to be verified if the signature public key belongs to the chat.
chatID := chatEntity.GetChatId() chatID := chatEntity.GetChatId()
chat, ok := chats.Load(chatID) chat, ok := m.allChats.Load(chatID)
if !ok { if !ok {
return nil, errors.New("received group chat chatEntity for non-existing chat") return nil, errors.New("received group chat chatEntity for non-existing chat")
} }
@ -1019,7 +1024,7 @@ func (m *Messenger) HandleEmojiReaction(state *ReceivedMessageState, pbEmojiR pr
return nil return nil
} }
chat, err := m.matchChatEntity(emojiReaction, state.AllChats, state.AllContacts, state.Timesource) chat, err := m.matchChatEntity(emojiReaction)
if err != nil { if err != nil {
return err // matchChatEntity returns a descriptive error message return err // matchChatEntity returns a descriptive error message
} }
@ -1049,7 +1054,7 @@ func (m *Messenger) HandleEmojiReaction(state *ReceivedMessageState, pbEmojiR pr
} }
func (m *Messenger) HandleGroupChatInvitation(state *ReceivedMessageState, pbGHInvitations protobuf.GroupChatInvitation) error { func (m *Messenger) HandleGroupChatInvitation(state *ReceivedMessageState, pbGHInvitations protobuf.GroupChatInvitation) error {
allowed, err := m.isMessageAllowedFrom(state.AllContacts, state.CurrentMessageState.Contact.ID, nil) allowed, err := m.isMessageAllowedFrom(state.CurrentMessageState.Contact.ID, nil)
if err != nil { if err != nil {
return err return err
} }
@ -1102,7 +1107,7 @@ func (m *Messenger) HandleGroupChatInvitation(state *ReceivedMessageState, pbGHI
// extracts contact information stored in the protobuf and adds it to the user's contact for update. // extracts contact information stored in the protobuf and adds it to the user's contact for update.
func (m *Messenger) HandleChatIdentity(state *ReceivedMessageState, ci protobuf.ChatIdentity) error { func (m *Messenger) HandleChatIdentity(state *ReceivedMessageState, ci protobuf.ChatIdentity) error {
logger := m.logger.With(zap.String("site", "HandleChatIdentity")) logger := m.logger.With(zap.String("site", "HandleChatIdentity"))
allowed, err := m.isMessageAllowedFrom(state.AllContacts, state.CurrentMessageState.Contact.ID, nil) allowed, err := m.isMessageAllowedFrom(state.CurrentMessageState.Contact.ID, nil)
if err != nil { if err != nil {
return err return err
} }
@ -1135,10 +1140,31 @@ func (m *Messenger) HandleChatIdentity(state *ReceivedMessageState, ci protobuf.
func (m *Messenger) checkForEdits(message *common.Message) error { func (m *Messenger) checkForEdits(message *common.Message) error {
// Check for any pending edit // Check for any pending edit
// If any pending edits are available and valid, apply them // If any pending edits are available and valid, apply them
edits, err := m.persistence.GetEdits(message.ID, message.From)
if err != nil {
return err
}
if len(edits) == 0 {
return nil
}
// Apply the first edit that is valid
for _, e := range edits {
if e.Clock >= message.Clock {
// Update message and return it
err := m.applyEditMessage(&e.EditMessage, message)
if err != nil {
return err
}
return nil
}
}
return nil return nil
} }
func (m *Messenger) isMessageAllowedFrom(allContacts *contactMap, publicKey string, chat *Chat) (bool, error) { func (m *Messenger) isMessageAllowedFrom(publicKey string, chat *Chat) (bool, error) {
onlyFromContacts, err := m.settings.GetMessagesFromContactsOnly() onlyFromContacts, err := m.settings.GetMessagesFromContactsOnly()
if err != nil { if err != nil {
return false, err return false, err
@ -1163,7 +1189,7 @@ func (m *Messenger) isMessageAllowedFrom(allContacts *contactMap, publicKey stri
return true, nil return true, nil
} }
contact, ok := allContacts.Load(publicKey) contact, ok := m.allContacts.Load(publicKey)
if !ok { if !ok {
// If it's not in contacts, we don't allow it // If it's not in contacts, we don't allow it
return false, nil return false, nil

View File

@ -1428,10 +1428,30 @@ func (db sqlitePersistence) deactivateChat(chat *Chat, currentClockValue uint64,
} }
func (db sqlitePersistence) SaveEdit(editMessage EditMessage) error { func (db sqlitePersistence) SaveEdit(editMessage EditMessage) error {
_, err := db.db.Exec(`INSERT INTO user_messages_edits (clock, chat_id, message_id, source, text, id) VALUES(?,?,?,?,?,?)`, editMessage.Clock, editMessage.ChatId, editMessage.MessageId, editMessage.Text, editMessage.From, editMessage.ID) _, err := db.db.Exec(`INSERT INTO user_messages_edits (clock, chat_id, message_id, text, source, id) VALUES(?,?,?,?,?,?)`, editMessage.Clock, editMessage.ChatId, editMessage.MessageId, editMessage.Text, editMessage.From, editMessage.ID)
return err return err
} }
func (db sqlitePersistence) GetEdits(messageID string, from string) ([]*EditMessage, error) {
rows, err := db.db.Query(`SELECT clock, chat_id, message_id, source, text, id FROM user_messages_edits WHERE message_id = ? AND source = ? ORDER BY CLOCK DESC`, messageID, from)
if err != nil {
return nil, err
}
var messages []*EditMessage
for rows.Next() {
e := &EditMessage{}
err := rows.Scan(&e.Clock, &e.ChatId, &e.MessageId, &e.From, &e.Text, &e.ID)
if err != nil {
return nil, err
}
messages = append(messages, e)
}
return messages, nil
}
func (db sqlitePersistence) clearHistory(chat *Chat, currentClockValue uint64, tx *sql.Tx, deactivate bool) error { func (db sqlitePersistence) clearHistory(chat *Chat, currentClockValue uint64, tx *sql.Tx, deactivate bool) error {
// Set deleted at clock value if it's not a public chat so that // Set deleted at clock value if it's not a public chat so that
// old messages will be discarded, or if it's a straight clear history // old messages will be discarded, or if it's a straight clear history

View File

@ -11,6 +11,7 @@ import (
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/protocol/tt"
@ -220,3 +221,59 @@ func (s *MessengerEditMessageSuite) TestEditMessageEdgeCases() {
// It discards the edit // It discards the edit
s.Require().Len(response.Messages(), 0) s.Require().Len(response.Messages(), 0)
} }
func (s *MessengerEditMessageSuite) TestEditMessageFirstEditsThenMessage() {
theirMessenger := s.newMessenger()
_, err := theirMessenger.Start()
s.Require().NoError(err)
theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
err = theirMessenger.SaveChat(theirChat)
s.Require().NoError(err)
contact, err := BuildContactFromPublicKey(&theirMessenger.identity.PublicKey)
s.Require().NoError(err)
ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
err = s.m.SaveChat(ourChat)
s.Require().NoError(err)
messageID := "message-id"
inputMessage := buildTestMessage(*theirChat)
inputMessage.Clock = 1
editMessage := EditMessage{
EditMessage: protobuf.EditMessage{
Clock: 2,
Text: "some text",
MessageId: messageID,
ChatId: theirChat.ID,
},
From: common.PubkeyToHex(&theirMessenger.identity.PublicKey),
}
response := &MessengerResponse{}
// Handle edit first
err = s.m.HandleEditMessage(response, editMessage)
s.Require().NoError(err)
// Handle chat message
response = &MessengerResponse{}
state := &ReceivedMessageState{
Response: response,
CurrentMessageState: &CurrentMessageState{
Message: inputMessage.ChatMessage,
MessageID: messageID,
WhisperTimestamp: s.m.getTimesource().GetCurrentTime(),
Contact: contact,
PublicKey: &theirMessenger.identity.PublicKey,
},
}
err = s.m.HandleChatMessage(state)
s.Require().NoError(err)
s.Require().Len(response.Messages(), 1)
editedMessage := response.Messages()[0]
s.Require().Equal(uint64(2), editedMessage.EditedAt)
}

View File

@ -24,12 +24,7 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
return nil, err return nil, err
} }
sender, err := message.GetSenderPubKey() if message.From != common.PubkeyToHex(&m.identity.PublicKey) {
if err != nil {
return nil, err
}
if !sender.Equal(&m.identity.PublicKey) {
return nil, ErrInvalidEditAuthor return nil, ErrInvalidEditAuthor
} }
@ -80,6 +75,7 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
} }
func (m *Messenger) applyEditMessage(editMessage *protobuf.EditMessage, message *common.Message) error { func (m *Messenger) applyEditMessage(editMessage *protobuf.EditMessage, message *common.Message) error {
// TODO: should save an edit if it's the first time it's being edited
message.Text = editMessage.Text message.Text = editMessage.Text
message.EditedAt = editMessage.Clock message.EditedAt = editMessage.Clock

View File

@ -36,8 +36,8 @@
// 1622464518_set_synced_to_from.up.sql (105B) // 1622464518_set_synced_to_from.up.sql (105B)
// 1622464519_add_chat_description.up.sql (93B) // 1622464519_add_chat_description.up.sql (93B)
// 1622622253_add_pinned_by_to_pin_messages.up.sql (52B) // 1622622253_add_pinned_by_to_pin_messages.up.sql (52B)
// 1622722745_add_original_message_id.up.sql (273B)
// 1623938329_add_author_activity_center_notification_field.up.sql (66B) // 1623938329_add_author_activity_center_notification_field.up.sql (66B)
// 1623938330_add_edit_messages.up.sql (369B)
// README.md (554B) // README.md (554B)
// doc.go (850B) // doc.go (850B)
@ -828,26 +828,6 @@ func _1622622253_add_pinned_by_to_pin_messagesUpSql() (*asset, error) {
return a, nil return a, nil
} }
var __1622722745_add_original_message_idUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xcc\xc1\x8a\x83\x30\x14\x85\xe1\x7d\x9e\xe2\x2c\x15\xe6\x0d\x5c\xdd\xd1\xcb\x8c\x4c\x8c\x43\x88\x05\x57\x41\xcc\xa5\x95\xb6\x08\x26\x42\x1f\xbf\x08\xed\xa2\xa0\xeb\xef\x9c\x9f\xb4\x63\x0b\x47\xdf\x9a\xb1\x46\x59\xfc\x5d\x62\x1c\xce\x12\x41\x55\x85\xb2\xd5\x5d\x63\x20\x61\x4a\x12\xfc\x90\x50\x1b\xc7\x3f\x6c\x0b\xa5\x4a\xcb\xe4\x78\xef\xe9\xb7\x79\x44\xa6\x80\xf1\x36\x8f\xd7\xf7\x09\xa6\x75\x30\x9d\xd6\x5f\x9b\x5c\x86\xe4\xa7\x80\x13\xd9\xf2\x97\x3e\xed\x15\x3a\xe2\x38\xaf\xcb\x28\xbb\x94\xe4\x91\x76\xe1\x20\xf5\x6f\xeb\x86\x6c\x8f\x3f\xee\xb3\x29\xe4\x2a\x2f\xd4\x33\x00\x00\xff\xff\x94\x32\x5e\xe6\x11\x01\x00\x00")
func _1622722745_add_original_message_idUpSqlBytes() ([]byte, error) {
return bindataRead(
__1622722745_add_original_message_idUpSql,
"1622722745_add_original_message_id.up.sql",
)
}
func _1622722745_add_original_message_idUpSql() (*asset, error) {
bytes, err := _1622722745_add_original_message_idUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1622722745_add_original_message_id.up.sql", size: 273, mode: os.FileMode(0644), modTime: time.Unix(1624368044, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0x47, 0x48, 0x84, 0x7a, 0x2f, 0x30, 0x5c, 0x33, 0xa4, 0x42, 0xfb, 0x7d, 0xe1, 0xa6, 0x46, 0x9d, 0x20, 0x19, 0x99, 0x56, 0xbb, 0x9f, 0xd, 0xe4, 0x6b, 0x99, 0x29, 0xe5, 0xef, 0xef, 0x58}}
return a, nil
}
var __1623938329_add_author_activity_center_notification_fieldUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\x4c\x2e\xc9\x2c\xcb\x2c\xa9\x8c\x4f\x4e\xcd\x2b\x49\x2d\x8a\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x2b\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x48\x2c\x2d\xc9\xc8\x2f\x52\x08\x71\x8d\x08\xb1\xe6\x02\x04\x00\x00\xff\xff\xc4\xaa\x64\x1f\x42\x00\x00\x00") var __1623938329_add_author_activity_center_notification_fieldUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\x4c\x2e\xc9\x2c\xcb\x2c\xa9\x8c\x4f\x4e\xcd\x2b\x49\x2d\x8a\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x2b\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x48\x2c\x2d\xc9\xc8\x2f\x52\x08\x71\x8d\x08\xb1\xe6\x02\x04\x00\x00\xff\xff\xc4\xaa\x64\x1f\x42\x00\x00\x00")
func _1623938329_add_author_activity_center_notification_fieldUpSqlBytes() ([]byte, error) { func _1623938329_add_author_activity_center_notification_fieldUpSqlBytes() ([]byte, error) {
@ -868,6 +848,26 @@ func _1623938329_add_author_activity_center_notification_fieldUpSql() (*asset, e
return a, nil return a, nil
} }
var __1623938330_add_edit_messagesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x8f\xc1\x8a\x83\x30\x14\x45\xf7\xf9\x8a\xb7\x54\xf0\x0f\x5c\xbd\xd1\xc7\x8c\x4c\x8c\x25\xc4\x52\x57\x41\xcc\xa3\x95\xb6\x08\x26\x42\x3f\xbf\x08\x16\x29\xc4\xf5\xb9\xf7\x72\x2e\x4a\x43\x1a\x0c\xfe\x48\x82\xc5\xf3\x6c\x9f\xec\x7d\x7f\x65\x0f\x58\x96\x50\x34\xb2\xad\x15\xb0\x1b\x03\x3b\xdb\x07\xa8\x94\xa1\x5f\xd2\xb9\x10\x85\x26\x34\x14\x6b\xda\x35\xee\x21\x11\x00\xc3\x63\x1a\xee\x9f\x12\xa8\xc6\x80\x6a\xa5\xcc\x56\x72\xeb\x83\x1d\x1d\x9c\x51\x17\x7f\xf8\xcd\xb6\xa1\x23\xec\xa7\x65\x1e\x38\x8a\x02\xbf\x42\x14\x1c\x4c\x9d\x74\x55\xa3\xee\xe0\x9f\xba\x64\x74\xa9\x48\xf7\x63\x95\x2a\xe9\x12\x3b\x66\x77\x3d\xbb\xa9\x34\x2a\x16\x4c\xf6\x60\xb6\x49\xa7\xb9\x78\x07\x00\x00\xff\xff\x6a\x1b\xa7\x21\x71\x01\x00\x00")
func _1623938330_add_edit_messagesUpSqlBytes() ([]byte, error) {
return bindataRead(
__1623938330_add_edit_messagesUpSql,
"1623938330_add_edit_messages.up.sql",
)
}
func _1623938330_add_edit_messagesUpSql() (*asset, error) {
bytes, err := _1623938330_add_edit_messagesUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1623938330_add_edit_messages.up.sql", size: 369, mode: os.FileMode(0644), modTime: time.Unix(1624368052, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x7e, 0xd2, 0xce, 0xe, 0x5c, 0x19, 0xbe, 0x5e, 0x29, 0xbe, 0x9b, 0x31, 0x53, 0x76, 0xb2, 0xc8, 0x56, 0xf0, 0x82, 0xfe, 0x7d, 0x6c, 0xe8, 0x5c, 0xe9, 0x7a, 0x5d, 0x5, 0xc4, 0x92, 0x38, 0xe3}}
return a, nil
}
var _readmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x91\xc1\xce\xd3\x30\x10\x84\xef\x7e\x8a\x91\x7a\x01\xa9\x2a\x8f\xc0\x0d\x71\x82\x03\x48\x1c\xc9\x36\x9e\x36\x96\x1c\x6f\xf0\xae\x93\xe6\xed\x91\xa3\xc2\xdf\xff\x66\xed\xd8\x33\xdf\x78\x4f\xa7\x13\xbe\xea\x06\x57\x6c\x35\x39\x31\xa7\x7b\x15\x4f\x5a\xec\x73\x08\xbf\x08\x2d\x79\x7f\x4a\x43\x5b\x86\x17\xfd\x8c\x21\xea\x56\x5e\x47\x90\x4a\x14\x75\x48\xde\x64\x37\x2c\x6a\x96\xae\x99\x48\x05\xf6\x27\x77\x13\xad\x08\xae\x8a\x51\xe7\x25\xf3\xf1\xa9\x9f\xf9\x58\x58\x2c\xad\xbc\xe0\x8b\x56\xf0\x21\x5d\xeb\x4c\x95\xb3\xae\x84\x60\xd4\xdc\xe6\x82\x5d\x1b\x36\x6d\x39\x62\x92\xf5\xb8\x11\xdb\x92\xd3\x28\xce\xe0\x13\xe1\x72\xcd\x3c\x63\xd4\x65\x87\xae\xac\xe8\xc3\x28\x2e\x67\x44\x66\x3a\x21\x25\xa2\x72\xac\x14\x67\xbc\x84\x9f\x53\x32\x8c\x52\x70\x25\x56\xd6\xfd\x8d\x05\x37\xad\x30\x9d\x9f\xa6\x86\x0f\xcd\x58\x7f\xcf\x34\x93\x3b\xed\x90\x9f\xa4\x1f\xcf\x30\x85\x4d\x07\x58\xaf\x7f\x25\xc4\x9d\xf3\x72\x64\x84\xd0\x7f\xf9\x9b\x3a\x2d\x84\xef\x85\x48\x66\x8d\xd8\x88\x9b\x8c\x8c\x98\x5b\xf6\x74\x14\x4e\x33\x0d\xc9\xe0\x93\x38\xda\x12\xc5\x69\xbd\xe4\xf0\x2e\x7a\x78\x07\x1c\xfe\x13\x9f\x91\x29\x31\x95\x7b\x7f\x62\x59\x37\xb4\xe5\x5e\x25\xfe\x33\xee\xd5\x53\x71\xd6\xda\x3a\xd8\xcb\xde\x2e\xf8\xa1\x90\x55\x53\x0c\xc7\xaa\x0d\xe9\x76\x14\x29\x1c\x7b\x68\xdd\x2f\xe1\x6f\x00\x00\x00\xff\xff\x3c\x0a\xc2\xfe\x2a\x02\x00\x00") var _readmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x91\xc1\xce\xd3\x30\x10\x84\xef\x7e\x8a\x91\x7a\x01\xa9\x2a\x8f\xc0\x0d\x71\x82\x03\x48\x1c\xc9\x36\x9e\x36\x96\x1c\x6f\xf0\xae\x93\xe6\xed\x91\xa3\xc2\xdf\xff\x66\xed\xd8\x33\xdf\x78\x4f\xa7\x13\xbe\xea\x06\x57\x6c\x35\x39\x31\xa7\x7b\x15\x4f\x5a\xec\x73\x08\xbf\x08\x2d\x79\x7f\x4a\x43\x5b\x86\x17\xfd\x8c\x21\xea\x56\x5e\x47\x90\x4a\x14\x75\x48\xde\x64\x37\x2c\x6a\x96\xae\x99\x48\x05\xf6\x27\x77\x13\xad\x08\xae\x8a\x51\xe7\x25\xf3\xf1\xa9\x9f\xf9\x58\x58\x2c\xad\xbc\xe0\x8b\x56\xf0\x21\x5d\xeb\x4c\x95\xb3\xae\x84\x60\xd4\xdc\xe6\x82\x5d\x1b\x36\x6d\x39\x62\x92\xf5\xb8\x11\xdb\x92\xd3\x28\xce\xe0\x13\xe1\x72\xcd\x3c\x63\xd4\x65\x87\xae\xac\xe8\xc3\x28\x2e\x67\x44\x66\x3a\x21\x25\xa2\x72\xac\x14\x67\xbc\x84\x9f\x53\x32\x8c\x52\x70\x25\x56\xd6\xfd\x8d\x05\x37\xad\x30\x9d\x9f\xa6\x86\x0f\xcd\x58\x7f\xcf\x34\x93\x3b\xed\x90\x9f\xa4\x1f\xcf\x30\x85\x4d\x07\x58\xaf\x7f\x25\xc4\x9d\xf3\x72\x64\x84\xd0\x7f\xf9\x9b\x3a\x2d\x84\xef\x85\x48\x66\x8d\xd8\x88\x9b\x8c\x8c\x98\x5b\xf6\x74\x14\x4e\x33\x0d\xc9\xe0\x93\x38\xda\x12\xc5\x69\xbd\xe4\xf0\x2e\x7a\x78\x07\x1c\xfe\x13\x9f\x91\x29\x31\x95\x7b\x7f\x62\x59\x37\xb4\xe5\x5e\x25\xfe\x33\xee\xd5\x53\x71\xd6\xda\x3a\xd8\xcb\xde\x2e\xf8\xa1\x90\x55\x53\x0c\xc7\xaa\x0d\xe9\x76\x14\x29\x1c\x7b\x68\xdd\x2f\xe1\x6f\x00\x00\x00\xff\xff\x3c\x0a\xc2\xfe\x2a\x02\x00\x00")
func readmeMdBytes() ([]byte, error) { func readmeMdBytes() ([]byte, error) {
@ -1071,10 +1071,10 @@ var _bindata = map[string]func() (*asset, error){
"1622622253_add_pinned_by_to_pin_messages.up.sql": _1622622253_add_pinned_by_to_pin_messagesUpSql, "1622622253_add_pinned_by_to_pin_messages.up.sql": _1622622253_add_pinned_by_to_pin_messagesUpSql,
"1622722745_add_original_message_id.up.sql": _1622722745_add_original_message_idUpSql,
"1623938329_add_author_activity_center_notification_field.up.sql": _1623938329_add_author_activity_center_notification_fieldUpSql, "1623938329_add_author_activity_center_notification_field.up.sql": _1623938329_add_author_activity_center_notification_fieldUpSql,
"1623938330_add_edit_messages.up.sql": _1623938330_add_edit_messagesUpSql,
"README.md": readmeMd, "README.md": readmeMd,
"doc.go": docGo, "doc.go": docGo,
@ -1157,10 +1157,10 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1622464518_set_synced_to_from.up.sql": &bintree{_1622464518_set_synced_to_fromUpSql, map[string]*bintree{}}, "1622464518_set_synced_to_from.up.sql": &bintree{_1622464518_set_synced_to_fromUpSql, map[string]*bintree{}},
"1622464519_add_chat_description.up.sql": &bintree{_1622464519_add_chat_descriptionUpSql, map[string]*bintree{}}, "1622464519_add_chat_description.up.sql": &bintree{_1622464519_add_chat_descriptionUpSql, map[string]*bintree{}},
"1622622253_add_pinned_by_to_pin_messages.up.sql": &bintree{_1622622253_add_pinned_by_to_pin_messagesUpSql, map[string]*bintree{}}, "1622622253_add_pinned_by_to_pin_messages.up.sql": &bintree{_1622622253_add_pinned_by_to_pin_messagesUpSql, map[string]*bintree{}},
"1622722745_add_original_message_id.up.sql": &bintree{_1622722745_add_original_message_idUpSql, map[string]*bintree{}},
"1623938329_add_author_activity_center_notification_field.up.sql": &bintree{_1623938329_add_author_activity_center_notification_fieldUpSql, map[string]*bintree{}}, "1623938329_add_author_activity_center_notification_field.up.sql": &bintree{_1623938329_add_author_activity_center_notification_fieldUpSql, map[string]*bintree{}},
"README.md": &bintree{readmeMd, map[string]*bintree{}}, "1623938330_add_edit_messages.up.sql": &bintree{_1623938330_add_edit_messagesUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}}, "README.md": &bintree{readmeMd, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}} }}
// RestoreAsset restores an asset under the given directory. // RestoreAsset restores an asset under the given directory.

View File

@ -9,3 +9,5 @@ CREATE TABLE user_messages_edits (
id VARCHAR NOT NULL, id VARCHAR NOT NULL,
PRIMARY KEY(id) PRIMARY KEY(id)
); );
CREATE INDEX user_messages_edits_message_id_source ON user_messages_edits(message_id, source);

View File

@ -591,6 +591,10 @@ func (api *PublicAPI) SendChatMessages(ctx context.Context, messages []*common.M
return api.service.messenger.SendChatMessages(ctx, messages) return api.service.messenger.SendChatMessages(ctx, messages)
} }
func (api *PublicAPI) EditMessage(ctx context.Context, request *requests.EditMessage) (*protocol.MessengerResponse, error) {
return api.service.messenger.EditMessage(ctx, request)
}
func (api *PublicAPI) SendPinMessage(ctx context.Context, message *common.PinMessage) (*protocol.MessengerResponse, error) { func (api *PublicAPI) SendPinMessage(ctx context.Context, message *common.PinMessage) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendPinMessage(ctx, message) return api.service.messenger.SendPinMessage(ctx, message)
} }