From 62f342c2e9766be15100b9f67bcb6b5e622be752 Mon Sep 17 00:00:00 2001 From: Icaro Motta Date: Mon, 29 Jul 2024 14:22:15 -0300 Subject: [PATCH] feat_: Check for mobile data connection if setting is on (#5511) (#5613) * feat_: Check for mobile data connection if setting is on * fix_: check code control flag inside asyncRequestAllHistoricMessages --------- Co-authored-by: Andrea Maria Piana Co-authored-by: Igor Sirotin --- multiaccounts/settings/database.go | 8 + .../settings/database_settings_manager.go | 1 + .../mocks/database_info_retriever_mock.go | 8 + protocol/messenger.go | 4 +- protocol/messenger_communities.go | 3 + protocol/messenger_mailserver.go | 68 +++++++- protocol/messenger_mailserver_cycle.go | 10 +- protocol/messenger_settings.go | 14 ++ protocol/push_notification_test.go | 159 ++++++++++++++++++ protocol/pushnotificationclient/client.go | 44 +++++ .../requests/set_syncing_on_mobile_network.go | 9 + services/ext/api.go | 4 + 12 files changed, 321 insertions(+), 11 deletions(-) create mode 100644 protocol/requests/set_syncing_on_mobile_network.go diff --git a/multiaccounts/settings/database.go b/multiaccounts/settings/database.go index 9d791b468..61c5a13d0 100644 --- a/multiaccounts/settings/database.go +++ b/multiaccounts/settings/database.go @@ -767,6 +767,14 @@ func (db *Database) SetPeerSyncingEnabled(value bool) error { return db.SaveSettingField(PeerSyncingEnabled, value) } +func (db *Database) SetSyncingOnMobileNetwork(value bool) error { + err := db.SaveSettingField(SyncingOnMobileNetwork, value) + if err != nil { + return err + } + return db.SaveSettingField(RememberSyncingChoice, true) +} + func (db *Database) GetPeerSyncingEnabled() (result bool, err error) { err = db.makeSelectRow(PeerSyncingEnabled).Scan(&result) if err == sql.ErrNoRows { diff --git a/multiaccounts/settings/database_settings_manager.go b/multiaccounts/settings/database_settings_manager.go index 599ac74de..7a09c940f 100644 --- a/multiaccounts/settings/database_settings_manager.go +++ b/multiaccounts/settings/database_settings_manager.go @@ -50,6 +50,7 @@ type DatabaseSettingsManager interface { SetUseMailservers(value bool) error SetTokenGroupByCommunity(value bool) error SetPeerSyncingEnabled(value bool) error + SetSyncingOnMobileNetwork(value bool) error CreateSettings(s Settings, n params.NodeConfig) error SaveSetting(setting string, value interface{}) error diff --git a/multiaccounts/settings/mocks/database_info_retriever_mock.go b/multiaccounts/settings/mocks/database_info_retriever_mock.go index f9f7745f0..4cb1c86d1 100644 --- a/multiaccounts/settings/mocks/database_info_retriever_mock.go +++ b/multiaccounts/settings/mocks/database_info_retriever_mock.go @@ -206,6 +206,14 @@ func (m *MockDatabaseSettingsManager) SetPeerSyncingEnabled(value bool) error { return ret0 } +// SetSyncingOnMobileNetwork mocks base method. +func (m *MockDatabaseSettingsManager) SetSyncingOnMobileNetwork(value bool) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetSyncingOnMobile", value) + ret0, _ := ret[0].(error) + return ret0 +} + // SetPeerSyncingEnabled indicates an expected call of SetPeerSyncingEnabled. func (mr *MockDatabaseSettingsManagerMockRecorder) SetPeerSyncingEnabled(value interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() diff --git a/protocol/messenger.go b/protocol/messenger.go index 9b6a22cba..7f7ff65bd 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -740,6 +740,8 @@ func (m *Messenger) ToForeground() { if m.httpServer != nil { m.httpServer.ToForeground() } + + m.asyncRequestAllHistoricMessages() } func (m *Messenger) ToBackground() { @@ -1005,7 +1007,7 @@ func (m *Messenger) handleConnectionChange(online bool) { } // Start fetching messages from store nodes - if online && m.config.codeControlFlags.AutoRequestHistoricMessages { + if online { m.asyncRequestAllHistoricMessages() } diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index aacffc442..bd2ea46cf 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -1205,6 +1205,9 @@ func (m *Messenger) SpectateCommunity(communityID types.HexBytes) (*MessengerRes return nil, err } + // sync community + m.asyncRequestAllHistoricMessages() + return response, nil } diff --git a/protocol/messenger_mailserver.go b/protocol/messenger_mailserver.go index 4290790b1..9de221065 100644 --- a/protocol/messenger_mailserver.go +++ b/protocol/messenger_mailserver.go @@ -261,12 +261,19 @@ func (m *Messenger) syncBackup() error { if filter == nil { return errors.New("personal topic filter not loaded") } + canSync, err := m.canSyncWithStoreNodes() + if err != nil { + return err + } + if !canSync { + return nil + } from, to := m.calculateMailserverTimeBounds(oneMonthDuration) batch := MailserverBatch{From: from, To: to, Topics: []types.TopicType{filter.ContentTopic}} ms := m.getActiveMailserver(filter.ChatID) - err := m.processMailserverBatch(*ms, batch) + err = m.processMailserverBatch(*ms, batch) if err != nil { return err } @@ -365,16 +372,20 @@ func (m *Messenger) RequestAllHistoricMessages(forceFetchingBackup, withRetries if err != nil { return nil, err } - allResponses.AddChats(response.Chats()) - allResponses.AddMessages(response.Messages()) + if response != nil { + allResponses.AddChats(response.Chats()) + allResponses.AddMessages(response.Messages()) + } continue } response, err := m.syncFilters(*ms, filtersForMs) if err != nil { return nil, err } - allResponses.AddChats(response.Chats()) - allResponses.AddMessages(response.Messages()) + if response != nil { + allResponses.AddChats(response.Chats()) + allResponses.AddMessages(response.Messages()) + } } return allResponses, nil } @@ -415,6 +426,14 @@ func getPrioritizedBatches() []int { } func (m *Messenger) syncFiltersFrom(ms mailservers.Mailserver, filters []*transport.Filter, lastRequest uint32) (*MessengerResponse, error) { + canSync, err := m.canSyncWithStoreNodes() + if err != nil { + return nil, err + } + if !canSync { + return nil, nil + } + response := &MessengerResponse{} topicInfo, err := m.mailserversDatabase.Topics() if err != nil { @@ -866,12 +885,27 @@ loop: return result } +func (m *Messenger) canSyncWithStoreNodes() (bool, error) { + if m.featureFlags.StoreNodesDisabled { + return false, nil + } + if m.connectionState.IsExpensive() { + return m.settings.CanSyncOnMobileNetwork() + } + + return true, nil +} + func (m *Messenger) DisableStoreNodes() { m.featureFlags.StoreNodesDisabled = true } func (m *Messenger) processMailserverBatch(ms mailservers.Mailserver, batch MailserverBatch) error { - if m.featureFlags.StoreNodesDisabled { + canSync, err := m.canSyncWithStoreNodes() + if err != nil { + return err + } + if !canSync { return nil } @@ -884,7 +918,11 @@ func (m *Messenger) processMailserverBatch(ms mailservers.Mailserver, batch Mail } func (m *Messenger) processMailserverBatchWithOptions(ms mailservers.Mailserver, batch MailserverBatch, pageLimit uint32, shouldProcessNextPage func(int) (bool, uint32), processEnvelopes bool) error { - if m.featureFlags.StoreNodesDisabled { + canSync, err := m.canSyncWithStoreNodes() + if err != nil { + return err + } + if !canSync { return nil } @@ -914,6 +952,14 @@ func (m *Messenger) SyncChatFromSyncedFrom(chatID string) (uint32, error) { ms := m.getActiveMailserver(chat.CommunityID) var from uint32 _, err := m.performMailserverRequest(ms, func(ms mailservers.Mailserver) (*MessengerResponse, error) { + canSync, err := m.canSyncWithStoreNodes() + if err != nil { + return nil, err + } + if !canSync { + return nil, nil + } + pubsubTopic, topics, err := m.topicsForChat(chatID) if err != nil { return nil, nil @@ -1094,6 +1140,14 @@ func (m *Messenger) fetchMessages(chatID string, duration time.Duration) (uint32 ms := m.getActiveMailserver(chat.CommunityID) _, err := m.performMailserverRequest(ms, func(ms mailservers.Mailserver) (*MessengerResponse, error) { + canSync, err := m.canSyncWithStoreNodes() + if err != nil { + return nil, err + } + if !canSync { + return nil, nil + } + m.logger.Debug("fetching messages", zap.String("chatID", chatID), zap.String("mailserver", ms.Name)) pubsubTopic, topics, err := m.topicsForChat(chatID) if err != nil { diff --git a/protocol/messenger_mailserver_cycle.go b/protocol/messenger_mailserver_cycle.go index 53861757a..fe8342ea5 100644 --- a/protocol/messenger_mailserver_cycle.go +++ b/protocol/messenger_mailserver_cycle.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/status-im/status-go/params" "github.com/status-im/status-go/protocol/storenodes" "github.com/status-im/status-go/services/mailservers" @@ -433,9 +434,7 @@ func (m *Messenger) connectToMailserver(ms mailservers.Mailserver) error { m.transport.SetStorePeerID(peerID) // Query mailserver - if m.config.codeControlFlags.AutoRequestHistoricMessages { - m.asyncRequestAllHistoricMessages() - } + m.asyncRequestAllHistoricMessages() } } return nil @@ -623,7 +622,12 @@ func (m *Messenger) handleMailserverCycleEvent(connectedPeers []ConnectedPeer) e } func (m *Messenger) asyncRequestAllHistoricMessages() { + if !m.config.codeControlFlags.AutoRequestHistoricMessages { + return + } + m.logger.Debug("asyncRequestAllHistoricMessages") + go func() { _, err := m.RequestAllHistoricMessages(false, true) if err != nil { diff --git a/protocol/messenger_settings.go b/protocol/messenger_settings.go index 8815da546..b42337862 100644 --- a/protocol/messenger_settings.go +++ b/protocol/messenger_settings.go @@ -16,6 +16,20 @@ func (m *Messenger) SetStoreConfirmationForMessagesSent(request *requests.SetSto return nodecfg.SetStoreConfirmationForMessagesSent(m.database, request.Enabled) } +func (m *Messenger) SetSyncingOnMobileNetwork(request *requests.SetSyncingOnMobileNetwork) error { + if err := request.Validate(); err != nil { + return err + } + err := m.settings.SetSyncingOnMobileNetwork(request.Enabled) + if err != nil { + return err + } + if request.Enabled { + m.asyncRequestAllHistoricMessages() + } + return nil +} + func (m *Messenger) SetLogLevel(request *requests.SetLogLevel) error { if err := request.Validate(); err != nil { return err diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 290594e55..048a104da 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -1144,3 +1144,162 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationPairedDevice }) s.Require().NoError(err) } + +func (s *MessengerPushNotificationSuite) TestReceivePushNotificationReply() { + + bob := s.m + + serverKey, err := crypto.GenerateKey() + s.Require().NoError(err) + server := s.newPushNotificationServer(s.shh, serverKey) + defer TearDownMessenger(&s.Suite, server) + + alice := s.newMessenger(s.shh) + s.Require().NoError(err) + defer TearDownMessenger(&s.Suite, alice) + s.Require().NoError(alice.EnableSendingPushNotifications()) + bobInstallationIDs := []string{bob.installationID} + + // Create public chat and join for both alice and bob + chat := CreatePublicChat("status", s.m.transport) + err = bob.SaveChat(chat) + s.Require().NoError(err) + + _, err = bob.Join(chat) + s.Require().NoError(err) + + err = alice.SaveChat(chat) + s.Require().NoError(err) + + _, err = alice.Join(chat) + s.Require().NoError(err) + + // Register bob + err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom) + s.Require().NoError(err) + + err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN) + + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob.RetrieveAll() + if err != nil { + return err + } + registered, err := bob.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + + bobServers, err := bob.GetPushNotificationsServers() + if err != nil { + return err + } + + if len(bobServers) == 0 { + return errors.New("not registered") + } + + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bobServers, err := bob.GetPushNotificationsServers() + s.Require().NoError(err) + + firstMessage := buildTestMessage(*chat) + firstMessage.Text = "Hello!" + response, err := bob.SendChatMessage(context.Background(), firstMessage) + s.Require().NoError(err) + messageIDString := response.Messages()[0].ID + + _, err = WaitOnMessengerResponse( + alice, + func(r *MessengerResponse) bool { + for _, message := range r.Messages() { + if message.ID == messageIDString { + return true + } + } + return false + + }, + "no messages", + ) + + replyMessage := buildTestMessage(*chat) + replyMessage.Text = "Hello reply" + replyMessage.ResponseTo = messageIDString + response, err = alice.SendChatMessage(context.Background(), replyMessage) + s.Require().NoError(err) + messageIDString = response.Messages()[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) + s.Require().NoError(err) + + var bobInfo []*pushnotificationclient.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + + bobInfo, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for bob + if len(bobInfo) != 1 { + return errors.New("info not fetched") + } + return nil + + }) + + s.Require().NoError(err) + + s.Require().NotEmpty(bobInfo) + s.Require().Equal(bob.installationID, bobInfo[0].InstallationID) + s.Require().Equal(bobServers[0].AccessToken, bobInfo[0].AccessToken) + s.Require().Equal(&bob.identity.PublicKey, bobInfo[0].PublicKey) + + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + + s.Require().NoError(err) + s.Require().NotNil(retrievedNotificationInfo) + s.Require().Len(retrievedNotificationInfo, 1) + + var sentNotification *pushnotificationclient.SentNotification + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID) + if err != nil { + return err + } + if sentNotification == nil { + return errors.New("sent notification not found") + } + if !sentNotification.Success { + return errors.New("sent notification not successul") + } + return nil + }) + s.Require().NoError(err) +} diff --git a/protocol/pushnotificationclient/client.go b/protocol/pushnotificationclient/client.go index b292047ff..d06fd635d 100644 --- a/protocol/pushnotificationclient/client.go +++ b/protocol/pushnotificationclient/client.go @@ -907,6 +907,50 @@ func (c *Client) handlePublicMessageSent(sentMessage *common.SentMessage) error } c.config.Logger.Debug("message found", zap.Binary("messageID", messageID)) + + if message.ResponseTo != "" { + reply, err := c.getMessage(message.ResponseTo) + if err != nil { + c.config.Logger.Error("could not retrieve message", zap.Error(err)) + } + if reply != nil { + pkString := reply.From + c.config.Logger.Debug("handling mention", zap.String("publickey", pkString)) + pubkeyBytes, err := types.DecodeHex(pkString) + if err != nil { + return err + } + + publicKey, err := crypto.UnmarshalPubkey(pubkeyBytes) + if err != nil { + return err + } + + // we use a synthetic installationID for mentions, as all devices need to be notified + shouldNotify, err := c.shouldNotifyOn(publicKey, mentionInstallationID, messageID) + if err != nil { + return err + } + + c.config.Logger.Debug("should no mention", zap.Any("publickey", shouldNotify)) + // we send the notifications and return the info of the devices notified + infos, err := c.SendNotification(publicKey, nil, messageID, message.LocalChatID, protobuf.PushNotification_MENTION) + if err != nil { + return err + } + + // mark message as sent so we don't notify again + for _, i := range infos { + c.config.Logger.Debug("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) + if err := c.notifiedOn(publicKey, i.InstallationID, messageID, message.LocalChatID, protobuf.PushNotification_MESSAGE); err != nil { + return err + } + + } + } + + } + for _, pkString := range message.Mentions { c.config.Logger.Debug("handling mention", zap.String("publickey", pkString)) pubkeyBytes, err := types.DecodeHex(pkString) diff --git a/protocol/requests/set_syncing_on_mobile_network.go b/protocol/requests/set_syncing_on_mobile_network.go new file mode 100644 index 000000000..b5b5f7b00 --- /dev/null +++ b/protocol/requests/set_syncing_on_mobile_network.go @@ -0,0 +1,9 @@ +package requests + +type SetSyncingOnMobileNetwork struct { + Enabled bool `json:"enabled"` +} + +func (r *SetSyncingOnMobileNetwork) Validate() error { + return nil +} diff --git a/services/ext/api.go b/services/ext/api.go index ac9ad9892..8104755d0 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -1526,6 +1526,10 @@ func (api *PublicAPI) TogglePeerSyncing(request *requests.TogglePeerSyncingReque return api.service.messenger.TogglePeerSyncing(request) } +func (api *PublicAPI) SetSyncingOnMobileNetwork(request *requests.SetSyncingOnMobileNetwork) error { + return api.service.messenger.SetSyncingOnMobileNetwork(request) +} + func (api *PublicAPI) SetPinnedMailservers(pinnedMailservers map[string]string) error { return api.service.messenger.SetPinnedMailservers(pinnedMailservers) }