github.com/status-im/status-go@v1.1.0/protocol/messenger_contacts.go (about)

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"crypto/ecdsa"
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/golang/protobuf/proto"
    10  	"go.uber.org/zap"
    11  
    12  	"github.com/ethereum/go-ethereum/log"
    13  
    14  	"github.com/status-im/status-go/deprecation"
    15  	"github.com/status-im/status-go/eth-node/crypto"
    16  	"github.com/status-im/status-go/eth-node/types"
    17  	multiaccountscommon "github.com/status-im/status-go/multiaccounts/common"
    18  	"github.com/status-im/status-go/protocol/common"
    19  	"github.com/status-im/status-go/protocol/protobuf"
    20  	"github.com/status-im/status-go/protocol/requests"
    21  	"github.com/status-im/status-go/protocol/transport"
    22  )
    23  
    24  const outgoingMutualStateEventSentDefaultText = "You sent a contact request to @%s"
    25  const outgoingMutualStateEventAcceptedDefaultText = "You accepted @%s's contact request"
    26  const outgoingMutualStateEventRemovedDefaultText = "You removed @%s as a contact"
    27  const incomingMutualStateEventSentDefaultText = "@%s sent you a contact request"
    28  const incomingMutualStateEventAcceptedDefaultText = "@%s accepted your contact request"
    29  const incomingMutualStateEventRemovedDefaultText = "@%s removed you as a contact"
    30  
    31  var ErrGetLatestContactRequestForContactInvalidID = errors.New("get-latest-contact-request-for-contact: invalid id")
    32  
    33  type SelfContactChangeEvent struct {
    34  	DisplayNameChanged   bool
    35  	PreferredNameChanged bool
    36  	BioChanged           bool
    37  	SocialLinksChanged   bool
    38  	ImagesChanged        bool
    39  }
    40  
    41  func (m *Messenger) prepareMutualStateUpdateMessage(contactID string, updateType MutualStateUpdateType, clock uint64, timestamp uint64, outgoing bool) (*common.Message, error) {
    42  	var text string
    43  	var to string
    44  	var from string
    45  	var contentType protobuf.ChatMessage_ContentType
    46  	if outgoing {
    47  		to = contactID
    48  		from = m.myHexIdentity()
    49  
    50  		switch updateType {
    51  		case MutualStateUpdateTypeSent:
    52  			text = fmt.Sprintf(outgoingMutualStateEventSentDefaultText, contactID)
    53  			contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT
    54  		case MutualStateUpdateTypeAdded:
    55  			text = fmt.Sprintf(outgoingMutualStateEventAcceptedDefaultText, contactID)
    56  			contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED
    57  		case MutualStateUpdateTypeRemoved:
    58  			text = fmt.Sprintf(outgoingMutualStateEventRemovedDefaultText, contactID)
    59  			contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED
    60  		default:
    61  			return nil, fmt.Errorf("unhandled outgoing MutualStateUpdateType = %d", updateType)
    62  		}
    63  	} else {
    64  		to = m.myHexIdentity()
    65  		from = contactID
    66  
    67  		switch updateType {
    68  		case MutualStateUpdateTypeSent:
    69  			text = fmt.Sprintf(incomingMutualStateEventSentDefaultText, contactID)
    70  			contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_SENT
    71  		case MutualStateUpdateTypeAdded:
    72  			text = fmt.Sprintf(incomingMutualStateEventAcceptedDefaultText, contactID)
    73  			contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_ACCEPTED
    74  		case MutualStateUpdateTypeRemoved:
    75  			text = fmt.Sprintf(incomingMutualStateEventRemovedDefaultText, contactID)
    76  			contentType = protobuf.ChatMessage_SYSTEM_MESSAGE_MUTUAL_EVENT_REMOVED
    77  		default:
    78  			return nil, fmt.Errorf("unhandled incoming MutualStateUpdateType = %d", updateType)
    79  		}
    80  	}
    81  
    82  	message := &common.Message{
    83  		ChatMessage: &protobuf.ChatMessage{
    84  			ChatId:      contactID,
    85  			Text:        text,
    86  			MessageType: protobuf.MessageType_ONE_TO_ONE,
    87  			ContentType: contentType,
    88  			Clock:       clock,
    89  			Timestamp:   timestamp,
    90  		},
    91  		From:             from,
    92  		WhisperTimestamp: timestamp,
    93  		LocalChatID:      contactID,
    94  		Seen:             true,
    95  		ID:               types.EncodeHex(crypto.Keccak256([]byte(fmt.Sprintf("%s%s%d%d", from, to, updateType, clock)))),
    96  	}
    97  
    98  	return message, nil
    99  }
   100  
   101  func (m *Messenger) acceptContactRequest(ctx context.Context, requestID string, fromSyncing bool) (*MessengerResponse, error) {
   102  	contactRequest, err := m.persistence.MessageByID(requestID)
   103  	if err != nil {
   104  		m.logger.Error("could not find contact request message", zap.Error(err))
   105  		return nil, err
   106  	}
   107  
   108  	m.logger.Info("acceptContactRequest")
   109  
   110  	var ensName, nickname, displayName string
   111  	customizationColor := multiaccountscommon.IDToColorFallbackToBlue(contactRequest.CustomizationColor)
   112  
   113  	if contact, ok := m.allContacts.Load(contactRequest.From); ok {
   114  		ensName = contact.EnsName
   115  		nickname = contact.LocalNickname
   116  		displayName = contact.DisplayName
   117  		customizationColor = contact.CustomizationColor
   118  	}
   119  
   120  	response, err := m.addContact(ctx, contactRequest.From, ensName, nickname, displayName, customizationColor, contactRequest.ID, "", fromSyncing, false, false)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	// Force activate chat
   126  	chat, ok := m.allChats.Load(contactRequest.From)
   127  	if !ok {
   128  		publicKey, err := common.HexToPubkey(contactRequest.From)
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  
   133  		chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
   134  	}
   135  
   136  	chat.Active = true
   137  	if err := m.saveChat(chat); err != nil {
   138  		return nil, err
   139  	}
   140  	response.AddChat(chat)
   141  
   142  	return response, nil
   143  }
   144  
   145  func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.AcceptContactRequest) (*MessengerResponse, error) {
   146  	err := request.Validate()
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	response, err := m.acceptContactRequest(ctx, request.ID.String(), false)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	err = m.syncContactRequestDecision(ctx, request.ID.String(), "", true, m.dispatchMessage)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	return response, nil
   162  }
   163  
   164  func (m *Messenger) declineContactRequest(requestID, contactID string, fromSyncing bool) (*MessengerResponse, error) {
   165  	m.logger.Info("declineContactRequest")
   166  
   167  	contactRequest, err := m.persistence.MessageByID(requestID)
   168  	if err == common.ErrRecordNotFound && fromSyncing {
   169  		// original requestID(Message ID) is useless since we don't sync UserMessage in this case
   170  		requestID = defaultContactRequestID(contactID)
   171  		contactRequest, err = m.persistence.MessageByID(requestID)
   172  	}
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	response := &MessengerResponse{}
   178  	var contact *Contact
   179  	if contactRequest != nil {
   180  		contact, err = m.BuildContact(&requests.BuildContact{PublicKey: contactRequest.From})
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		contactRequest.ContactRequestState = common.ContactRequestStateDismissed
   185  		err = m.persistence.SetContactRequestState(contactRequest.ID, contactRequest.ContactRequestState)
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  		response.AddMessage(contactRequest)
   190  	}
   191  
   192  	if !fromSyncing {
   193  		_, clock, err := m.getOneToOneAndNextClock(contact)
   194  		if err != nil {
   195  			return nil, err
   196  		}
   197  
   198  		contact.DismissContactRequest(clock)
   199  		err = m.persistence.SaveContact(contact, nil)
   200  		if err != nil {
   201  			return nil, err
   202  		}
   203  
   204  		response.AddContact(contact)
   205  	}
   206  
   207  	// update notification with the correct status
   208  	notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(requestID))
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  	if notification != nil {
   213  		notification.Name = contact.PrimaryName()
   214  		notification.Message = contactRequest
   215  		notification.Read = true
   216  		notification.Dismissed = true
   217  		notification.UpdatedAt = m.GetCurrentTimeInMillis()
   218  
   219  		err = m.addActivityCenterNotification(response, notification, m.syncActivityCenterDismissedByIDs)
   220  		if err != nil {
   221  			m.logger.Error("failed to save notification", zap.Error(err))
   222  			return nil, err
   223  		}
   224  	}
   225  	return response, nil
   226  }
   227  
   228  func (m *Messenger) DeclineContactRequest(ctx context.Context, request *requests.DeclineContactRequest) (*MessengerResponse, error) {
   229  	err := request.Validate()
   230  	if err != nil {
   231  		return nil, err
   232  	}
   233  
   234  	response, err := m.declineContactRequest(request.ID.String(), "", false)
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	err = m.syncContactRequestDecision(ctx, request.ID.String(), "", false, m.dispatchMessage)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	return response, nil
   245  }
   246  
   247  func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.SendContactRequest) (*MessengerResponse, error) {
   248  	err := request.Validate()
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	chatID, err := request.HexID()
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	var ensName, nickname, displayName string
   259  	customizationColor := multiaccountscommon.CustomizationColorBlue
   260  
   261  	if contact, ok := m.allContacts.Load(chatID); ok {
   262  		ensName = contact.EnsName
   263  		nickname = contact.LocalNickname
   264  		displayName = contact.DisplayName
   265  		customizationColor = contact.CustomizationColor
   266  	}
   267  
   268  	return m.addContact(
   269  		ctx,
   270  		chatID,
   271  		ensName,
   272  		nickname,
   273  		displayName,
   274  		customizationColor,
   275  		"",
   276  		request.Message,
   277  		false,
   278  		false,
   279  		true,
   280  	)
   281  }
   282  
   283  func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, contactRequestID, contactID string, fromSyncing bool) (*MessengerResponse, error) {
   284  	m.logger.Debug("updateAcceptedContactRequest", zap.String("contactRequestID", contactRequestID), zap.String("contactID", contactID), zap.Bool("fromSyncing", fromSyncing))
   285  
   286  	contactRequest, err := m.persistence.MessageByID(contactRequestID)
   287  	if err == common.ErrRecordNotFound && fromSyncing {
   288  		// original requestID(Message ID) is useless since we don't sync UserMessage in this case
   289  		contactRequestID = defaultContactRequestID(contactID)
   290  		contactRequest, err = m.persistence.MessageByID(contactRequestID)
   291  	}
   292  	if err != nil {
   293  		m.logger.Error("contact request not found", zap.String("contactRequestID", contactRequestID), zap.Error(err))
   294  		return nil, err
   295  	}
   296  
   297  	contactRequest.ContactRequestState = common.ContactRequestStateAccepted
   298  
   299  	err = m.persistence.SetContactRequestState(contactRequest.ID, contactRequest.ContactRequestState)
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	contact, ok := m.allContacts.Load(contactRequest.From)
   305  	if !ok {
   306  		m.logger.Error("failed to update contact request: contact not found", zap.String("contact id", contactRequest.From))
   307  		return nil, errors.New("failed to update contact request: contact not found")
   308  	}
   309  
   310  	chat, ok := m.allChats.Load(contact.ID)
   311  	if !ok {
   312  		return nil, errors.New("no chat found for accepted contact request")
   313  	}
   314  
   315  	notification, err := m.persistence.GetActivityCenterNotificationByID(types.FromHex(contactRequest.ID))
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  
   320  	clock, _ := chat.NextClockAndTimestamp(m.transport)
   321  	contact.AcceptContactRequest(clock)
   322  
   323  	if !fromSyncing {
   324  		acceptContactRequest := &protobuf.AcceptContactRequest{
   325  			Id:    contactRequest.ID,
   326  			Clock: clock,
   327  		}
   328  		encodedMessage, err := proto.Marshal(acceptContactRequest)
   329  		if err != nil {
   330  			return nil, err
   331  		}
   332  		_, err = m.dispatchMessage(context.Background(), common.RawMessage{
   333  			LocalChatID: contactRequest.From,
   334  			Payload:     encodedMessage,
   335  			MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_CONTACT_REQUEST,
   336  			ResendType:  common.ResendTypeDataSync,
   337  		})
   338  		if err != nil {
   339  			return nil, err
   340  		}
   341  
   342  		// Dispatch profile message to add a contact to the encrypted profile part
   343  		err = m.DispatchProfileShowcase()
   344  		if err != nil {
   345  			return nil, err
   346  		}
   347  	}
   348  
   349  	if response == nil {
   350  		response = &MessengerResponse{}
   351  	}
   352  
   353  	if notification != nil {
   354  		notification.Name = contact.PrimaryName()
   355  		notification.Message = contactRequest
   356  		notification.Read = true
   357  		notification.Accepted = true
   358  		notification.UpdatedAt = m.GetCurrentTimeInMillis()
   359  
   360  		err = m.addActivityCenterNotification(response, notification, nil)
   361  		if err != nil {
   362  			m.logger.Error("failed to save notification", zap.Error(err))
   363  			return nil, err
   364  		}
   365  	}
   366  
   367  	response.AddMessage(contactRequest)
   368  	response.AddContact(contact)
   369  
   370  	// Add mutual state update message for incoming contact request
   371  	clock, timestamp := chat.NextClockAndTimestamp(m.transport)
   372  	updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeAdded, clock, timestamp, true)
   373  	if err != nil {
   374  		return nil, err
   375  	}
   376  
   377  	err = m.prepareMessage(updateMessage, m.httpServer)
   378  	if err != nil {
   379  		return nil, err
   380  	}
   381  	err = m.persistence.SaveMessages([]*common.Message{updateMessage})
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  	response.AddMessage(updateMessage)
   386  	response.AddChat(chat)
   387  
   388  	return response, nil
   389  }
   390  
   391  func (m *Messenger) addContact(ctx context.Context,
   392  	pubKey, ensName, nickname, displayName string,
   393  	customizationColor multiaccountscommon.CustomizationColor,
   394  	contactRequestID, contactRequestText string,
   395  	fromSyncing, sendContactUpdate, createOutgoingContactRequestNotification bool) (*MessengerResponse, error) {
   396  	contact, err := m.BuildContact(&requests.BuildContact{PublicKey: pubKey})
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  
   401  	response := &MessengerResponse{}
   402  
   403  	chat, clock, err := m.getOneToOneAndNextClock(contact)
   404  	if err != nil {
   405  		return nil, err
   406  	}
   407  
   408  	if ensName != "" {
   409  		err := m.ensVerifier.ENSVerified(pubKey, ensName, clock)
   410  		if err != nil {
   411  			return nil, err
   412  		}
   413  	}
   414  	if err := m.addENSNameToContact(contact); err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	if len(nickname) != 0 {
   419  		contact.LocalNickname = nickname
   420  	}
   421  
   422  	if len(displayName) != 0 {
   423  		contact.DisplayName = displayName
   424  	}
   425  
   426  	contact.CustomizationColor = customizationColor
   427  
   428  	contact.LastUpdatedLocally = clock
   429  	contact.ContactRequestSent(clock)
   430  
   431  	if !fromSyncing {
   432  		// We sync the contact with the other devices
   433  		err := m.syncContact(context.Background(), contact, m.dispatchMessage)
   434  		if err != nil {
   435  			return nil, err
   436  		}
   437  	}
   438  
   439  	err = m.persistence.SaveContact(contact, nil)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  
   444  	// TODO(samyoul) remove storing of an updated reference pointer?
   445  	m.allContacts.Store(contact.ID, contact)
   446  
   447  	// And we re-register for push notications
   448  	err = m.reregisterForPushNotifications()
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  
   453  	// Reset last published time for ChatIdentity so new contact can receive data
   454  	err = m.resetLastPublishedTimeForChatIdentity()
   455  	if err != nil {
   456  		return nil, err
   457  	}
   458  
   459  	// Profile chats are deprecated.
   460  	// Code below can be removed after some reasonable time.
   461  
   462  	//Create the corresponding chat
   463  	var profileChat *Chat
   464  	if !deprecation.ChatProfileDeprecated {
   465  		profileChat = m.buildProfileChat(contact.ID)
   466  
   467  		_, err = m.Join(profileChat)
   468  		if err != nil {
   469  			return nil, err
   470  		}
   471  
   472  		if err := m.saveChat(profileChat); err != nil {
   473  			return nil, err
   474  		}
   475  	}
   476  
   477  	publicKey, err := contact.PublicKey()
   478  	if err != nil {
   479  		return nil, err
   480  	}
   481  
   482  	// Fetch contact code
   483  	_, err = m.scheduleSyncFiltersForContact(publicKey)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  
   488  	if sendContactUpdate {
   489  		// Get ENS name of a current user
   490  		ensName, err = m.settings.ENSName()
   491  		if err != nil {
   492  			return nil, err
   493  		}
   494  
   495  		// Get display name of a current user
   496  		displayName, err = m.settings.DisplayName()
   497  		if err != nil {
   498  			return nil, err
   499  		}
   500  		response, err = m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "", m.account.GetCustomizationColor(), m.dispatchMessage)
   501  		if err != nil {
   502  			return nil, err
   503  		}
   504  	}
   505  
   506  	if len(contactRequestID) != 0 {
   507  		updatedResponse, err := m.updateAcceptedContactRequest(response, contactRequestID, "", false)
   508  		if err != nil {
   509  			return nil, err
   510  		}
   511  		err = response.Merge(updatedResponse)
   512  		if err != nil {
   513  			return nil, err
   514  		}
   515  	}
   516  
   517  	// Sends a standalone ChatIdentity message
   518  	err = m.handleStandaloneChatIdentity(chat)
   519  	if err != nil {
   520  		return nil, err
   521  	}
   522  
   523  	// Profile chats are deprecated.
   524  	// Code below can be removed after some reasonable time.
   525  
   526  	// Add chat
   527  	if !deprecation.ChatProfileDeprecated {
   528  		response.AddChat(profileChat)
   529  
   530  		_, err = m.transport.InitFilters([]transport.FiltersToInitialize{{ChatID: profileChat.ID}}, []*ecdsa.PublicKey{publicKey})
   531  		if err != nil {
   532  			return nil, err
   533  		}
   534  	}
   535  
   536  	// Publish contact code
   537  	err = m.publishContactCode()
   538  	if err != nil {
   539  		return nil, err
   540  	}
   541  
   542  	// Add mutual state update message for outgoing contact request
   543  	if len(contactRequestID) == 0 {
   544  		clock, timestamp := chat.NextClockAndTimestamp(m.transport)
   545  		updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeSent, clock, timestamp, true)
   546  		if err != nil {
   547  			return nil, err
   548  		}
   549  
   550  		err = m.prepareMessage(updateMessage, m.httpServer)
   551  		if err != nil {
   552  			return nil, err
   553  		}
   554  		err = m.persistence.SaveMessages([]*common.Message{updateMessage})
   555  		if err != nil {
   556  			return nil, err
   557  		}
   558  		response.AddMessage(updateMessage)
   559  		err = chat.UpdateFromMessage(updateMessage, m.getTimesource())
   560  		if err != nil {
   561  			return nil, err
   562  		}
   563  		response.AddChat(chat)
   564  	}
   565  
   566  	// Add outgoing contact request notification
   567  	if createOutgoingContactRequestNotification {
   568  		clock, timestamp := chat.NextClockAndTimestamp(m.transport)
   569  		contactRequest, err := m.generateContactRequest(clock, timestamp, contact, contactRequestText, true)
   570  		if err != nil {
   571  			return nil, err
   572  		}
   573  
   574  		// Send contact request as a plain chat message
   575  		messageResponse, err := m.sendChatMessage(ctx, contactRequest)
   576  		if err != nil {
   577  			return nil, err
   578  		}
   579  
   580  		err = response.Merge(messageResponse)
   581  		if err != nil {
   582  			return nil, err
   583  		}
   584  
   585  		notification := m.generateOutgoingContactRequestNotification(contact, contactRequest)
   586  		err = m.addActivityCenterNotification(response, notification, nil)
   587  		if err != nil {
   588  			return nil, err
   589  		}
   590  	}
   591  
   592  	// Add contact
   593  	response.AddContact(contact)
   594  	return response, nil
   595  }
   596  
   597  func (m *Messenger) generateContactRequest(clock uint64, timestamp uint64, contact *Contact, text string, outgoing bool) (*common.Message, error) {
   598  	if contact == nil {
   599  		return nil, errors.New("contact cannot be nil")
   600  	}
   601  
   602  	contactRequest := common.NewMessage()
   603  	contactRequest.ChatId = contact.ID
   604  	contactRequest.WhisperTimestamp = timestamp
   605  	contactRequest.Seen = true
   606  	contactRequest.Text = text
   607  	if outgoing {
   608  		contactRequest.From = m.myHexIdentity()
   609  		contactRequest.CustomizationColor = m.account.GetCustomizationColorID()
   610  	} else {
   611  		contactRequest.From = contact.ID
   612  		contactRequest.CustomizationColor = multiaccountscommon.ColorToIDFallbackToBlue(contact.CustomizationColor)
   613  	}
   614  	contactRequest.LocalChatID = contact.ID
   615  	contactRequest.ContentType = protobuf.ChatMessage_CONTACT_REQUEST
   616  	contactRequest.Clock = clock
   617  	if contact.mutual() {
   618  		contactRequest.ContactRequestState = common.ContactRequestStateAccepted
   619  	} else {
   620  		contactRequest.ContactRequestState = common.ContactRequestStatePending
   621  	}
   622  	err := contactRequest.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
   623  	return contactRequest, err
   624  }
   625  
   626  func (m *Messenger) generateOutgoingContactRequestNotification(contact *Contact, contactRequest *common.Message) *ActivityCenterNotification {
   627  	return &ActivityCenterNotification{
   628  		ID:        types.FromHex(contactRequest.ID),
   629  		Type:      ActivityCenterNotificationTypeContactRequest,
   630  		Name:      contact.PrimaryName(),
   631  		Author:    m.myHexIdentity(),
   632  		Message:   contactRequest,
   633  		Timestamp: m.getTimesource().GetCurrentTime(),
   634  		ChatID:    contact.ID,
   635  		Read: contactRequest.ContactRequestState == common.ContactRequestStateAccepted ||
   636  			contactRequest.ContactRequestState == common.ContactRequestStateDismissed ||
   637  			contactRequest.ContactRequestState == common.ContactRequestStatePending,
   638  		Accepted:  contactRequest.ContactRequestState == common.ContactRequestStateAccepted,
   639  		Dismissed: contactRequest.ContactRequestState == common.ContactRequestStateDismissed,
   640  		UpdatedAt: m.GetCurrentTimeInMillis(),
   641  	}
   642  }
   643  
   644  func (m *Messenger) AddContact(ctx context.Context, request *requests.AddContact) (*MessengerResponse, error) {
   645  	err := request.Validate()
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  
   650  	id, err := request.HexID()
   651  	if err != nil {
   652  		return nil, err
   653  	}
   654  
   655  	return m.addContact(
   656  		ctx,
   657  		id,
   658  		request.ENSName,
   659  		request.Nickname,
   660  		request.DisplayName,
   661  		multiaccountscommon.CustomizationColor(request.CustomizationColor),
   662  		"",
   663  		defaultContactRequestText(),
   664  		false,
   665  		true,
   666  		true,
   667  	)
   668  }
   669  
   670  func (m *Messenger) resetLastPublishedTimeForChatIdentity() error {
   671  	// Reset last published time for ChatIdentity so new contact can receive data
   672  	contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
   673  	m.logger.Debug("contact state changed ResetWhenChatIdentityLastPublished")
   674  	return m.persistence.ResetWhenChatIdentityLastPublished(contactCodeTopic)
   675  }
   676  
   677  func (m *Messenger) removeContact(ctx context.Context, response *MessengerResponse, pubKey string, sync bool) error {
   678  	contact, ok := m.allContacts.Load(pubKey)
   679  	if !ok {
   680  		return ErrContactNotFound
   681  	}
   682  
   683  	// System message for mutual state update
   684  	chat, clock, err := m.getOneToOneAndNextClock(contact)
   685  	if err != nil {
   686  		return err
   687  	}
   688  	timestamp := m.getTimesource().GetCurrentTime()
   689  	updateMessage, err := m.prepareMutualStateUpdateMessage(contact.ID, MutualStateUpdateTypeRemoved, clock, timestamp, true)
   690  	if err != nil {
   691  		return err
   692  	}
   693  
   694  	err = m.prepareMessage(updateMessage, m.httpServer)
   695  	if err != nil {
   696  		return err
   697  	}
   698  	err = m.persistence.SaveMessages([]*common.Message{updateMessage})
   699  	if err != nil {
   700  		return err
   701  	}
   702  	response.AddMessage(updateMessage)
   703  	err = chat.UpdateFromMessage(updateMessage, m.getTimesource())
   704  	if err != nil {
   705  		return err
   706  	}
   707  	response.AddChat(chat)
   708  
   709  	// Next we retract a contact request
   710  	contact.RetractContactRequest(clock)
   711  	contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
   712  
   713  	err = m.persistence.SaveContact(contact, nil)
   714  	if err != nil {
   715  		return err
   716  	}
   717  
   718  	if sync {
   719  		err = m.syncContact(context.Background(), contact, m.dispatchMessage)
   720  		if err != nil {
   721  			return err
   722  		}
   723  	}
   724  
   725  	// TODO(samyoul) remove storing of an updated reference pointer?
   726  	m.allContacts.Store(contact.ID, contact)
   727  
   728  	// And we re-register for push notications
   729  	err = m.reregisterForPushNotifications()
   730  	if err != nil {
   731  		return err
   732  	}
   733  
   734  	// Dispatch profile message to remove a contact from the encrypted profile part
   735  	err = m.DispatchProfileShowcase()
   736  	if err != nil {
   737  		return err
   738  	}
   739  
   740  	// Profile chats are deprecated.
   741  	// Code below can be removed after some reasonable time.
   742  
   743  	//Create the corresponding profile chat
   744  	if !deprecation.ChatProfileDeprecated {
   745  		profileChatID := buildProfileChatID(contact.ID)
   746  		_, ok = m.allChats.Load(profileChatID)
   747  
   748  		if ok {
   749  			chatResponse, err := m.deactivateChat(profileChatID, 0, false, true)
   750  			if err != nil {
   751  				return err
   752  			}
   753  			err = response.Merge(chatResponse)
   754  			if err != nil {
   755  				return err
   756  			}
   757  		}
   758  	}
   759  
   760  	response.Contacts = []*Contact{contact}
   761  	return nil
   762  }
   763  
   764  func (m *Messenger) RemoveContact(ctx context.Context, pubKey string) (*MessengerResponse, error) {
   765  	response := new(MessengerResponse)
   766  
   767  	err := m.removeContact(ctx, response, pubKey, true)
   768  	if err != nil {
   769  		return nil, err
   770  	}
   771  
   772  	return response, nil
   773  }
   774  
   775  func (m *Messenger) updateContactImagesURL(contact *Contact) error {
   776  	if m.httpServer != nil {
   777  		for k, v := range contact.Images {
   778  			publicKey, err := contact.PublicKey()
   779  			if err != nil {
   780  				return err
   781  			}
   782  			v.LocalURL = m.httpServer.MakeContactImageURL(common.PubkeyToHex(publicKey), k)
   783  			contact.Images[k] = v
   784  		}
   785  	}
   786  	return nil
   787  }
   788  
   789  func (m *Messenger) Contacts() []*Contact {
   790  	var contacts []*Contact
   791  	m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
   792  		contacts = append(contacts, contact)
   793  		return true
   794  	})
   795  	return contacts
   796  }
   797  
   798  func (m *Messenger) AddedContacts() []*Contact {
   799  	var contacts []*Contact
   800  	m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
   801  		if contact.added() {
   802  			contacts = append(contacts, contact)
   803  		}
   804  		return true
   805  	})
   806  	return contacts
   807  }
   808  
   809  func (m *Messenger) MutualContacts() []*Contact {
   810  	var contacts []*Contact
   811  	m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
   812  		if contact.mutual() {
   813  			contacts = append(contacts, contact)
   814  		}
   815  		return true
   816  	})
   817  	return contacts
   818  }
   819  
   820  func (m *Messenger) BlockedContacts() []*Contact {
   821  	var contacts []*Contact
   822  	m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
   823  		if contact.Blocked {
   824  			contacts = append(contacts, contact)
   825  		}
   826  		return true
   827  	})
   828  	return contacts
   829  }
   830  
   831  // GetContactByID returns a Contact for given pubKey, if it's known.
   832  // This function automatically checks if pubKey is self identity key and returns a Contact
   833  // filled with self information.
   834  // pubKey is assumed to include `0x` prefix
   835  func (m *Messenger) GetContactByID(pubKey string) *Contact {
   836  	if pubKey == m.IdentityPublicKeyString() {
   837  		return m.selfContact
   838  	}
   839  	contact, _ := m.allContacts.Load(pubKey)
   840  	return contact
   841  }
   842  
   843  func (m *Messenger) GetSelfContact() *Contact {
   844  	return m.selfContact
   845  }
   846  
   847  func (m *Messenger) SetContactLocalNickname(request *requests.SetContactLocalNickname) (*MessengerResponse, error) {
   848  
   849  	if err := request.Validate(); err != nil {
   850  		return nil, err
   851  	}
   852  
   853  	pubKey := request.ID.String()
   854  	nickname := request.Nickname
   855  
   856  	contact, err := m.BuildContact(&requests.BuildContact{PublicKey: pubKey})
   857  	if err != nil {
   858  		return nil, err
   859  	}
   860  
   861  	if err := m.addENSNameToContact(contact); err != nil {
   862  		return nil, err
   863  	}
   864  
   865  	clock := m.getTimesource().GetCurrentTime()
   866  	contact.LocalNickname = nickname
   867  	contact.LastUpdatedLocally = clock
   868  
   869  	err = m.persistence.SaveContact(contact, nil)
   870  	if err != nil {
   871  		return nil, err
   872  	}
   873  
   874  	m.allContacts.Store(contact.ID, contact)
   875  
   876  	response := &MessengerResponse{}
   877  	response.Contacts = []*Contact{contact}
   878  
   879  	err = m.syncContact(context.Background(), contact, m.dispatchMessage)
   880  	if err != nil {
   881  		return nil, err
   882  	}
   883  
   884  	return response, nil
   885  }
   886  
   887  func (m *Messenger) blockContact(ctx context.Context, response *MessengerResponse, contactID string, isDesktopFunc bool, fromSyncing bool) error {
   888  	contact, err := m.BuildContact(&requests.BuildContact{PublicKey: contactID})
   889  	if err != nil {
   890  		return err
   891  	}
   892  
   893  	response.AddContact(contact)
   894  
   895  	_, clock, err := m.getOneToOneAndNextClock(contact)
   896  	if err != nil {
   897  		return err
   898  	}
   899  
   900  	contactWasAdded := contact.added()
   901  	contact.Block(clock)
   902  
   903  	contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
   904  
   905  	chats, err := m.persistence.BlockContact(contact, isDesktopFunc)
   906  	if err != nil {
   907  		return err
   908  	}
   909  
   910  	response.AddChats(chats)
   911  
   912  	m.allContacts.Store(contact.ID, contact)
   913  	for _, chat := range chats {
   914  		m.allChats.Store(chat.ID, chat)
   915  	}
   916  
   917  	if !isDesktopFunc {
   918  		m.allChats.Delete(contact.ID)
   919  		m.allChats.Delete(buildProfileChatID(contact.ID))
   920  	}
   921  
   922  	if !fromSyncing {
   923  		if contactWasAdded {
   924  			err = m.sendRetractContactRequest(contact)
   925  			if err != nil {
   926  				return err
   927  			}
   928  		}
   929  
   930  		err = m.syncContact(context.Background(), contact, m.dispatchMessage)
   931  		if err != nil {
   932  			return err
   933  		}
   934  
   935  		// We remove anything that's related to this contact request
   936  		updatedAt := m.GetCurrentTimeInMillis()
   937  		notifications, err := m.persistence.DeleteChatContactRequestActivityCenterNotifications(contact.ID, updatedAt)
   938  		if err != nil {
   939  			return err
   940  		}
   941  		err = m.syncActivityCenterDeleted(ctx, notifications, updatedAt)
   942  		if err != nil {
   943  			m.logger.Error("BlockContact, error syncing activity center notifications as deleted", zap.Error(err))
   944  			return err
   945  		}
   946  	}
   947  
   948  	// re-register for push notifications
   949  	err = m.reregisterForPushNotifications()
   950  	if err != nil {
   951  		return err
   952  	}
   953  
   954  	return nil
   955  }
   956  
   957  func (m *Messenger) BlockContact(ctx context.Context, contactID string, fromSyncing bool) (*MessengerResponse, error) {
   958  	response := &MessengerResponse{}
   959  
   960  	err := m.blockContact(ctx, response, contactID, false, fromSyncing)
   961  	if err != nil {
   962  		return nil, err
   963  	}
   964  
   965  	response, err = m.DeclineAllPendingGroupInvitesFromUser(ctx, response, contactID)
   966  	if err != nil {
   967  		return nil, err
   968  	}
   969  
   970  	// AC notifications are synced separately
   971  	// NOTE: Should we still do the local part (persistence.dismiss...) and only skip the syncing?
   972  	//		 This would make the solution more reliable even in case AC notification sync is not recevied.
   973  	//		 This should be considered separately, I'm not sure if that's safe.
   974  	//		 https://github.com/status-im/status-go/issues/3720
   975  	if !fromSyncing {
   976  		updatedAt := m.GetCurrentTimeInMillis()
   977  		_, err = m.DismissAllActivityCenterNotificationsFromUser(ctx, contactID, updatedAt)
   978  		if err != nil {
   979  			return nil, err
   980  		}
   981  	}
   982  
   983  	return response, nil
   984  }
   985  
   986  // The same function as the one above.
   987  // Should be removed with https://github.com/status-im/status-desktop/issues/8805
   988  func (m *Messenger) BlockContactDesktop(ctx context.Context, contactID string) (*MessengerResponse, error) {
   989  	response := &MessengerResponse{}
   990  
   991  	err := m.blockContact(ctx, response, contactID, true, false)
   992  	if err != nil {
   993  		return nil, err
   994  	}
   995  
   996  	response, err = m.DeclineAllPendingGroupInvitesFromUser(ctx, response, contactID)
   997  	if err != nil {
   998  		return nil, err
   999  	}
  1000  
  1001  	notifications, err := m.DismissAllActivityCenterNotificationsFromUser(ctx, contactID, m.GetCurrentTimeInMillis())
  1002  	if err != nil {
  1003  		return nil, err
  1004  	}
  1005  	response.AddActivityCenterNotifications(notifications)
  1006  	return response, nil
  1007  }
  1008  
  1009  func (m *Messenger) UnblockContact(contactID string) (*MessengerResponse, error) {
  1010  	response := &MessengerResponse{}
  1011  	contact, ok := m.allContacts.Load(contactID)
  1012  	if !ok || !contact.Blocked {
  1013  		return response, nil
  1014  	}
  1015  
  1016  	_, clock, err := m.getOneToOneAndNextClock(contact)
  1017  	if err != nil {
  1018  		return nil, err
  1019  	}
  1020  
  1021  	contact.Unblock(clock)
  1022  
  1023  	contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
  1024  
  1025  	err = m.persistence.SaveContact(contact, nil)
  1026  	if err != nil {
  1027  		return nil, err
  1028  	}
  1029  
  1030  	m.allContacts.Store(contact.ID, contact)
  1031  
  1032  	response.AddContact(contact)
  1033  
  1034  	err = m.syncContact(context.Background(), contact, m.dispatchMessage)
  1035  	if err != nil {
  1036  		return nil, err
  1037  	}
  1038  
  1039  	// re-register for push notifications
  1040  	err = m.reregisterForPushNotifications()
  1041  	if err != nil {
  1042  		return nil, err
  1043  	}
  1044  
  1045  	return response, nil
  1046  }
  1047  
  1048  // Send contact updates to all contacts added by us
  1049  func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImage string, customizationColor multiaccountscommon.CustomizationColor) (err error) {
  1050  	myID := contactIDFromPublicKey(&m.identity.PublicKey)
  1051  
  1052  	displayName, err := m.settings.DisplayName()
  1053  	if err != nil {
  1054  		return err
  1055  	}
  1056  
  1057  	if len(customizationColor) == 0 && m.account != nil {
  1058  		customizationColor = m.account.GetCustomizationColor()
  1059  	}
  1060  
  1061  	if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, profileImage, customizationColor, m.dispatchMessage); err != nil {
  1062  		return err
  1063  	}
  1064  
  1065  	// TODO: This should not be sending paired messages, as we do it above
  1066  	m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
  1067  		if contact.added() {
  1068  			if _, err = m.sendContactUpdate(ctx, contact.ID, displayName, ensName, profileImage, customizationColor, m.dispatchMessage); err != nil {
  1069  				return false
  1070  			}
  1071  		}
  1072  		return true
  1073  	})
  1074  	return err
  1075  }
  1076  
  1077  // NOTE: this endpoint does not add the contact, the reason being is that currently
  1078  // that's left as a responsibility to the client, which will call both `SendContactUpdate`
  1079  // and `SaveContact` with the correct system tag.
  1080  // Ideally we have a single endpoint that does both, but probably best to bring `ENS` name
  1081  // on the messenger first.
  1082  
  1083  // SendContactUpdate sends a contact update to a user and adds the user to contacts
  1084  func (m *Messenger) SendContactUpdate(ctx context.Context, chatID, ensName, profileImage string, customizationColor multiaccountscommon.CustomizationColor) (*MessengerResponse, error) {
  1085  	displayName, err := m.settings.DisplayName()
  1086  	if err != nil {
  1087  		return nil, err
  1088  	}
  1089  
  1090  	return m.sendContactUpdate(ctx, chatID, displayName, ensName, profileImage, customizationColor, m.dispatchMessage)
  1091  }
  1092  
  1093  func (m *Messenger) sendContactUpdate(ctx context.Context,
  1094  	chatID, displayName, ensName, profileImage string,
  1095  	customizationColor multiaccountscommon.CustomizationColor,
  1096  	rawMessageHandler RawMessageHandler) (*MessengerResponse, error) {
  1097  	var response MessengerResponse
  1098  
  1099  	contact, ok := m.allContacts.Load(chatID)
  1100  	if !ok || !contact.added() {
  1101  		return nil, nil
  1102  	}
  1103  
  1104  	chat, clock, err := m.getOneToOneAndNextClock(contact)
  1105  	if err != nil {
  1106  		return nil, err
  1107  	}
  1108  
  1109  	contactUpdate := &protobuf.ContactUpdate{
  1110  		Clock:                         clock,
  1111  		DisplayName:                   displayName,
  1112  		EnsName:                       ensName,
  1113  		ProfileImage:                  profileImage,
  1114  		ContactRequestClock:           contact.ContactRequestLocalClock,
  1115  		ContactRequestPropagatedState: contact.ContactRequestPropagatedState(),
  1116  		PublicKey:                     contact.ID,
  1117  		CustomizationColor:            multiaccountscommon.ColorToIDFallbackToBlue(customizationColor),
  1118  	}
  1119  
  1120  	encodedMessage, err := proto.Marshal(contactUpdate)
  1121  	if err != nil {
  1122  		return nil, err
  1123  	}
  1124  
  1125  	rawMessage := common.RawMessage{
  1126  		LocalChatID: chatID,
  1127  		Payload:     encodedMessage,
  1128  		MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
  1129  		ResendType:  common.ResendTypeDataSync,
  1130  	}
  1131  
  1132  	_, err = rawMessageHandler(ctx, rawMessage)
  1133  	if err != nil {
  1134  		return nil, err
  1135  	}
  1136  
  1137  	response.Contacts = []*Contact{contact}
  1138  	response.AddChat(chat)
  1139  
  1140  	chat.LastClockValue = clock
  1141  	err = m.saveChat(chat)
  1142  	if err != nil {
  1143  		return nil, err
  1144  	}
  1145  	return &response, nil
  1146  }
  1147  
  1148  func (m *Messenger) addENSNameToContact(contact *Contact) error {
  1149  
  1150  	// Check if there's already a verified record
  1151  	ensRecord, err := m.ensVerifier.GetVerifiedRecord(contact.ID)
  1152  	if err != nil {
  1153  		return err
  1154  	}
  1155  	if ensRecord == nil {
  1156  		return nil
  1157  	}
  1158  
  1159  	contact.EnsName = ensRecord.Name
  1160  	contact.ENSVerified = true
  1161  
  1162  	return nil
  1163  }
  1164  
  1165  func (m *Messenger) RetractContactRequest(request *requests.RetractContactRequest) (*MessengerResponse, error) {
  1166  	err := request.Validate()
  1167  	if err != nil {
  1168  		return nil, err
  1169  	}
  1170  	contact, ok := m.allContacts.Load(request.ID.String())
  1171  	if !ok {
  1172  		return nil, errors.New("contact not found")
  1173  	}
  1174  	response := &MessengerResponse{}
  1175  	err = m.removeContact(context.Background(), response, contact.ID, true)
  1176  	if err != nil {
  1177  		return nil, err
  1178  	}
  1179  
  1180  	err = m.sendRetractContactRequest(contact)
  1181  	if err != nil {
  1182  		return nil, err
  1183  	}
  1184  
  1185  	return response, err
  1186  }
  1187  
  1188  // Send message to remote account to remove our contact from their end.
  1189  func (m *Messenger) sendRetractContactRequest(contact *Contact) error {
  1190  	_, clock, err := m.getOneToOneAndNextClock(contact)
  1191  	if err != nil {
  1192  		return err
  1193  	}
  1194  	retractContactRequest := &protobuf.RetractContactRequest{
  1195  		Clock: clock,
  1196  	}
  1197  
  1198  	encodedMessage, err := proto.Marshal(retractContactRequest)
  1199  	if err != nil {
  1200  		return err
  1201  	}
  1202  
  1203  	_, err = m.dispatchMessage(context.Background(), common.RawMessage{
  1204  		LocalChatID: contact.ID,
  1205  		Payload:     encodedMessage,
  1206  		MessageType: protobuf.ApplicationMetadataMessage_RETRACT_CONTACT_REQUEST,
  1207  		ResendType:  common.ResendTypeDataSync,
  1208  	})
  1209  	if err != nil {
  1210  		return err
  1211  	}
  1212  
  1213  	return err
  1214  }
  1215  
  1216  func (m *Messenger) GetLatestContactRequestForContact(contactID string) (*MessengerResponse, error) {
  1217  	if len(contactID) == 0 {
  1218  		return nil, ErrGetLatestContactRequestForContactInvalidID
  1219  	}
  1220  
  1221  	contactRequestID, err := m.persistence.LatestPendingContactRequestIDForContact(contactID)
  1222  	if err != nil {
  1223  		return nil, err
  1224  	}
  1225  
  1226  	contactRequest, err := m.persistence.MessageByID(contactRequestID)
  1227  	if err != nil {
  1228  		m.logger.Error("contact request not found", zap.String("contactRequestID", contactRequestID), zap.Error(err))
  1229  		return nil, err
  1230  	}
  1231  
  1232  	response := &MessengerResponse{}
  1233  	response.AddMessage(contactRequest)
  1234  
  1235  	return response, nil
  1236  }
  1237  
  1238  func (m *Messenger) AcceptLatestContactRequestForContact(ctx context.Context, request *requests.AcceptLatestContactRequestForContact) (*MessengerResponse, error) {
  1239  	if err := request.Validate(); err != nil {
  1240  		return nil, err
  1241  	}
  1242  
  1243  	contactRequestID, err := m.persistence.LatestPendingContactRequestIDForContact(request.ID.String())
  1244  	if err != nil {
  1245  		return nil, err
  1246  	}
  1247  
  1248  	return m.AcceptContactRequest(ctx, &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequestID)})
  1249  }
  1250  
  1251  func (m *Messenger) DismissLatestContactRequestForContact(ctx context.Context, request *requests.DismissLatestContactRequestForContact) (*MessengerResponse, error) {
  1252  	if err := request.Validate(); err != nil {
  1253  		return nil, err
  1254  	}
  1255  
  1256  	contactRequestID, err := m.persistence.LatestPendingContactRequestIDForContact(request.ID.String())
  1257  	if err != nil {
  1258  		return nil, err
  1259  	}
  1260  
  1261  	return m.DeclineContactRequest(ctx, &requests.DeclineContactRequest{ID: types.Hex2Bytes(contactRequestID)})
  1262  }
  1263  
  1264  func (m *Messenger) PendingContactRequests(cursor string, limit int) ([]*common.Message, string, error) {
  1265  	return m.persistence.PendingContactRequests(cursor, limit)
  1266  }
  1267  
  1268  func defaultContactRequestID(contactID string) string {
  1269  	return "0x" + types.Bytes2Hex(append(types.Hex2Bytes(contactID), 0x20))
  1270  }
  1271  
  1272  func defaultContactRequestText() string {
  1273  	return "Please add me to your contacts"
  1274  }
  1275  
  1276  func (m *Messenger) BuildContact(request *requests.BuildContact) (*Contact, error) {
  1277  	contact, ok := m.allContacts.Load(request.PublicKey)
  1278  	if !ok {
  1279  		var err error
  1280  		contact, err = buildContactFromPkString(request.PublicKey)
  1281  		if err != nil {
  1282  			return nil, err
  1283  		}
  1284  
  1285  		if request.ENSName != "" {
  1286  			contact.ENSVerified = true
  1287  			contact.EnsName = request.ENSName
  1288  		}
  1289  
  1290  		if len(contact.CustomizationColor) == 0 {
  1291  			contact.CustomizationColor = multiaccountscommon.CustomizationColorBlue
  1292  		}
  1293  	}
  1294  
  1295  	// Schedule sync filter to fetch information about the contact
  1296  	publicKey, err := contact.PublicKey()
  1297  	if err != nil {
  1298  		return nil, err
  1299  	}
  1300  
  1301  	_, err = m.scheduleSyncFiltersForContact(publicKey)
  1302  	if err != nil {
  1303  		return nil, err
  1304  	}
  1305  
  1306  	return contact, nil
  1307  }
  1308  
  1309  func (m *Messenger) scheduleSyncFiltersForContact(publicKey *ecdsa.PublicKey) (*transport.Filter, error) {
  1310  	filter, err := m.transport.JoinPrivate(publicKey)
  1311  	if err != nil {
  1312  		return nil, err
  1313  	}
  1314  	_, err = m.scheduleSyncFilters([]*transport.Filter{filter})
  1315  	if err != nil {
  1316  		return filter, err
  1317  	}
  1318  	return filter, nil
  1319  }
  1320  
  1321  func (m *Messenger) FetchContact(contactID string, waitForResponse bool) (*Contact, error) {
  1322  	options := []StoreNodeRequestOption{
  1323  		WithWaitForResponseOption(waitForResponse),
  1324  	}
  1325  	contact, _, err := m.storeNodeRequestsManager.FetchContact(contactID, options)
  1326  	return contact, err
  1327  }
  1328  
  1329  func (m *Messenger) SubscribeToSelfContactChanges() chan *SelfContactChangeEvent {
  1330  	s := make(chan *SelfContactChangeEvent, 10)
  1331  	m.selfContactSubscriptions = append(m.selfContactSubscriptions, s)
  1332  	return s
  1333  }
  1334  
  1335  func (m *Messenger) publishSelfContactSubscriptions(event *SelfContactChangeEvent) {
  1336  	for _, s := range m.selfContactSubscriptions {
  1337  		select {
  1338  		case s <- event:
  1339  		default:
  1340  			log.Warn("self contact subscription channel full, dropping message")
  1341  		}
  1342  	}
  1343  }