diff --git a/VERSION b/VERSION index 36545ad33..da011ce41 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.92.0 +0.92.1 diff --git a/protocol/messenger.go b/protocol/messenger.go index 8a0e0a5d1..ed202a59d 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -64,8 +64,8 @@ const ( privateChat chatContext = "private-chat" ) -const emojiResendMinDelay = 30 -const emojiResendMaxCount = 3 +const messageResendMinDelay = 30 +const messageResendMaxCount = 3 var communityAdvertiseIntervalSecond int64 = 60 * 60 @@ -140,9 +140,16 @@ func (interceptor EnvelopeEventsInterceptor) EnvelopeSent(identifiers [][]byte) err := interceptor.Messenger.processSentMessages(ids) if err != nil { interceptor.Messenger.logger.Info("Messenger failed to process sent messages", zap.Error(err)) + } else { + interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers) } + } else { + // NOTE(rasom): In case if interceptor.Messenger is not nil and + // some error occurred on processing sent message we don't want + // to send envelop.sent signal to the client, thus `else` cause + // is necessary. + interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers) } - interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers) } // EnvelopeExpired triggered when envelope is expired but wasn't delivered to any peer. @@ -404,6 +411,10 @@ func NewMessenger( } func (m *Messenger) processSentMessages(ids []string) error { + if m.connectionState.Offline { + return errors.New("Can't mark message as sent while offline") + } + for _, id := range ids { rawMessage, err := m.persistence.RawMessageByID(id) if err != nil { @@ -416,32 +427,42 @@ func (m *Messenger) processSentMessages(ids []string) error { if err != nil { return errors.Wrapf(err, "Can't save raw message marked as sent") } + + err = m.UpdateMessageOutgoingStatus(id, "sent") + if err != nil { + return err + } } return nil } -func shouldResendEmojiReaction(message *common.RawMessage, t common.TimeSource) (bool, error) { - if message.MessageType != protobuf.ApplicationMetadataMessage_EMOJI_REACTION { - return false, errors.New("Should resend only emoji reactions") +func shouldResendMessage(message *common.RawMessage, t common.TimeSource) (bool, error) { + if !(message.MessageType == protobuf.ApplicationMetadataMessage_EMOJI_REACTION || + message.MessageType == protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) { + return false, errors.Errorf("Should resend only specific types of messages, can't resend %v", message.MessageType) } if message.Sent { return false, errors.New("Should resend only non-sent messages") } - if message.SendCount > emojiResendMaxCount { + if message.SendCount > messageResendMaxCount { return false, nil } //exponential backoff depends on how many attempts to send message already made - backoff := uint64(math.Pow(2, float64(message.SendCount-1))) * emojiResendMinDelay * uint64(time.Second) + backoff := uint64(math.Pow(2, float64(message.SendCount-1))) * messageResendMinDelay * uint64(time.Second.Milliseconds()) backoffElapsed := t.GetCurrentTime() > (message.LastSent + backoff) return backoffElapsed, nil } -func (m *Messenger) resendExpiredEmojiReactions() error { - ids, err := m.persistence.ExpiredEmojiReactionsIDs(emojiResendMaxCount) +func (m *Messenger) resendExpiredMessages() error { + if m.connectionState.Offline { + return errors.New("offline") + } + + ids, err := m.persistence.ExpiredMessagesIDs(messageResendMaxCount) if err != nil { return errors.Wrapf(err, "Can't get expired reactions from db") } @@ -452,7 +473,21 @@ func (m *Messenger) resendExpiredEmojiReactions() error { return errors.Wrapf(err, "Can't get raw message with id %v", id) } - if ok, err := shouldResendEmojiReaction(rawMessage, m.getTimesource()); ok { + chat, ok := m.allChats.Load(rawMessage.LocalChatID) + if !ok { + return ErrChatNotFound + } + + if !(chat.Public() || chat.CommunityChat()) { + return errors.New("Only public chats and community chats messages are resent") + } + + ok, err = shouldResendMessage(rawMessage, m.getTimesource()) + if err != nil { + return err + } + + if ok { err = m.persistence.SaveRawMessage(rawMessage) if err != nil { return errors.Wrapf(err, "Can't save raw message marked as non-expired") @@ -462,8 +497,6 @@ func (m *Messenger) resendExpiredEmojiReactions() error { if err != nil { return errors.Wrapf(err, "Can't resend expired message with id %v", rawMessage.ID) } - } else { - return err } } return nil @@ -524,7 +557,7 @@ func (m *Messenger) Start() (*MessengerResponse, error) { m.handleConnectionChange(m.online()) m.handleENSVerificationSubscription(ensSubscription) m.watchConnectionChange() - m.watchExpiredEmojis() + m.watchExpiredMessages() m.watchIdentityImageChanges() m.broadcastLatestUserStatus() m.startBackupLoop() @@ -998,15 +1031,15 @@ func (m *Messenger) watchConnectionChange() { }() } -// watchExpiredEmojis regularly checks for expired emojis and invoke their resending -func (m *Messenger) watchExpiredEmojis() { - m.logger.Debug("watching expired emojis") +// watchExpiredMessages regularly checks for expired emojis and invoke their resending +func (m *Messenger) watchExpiredMessages() { + m.logger.Debug("watching expired messages") go func() { for { select { case <-time.After(time.Second): if m.online() { - err := m.resendExpiredEmojiReactions() + err := m.resendExpiredMessages() if err != nil { m.logger.Debug("Error when resending expired emoji reactions", zap.Error(err)) } diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index 4a16c98aa..a81e44f41 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -2242,7 +2242,7 @@ func (s *MessengerSuite) TestLastSentField() { func (s *MessengerSuite) TestShouldResendEmoji() { // shouldn't try to resend non-emoji messages. - ok, err := shouldResendEmojiReaction(&common.RawMessage{ + ok, err := shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE, Sent: false, SendCount: 2, @@ -2251,7 +2251,7 @@ func (s *MessengerSuite) TestShouldResendEmoji() { s.False(ok) // shouldn't try to resend already sent message - ok, err = shouldResendEmojiReaction(&common.RawMessage{ + ok, err = shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION, Sent: true, SendCount: 1, @@ -2260,50 +2260,50 @@ func (s *MessengerSuite) TestShouldResendEmoji() { s.False(ok) // messages that already sent to many times shouldn't be resend - ok, err = shouldResendEmojiReaction(&common.RawMessage{ + ok, err = shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION, Sent: false, - SendCount: emojiResendMaxCount + 1, + SendCount: messageResendMaxCount + 1, }, s.m.getTimesource()) s.NoError(err) s.False(ok) // message sent one time CAN'T be resend in 15 seconds (only after 30) - ok, err = shouldResendEmojiReaction(&common.RawMessage{ + ok, err = shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION, Sent: false, SendCount: 1, - LastSent: s.m.getTimesource().GetCurrentTime() - 15*uint64(time.Second), + LastSent: s.m.getTimesource().GetCurrentTime() - 15*uint64(time.Second.Milliseconds()), }, s.m.getTimesource()) s.NoError(err) s.False(ok) // message sent one time CAN be resend in 35 seconds - ok, err = shouldResendEmojiReaction(&common.RawMessage{ + ok, err = shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION, Sent: false, SendCount: 1, - LastSent: s.m.getTimesource().GetCurrentTime() - 35*uint64(time.Second), + LastSent: s.m.getTimesource().GetCurrentTime() - 35*uint64(time.Second.Milliseconds()), }, s.m.getTimesource()) s.NoError(err) s.True(ok) // message sent three times CAN'T be resend in 100 seconds (only after 120) - ok, err = shouldResendEmojiReaction(&common.RawMessage{ + ok, err = shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION, Sent: false, SendCount: 3, - LastSent: s.m.getTimesource().GetCurrentTime() - 100*uint64(time.Second), + LastSent: s.m.getTimesource().GetCurrentTime() - 100*uint64(time.Second.Milliseconds()), }, s.m.getTimesource()) s.NoError(err) s.False(ok) // message sent tow times CAN be resend in 65 seconds - ok, err = shouldResendEmojiReaction(&common.RawMessage{ + ok, err = shouldResendMessage(&common.RawMessage{ MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION, Sent: false, SendCount: 3, - LastSent: s.m.getTimesource().GetCurrentTime() - 125*uint64(time.Second), + LastSent: s.m.getTimesource().GetCurrentTime() - 125*uint64(time.Second.Milliseconds()), }, s.m.getTimesource()) s.NoError(err) s.True(ok) @@ -2359,7 +2359,7 @@ func (s *MessengerSuite) TestResendExpiredEmojis() { s.Equal(1, rawMessage.SendCount) //imitate that more than 30 seconds passed since message was sent - rawMessage.LastSent = rawMessage.LastSent - 35*uint64(time.Second) + rawMessage.LastSent = rawMessage.LastSent - 35*uint64(time.Second.Milliseconds()) err = s.m.persistence.SaveRawMessage(rawMessage) s.NoError(err) time.Sleep(2 * time.Second) diff --git a/protocol/persistence.go b/protocol/persistence.go index dd3a8ec8f..c3f869020 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -643,7 +643,7 @@ func (db sqlitePersistence) SaveContactChatIdentity(contactID string, chatIdenti return } -func (db sqlitePersistence) ExpiredEmojiReactionsIDs(maxSendCount int) ([]string, error) { +func (db sqlitePersistence) ExpiredMessagesIDs(maxSendCount int) ([]string, error) { ids := []string{} rows, err := db.db.Query(` @@ -652,8 +652,11 @@ func (db sqlitePersistence) ExpiredEmojiReactionsIDs(maxSendCount int) ([]string FROM raw_messages WHERE - message_type = ? AND sent = ? AND send_count <= ?`, - protobuf.ApplicationMetadataMessage_EMOJI_REACTION, false, maxSendCount) + message_type IN (?, ?) AND sent = ? AND send_count <= ?`, + protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, + protobuf.ApplicationMetadataMessage_EMOJI_REACTION, + false, + maxSendCount) if err != nil { return ids, err } diff --git a/protocol/persistence_test.go b/protocol/persistence_test.go index c77754698..e38a80a88 100644 --- a/protocol/persistence_test.go +++ b/protocol/persistence_test.go @@ -533,23 +533,12 @@ func TestMessagesIDsByType(t *testing.T) { require.Equal(t, "emoji-message-id", ids[0]) } -func TestExpiredEmojiReactionsIDs(t *testing.T) { +func TestExpiredMessagesIDs(t *testing.T) { db, err := openTestDB() require.NoError(t, err) p := NewSQLitePersistence(db) - ids, err := p.ExpiredEmojiReactionsIDs(emojiResendMaxCount) - require.NoError(t, err) - require.Empty(t, ids) - - //save expired chat message - rawChatMessage := minimalRawMessage("chat-message-id", protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) - rawChatMessage.Sent = false - err = p.SaveRawMessage(rawChatMessage) - require.NoError(t, err) - - //make sure it doesn't apper in an expired emoji reactions list - ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount) + ids, err := p.ExpiredMessagesIDs(messageResendMaxCount) require.NoError(t, err) require.Empty(t, ids) @@ -560,7 +549,7 @@ func TestExpiredEmojiReactionsIDs(t *testing.T) { require.NoError(t, err) //make sure it appered in expired emoji reactions list - ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount) + ids, err = p.ExpiredMessagesIDs(messageResendMaxCount) require.NoError(t, err) require.Equal(t, 1, len(ids)) @@ -571,7 +560,7 @@ func TestExpiredEmojiReactionsIDs(t *testing.T) { require.NoError(t, err) //make sure it didn't appear in expired emoji reactions list - ids, err = p.ExpiredEmojiReactionsIDs(emojiResendMaxCount) + ids, err = p.ExpiredMessagesIDs(messageResendMaxCount) require.NoError(t, err) require.Equal(t, 1, len(ids)) }