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

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"strings"
     7  
     8  	"github.com/status-im/status-go/deprecation"
     9  	"github.com/status-im/status-go/protocol/common"
    10  	"github.com/status-im/status-go/protocol/protobuf"
    11  	"github.com/status-im/status-go/protocol/requests"
    12  	"github.com/status-im/status-go/protocol/transport"
    13  )
    14  
    15  func (m *Messenger) getOneToOneAndNextClock(contact *Contact) (*Chat, uint64, error) {
    16  	chat, ok := m.allChats.Load(contact.ID)
    17  	if !ok {
    18  		publicKey, err := contact.PublicKey()
    19  		if err != nil {
    20  			return nil, 0, err
    21  		}
    22  
    23  		chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
    24  
    25  		// We don't want to show the chat to the user by default
    26  		chat.Active = false
    27  
    28  		if err := m.saveChat(chat); err != nil {
    29  			return nil, 0, err
    30  		}
    31  		m.allChats.Store(chat.ID, chat)
    32  	}
    33  	clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
    34  
    35  	return chat, clock, nil
    36  }
    37  
    38  func (m *Messenger) Chats() []*Chat {
    39  	var chats []*Chat
    40  
    41  	m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
    42  		chats = append(chats, chat)
    43  		return true
    44  	})
    45  
    46  	return chats
    47  }
    48  
    49  func (m *Messenger) ChatsPreview() []*ChatPreview {
    50  	var chats []*ChatPreview
    51  
    52  	m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
    53  		if chat.Active || chat.Muted {
    54  			chatPreview := &ChatPreview{
    55  				ID:                    chat.ID,
    56  				Name:                  chat.Name,
    57  				Description:           chat.Description,
    58  				Color:                 chat.Color,
    59  				Emoji:                 chat.Emoji,
    60  				Active:                chat.Active,
    61  				ChatType:              chat.ChatType,
    62  				Timestamp:             chat.Timestamp,
    63  				LastClockValue:        chat.LastClockValue,
    64  				DeletedAtClockValue:   chat.DeletedAtClockValue,
    65  				UnviewedMessagesCount: chat.UnviewedMessagesCount,
    66  				UnviewedMentionsCount: chat.UnviewedMentionsCount,
    67  				Alias:                 chat.Alias,
    68  				Identicon:             chat.Identicon,
    69  				Muted:                 chat.Muted,
    70  				MuteTill:              chat.MuteTill,
    71  				Profile:               chat.Profile,
    72  				CommunityID:           chat.CommunityID,
    73  				CategoryID:            chat.CategoryID,
    74  				Joined:                chat.Joined,
    75  				SyncedTo:              chat.SyncedTo,
    76  				SyncedFrom:            chat.SyncedFrom,
    77  				Highlight:             chat.Highlight,
    78  				Members:               chat.Members,
    79  				Base64Image:           chat.Base64Image,
    80  			}
    81  
    82  			if chat.LastMessage != nil {
    83  
    84  				chatPreview.OutgoingStatus = chat.LastMessage.OutgoingStatus
    85  				chatPreview.ResponseTo = chat.LastMessage.ResponseTo
    86  				chatPreview.ContentType = chat.LastMessage.ContentType
    87  				chatPreview.From = chat.LastMessage.From
    88  				chatPreview.Deleted = chat.LastMessage.Deleted
    89  				chatPreview.DeletedForMe = chat.LastMessage.DeletedForMe
    90  
    91  				if chat.LastMessage.ContentType == protobuf.ChatMessage_IMAGE {
    92  					chatPreview.ParsedText = chat.LastMessage.ParsedText
    93  
    94  					image := chat.LastMessage.GetImage()
    95  					if image != nil {
    96  						chatPreview.AlbumImagesCount = image.AlbumImagesCount
    97  						chatPreview.ParsedText = chat.LastMessage.ParsedText
    98  					}
    99  				}
   100  
   101  				if chat.LastMessage.ContentType == protobuf.ChatMessage_TEXT_PLAIN {
   102  
   103  					simplifiedText, err := chat.LastMessage.GetSimplifiedText("", nil)
   104  
   105  					if err == nil {
   106  						if len(simplifiedText) > 100 {
   107  							chatPreview.Text = simplifiedText[:100]
   108  						} else {
   109  							chatPreview.Text = simplifiedText
   110  						}
   111  						if strings.Contains(chatPreview.Text, "0x") {
   112  							//if there is a mention, we would like to send parsed text as well
   113  							chatPreview.ParsedText = chat.LastMessage.ParsedText
   114  						}
   115  					}
   116  				} else if chat.LastMessage.ContentType == protobuf.ChatMessage_EMOJI ||
   117  					chat.LastMessage.ContentType == protobuf.ChatMessage_TRANSACTION_COMMAND {
   118  
   119  					chatPreview.Text = chat.LastMessage.Text
   120  					chatPreview.ParsedText = chat.LastMessage.ParsedText
   121  				}
   122  				if chat.LastMessage.ContentType == protobuf.ChatMessage_COMMUNITY {
   123  					chatPreview.ContentCommunityID = chat.LastMessage.CommunityID
   124  				}
   125  			}
   126  
   127  			chats = append(chats, chatPreview)
   128  		}
   129  
   130  		return true
   131  	})
   132  
   133  	return chats
   134  }
   135  
   136  func (m *Messenger) Chat(chatID string) *Chat {
   137  	chat, _ := m.allChats.Load(chatID)
   138  
   139  	return chat
   140  }
   141  
   142  func (m *Messenger) ActiveChats() []*Chat {
   143  	m.mutex.Lock()
   144  	defer m.mutex.Unlock()
   145  
   146  	var chats []*Chat
   147  
   148  	m.allChats.Range(func(chatID string, c *Chat) bool {
   149  		if c.Active {
   150  			chats = append(chats, c)
   151  		}
   152  		return true
   153  	})
   154  
   155  	return chats
   156  }
   157  
   158  func (m *Messenger) initChatSyncFields(chat *Chat) error {
   159  	defaultSyncPeriod, err := m.settings.GetDefaultSyncPeriod()
   160  	if err != nil {
   161  		return err
   162  	}
   163  	timestamp := uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncPeriod
   164  	chat.SyncedTo = timestamp
   165  	chat.SyncedFrom = timestamp
   166  
   167  	return nil
   168  }
   169  
   170  func (m *Messenger) createPublicChat(chatID string, response *MessengerResponse) (*MessengerResponse, error) {
   171  	chat, ok := m.allChats.Load(chatID)
   172  	if !ok {
   173  		chat = CreatePublicChat(chatID, m.getTimesource())
   174  
   175  	}
   176  	chat.Active = true
   177  	chat.DeletedAtClockValue = 0
   178  
   179  	// Save topics
   180  	_, err := m.Join(chat)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	// Store chat
   186  	m.allChats.Store(chat.ID, chat)
   187  
   188  	willSync, err := m.scheduleSyncChat(chat)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	// We set the synced to, synced from to the default time
   194  	if !willSync {
   195  		if err := m.initChatSyncFields(chat); err != nil {
   196  			return nil, err
   197  		}
   198  	}
   199  
   200  	err = m.saveChat(chat)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	err = m.reregisterForPushNotifications()
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  
   210  	response.AddChat(chat)
   211  
   212  	return response, nil
   213  }
   214  
   215  func (m *Messenger) CreatePublicChat(request *requests.CreatePublicChat) (*MessengerResponse, error) {
   216  	if err := request.Validate(); err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	chatID := request.ID
   221  	response := &MessengerResponse{}
   222  
   223  	return m.createPublicChat(chatID, response)
   224  }
   225  
   226  // Deprecated: CreateProfileChat shouldn't be used
   227  // and is only left here in case profile chat feature is re-introduced.
   228  func (m *Messenger) CreateProfileChat(request *requests.CreateProfileChat) (*MessengerResponse, error) {
   229  	// Return error to prevent usage of deprecated function
   230  	if deprecation.ChatProfileDeprecated {
   231  		return nil, errors.New("profile chats are deprecated")
   232  	}
   233  
   234  	if err := request.Validate(); err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	publicKey, err := common.HexToPubkey(request.ID)
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  
   243  	chat := m.buildProfileChat(request.ID)
   244  
   245  	chat.Active = true
   246  
   247  	// Save topics
   248  	_, err = m.Join(chat)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	// Check contact code
   254  	filter, err := m.transport.JoinPrivate(publicKey)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	// Store chat
   260  	m.allChats.Store(chat.ID, chat)
   261  
   262  	response := &MessengerResponse{}
   263  	response.AddChat(chat)
   264  
   265  	willSync, err := m.scheduleSyncChat(chat)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	// We set the synced to, synced from to the default time
   271  	if !willSync {
   272  		if err := m.initChatSyncFields(chat); err != nil {
   273  			return nil, err
   274  		}
   275  	}
   276  
   277  	_, err = m.scheduleSyncFilters([]*transport.Filter{filter})
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	err = m.saveChat(chat)
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  
   287  	return response, nil
   288  }
   289  
   290  func (m *Messenger) CreateOneToOneChat(request *requests.CreateOneToOneChat) (*MessengerResponse, error) {
   291  	if err := request.Validate(); err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	chatID := request.ID.String()
   296  	pk, err := common.HexToPubkey(chatID)
   297  	if err != nil {
   298  		return nil, err
   299  	}
   300  
   301  	response := &MessengerResponse{}
   302  
   303  	ensName := request.ENSName
   304  	if ensName != "" {
   305  		clock := m.getTimesource().GetCurrentTime()
   306  		err := m.ensVerifier.ENSVerified(chatID, ensName, clock)
   307  		if err != nil {
   308  			return nil, err
   309  		}
   310  		contact, err := m.BuildContact(&requests.BuildContact{PublicKey: chatID})
   311  		if err != nil {
   312  			return nil, err
   313  		}
   314  
   315  		contact.EnsName = ensName
   316  		contact.ENSVerified = true
   317  		err = m.persistence.SaveContact(contact, nil)
   318  		if err != nil {
   319  			return nil, err
   320  		}
   321  		response.Contacts = []*Contact{contact}
   322  	}
   323  
   324  	chat, ok := m.allChats.Load(chatID)
   325  	if !ok {
   326  		chat = CreateOneToOneChat(chatID, pk, m.getTimesource())
   327  	}
   328  	chat.Active = true
   329  
   330  	filters, err := m.Join(chat)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	// TODO(Samyoul) remove storing of an updated reference pointer?
   336  	m.allChats.Store(chatID, chat)
   337  
   338  	response.AddChat(chat)
   339  
   340  	willSync, err := m.scheduleSyncFilters(filters)
   341  	if err != nil {
   342  		return nil, err
   343  	}
   344  
   345  	// We set the synced to, synced from to the default time
   346  	if !willSync {
   347  		if err := m.initChatSyncFields(chat); err != nil {
   348  			return nil, err
   349  		}
   350  	}
   351  
   352  	err = m.saveChat(chat)
   353  	if err != nil {
   354  		return nil, err
   355  	}
   356  	return response, nil
   357  }
   358  
   359  func (m *Messenger) DeleteChat(chatID string) error {
   360  	return m.deleteChat(chatID)
   361  }
   362  
   363  func (m *Messenger) deleteChat(chatID string) error {
   364  	err := m.persistence.DeleteChat(chatID)
   365  	if err != nil {
   366  		return err
   367  	}
   368  
   369  	// We clean the cache to be able to receive the messages again later
   370  	err = m.transport.ClearProcessedMessageIDsCache()
   371  	if err != nil {
   372  		return err
   373  	}
   374  
   375  	chat, ok := m.allChats.Load(chatID)
   376  
   377  	if ok && chat.Active && chat.Public() {
   378  		m.allChats.Delete(chatID)
   379  		return m.reregisterForPushNotifications()
   380  	}
   381  
   382  	return nil
   383  }
   384  
   385  func (m *Messenger) SaveChat(chat *Chat) error {
   386  	return m.saveChat(chat)
   387  }
   388  
   389  func (m *Messenger) DeactivateChat(request *requests.DeactivateChat) (*MessengerResponse, error) {
   390  	if err := request.Validate(); err != nil {
   391  		return nil, err
   392  	}
   393  
   394  	doClearHistory := !request.PreserveHistory
   395  
   396  	return m.deactivateChat(request.ID, 0, true, doClearHistory)
   397  }
   398  
   399  func (m *Messenger) deactivateChat(chatID string, deactivationClock uint64, shouldBeSynced bool, doClearHistory bool) (*MessengerResponse, error) {
   400  	var response MessengerResponse
   401  	chat, ok := m.allChats.Load(chatID)
   402  	if !ok {
   403  		return nil, ErrChatNotFound
   404  	}
   405  
   406  	// Reset mailserver last request to allow re-fetching messages if joining a chat again
   407  	filters, err := m.filtersForChat(chatID)
   408  	if err != nil && err != ErrNoFiltersForChat {
   409  		return nil, err
   410  	}
   411  
   412  	if m.mailserversDatabase != nil {
   413  		for _, filter := range filters {
   414  			if !filter.Listen || filter.Ephemeral {
   415  				continue
   416  			}
   417  
   418  			err := m.mailserversDatabase.ResetLastRequest(filter.PubsubTopic, filter.ContentTopic.String())
   419  			if err != nil {
   420  				return nil, err
   421  			}
   422  		}
   423  	}
   424  
   425  	if deactivationClock == 0 {
   426  		deactivationClock, _ = chat.NextClockAndTimestamp(m.getTimesource())
   427  	}
   428  
   429  	err = m.persistence.DeactivateChat(chat, deactivationClock, doClearHistory)
   430  
   431  	if err != nil {
   432  		return nil, err
   433  	}
   434  
   435  	// We re-register as our options have changed and we don't want to
   436  	// receive PN from mentions in this chat anymore
   437  	if chat.Public() || chat.ProfileUpdates() {
   438  		err := m.reregisterForPushNotifications()
   439  		if err != nil {
   440  			return nil, err
   441  		}
   442  
   443  		err = m.transport.ClearProcessedMessageIDsCache()
   444  		if err != nil {
   445  			return nil, err
   446  		}
   447  	}
   448  
   449  	// TODO(samyoul) remove storing of an updated reference pointer?
   450  	m.allChats.Store(chatID, chat)
   451  
   452  	response.AddChat(chat)
   453  	// TODO: Remove filters
   454  
   455  	if shouldBeSynced {
   456  		err := m.syncChatRemoving(context.Background(), chat.ID, m.dispatchMessage)
   457  		if err != nil {
   458  			return nil, err
   459  		}
   460  	}
   461  
   462  	return &response, nil
   463  }
   464  
   465  func (m *Messenger) saveChats(chats []*Chat) error {
   466  	err := m.persistence.SaveChats(chats)
   467  	if err != nil {
   468  		return err
   469  	}
   470  	for _, chat := range chats {
   471  		m.allChats.Store(chat.ID, chat)
   472  	}
   473  
   474  	return nil
   475  
   476  }
   477  
   478  func (m *Messenger) saveChat(chat *Chat) error {
   479  	_, ok := m.allChats.Load(chat.ID)
   480  	if chat.OneToOne() {
   481  		name, identicon, err := generateAliasAndIdenticon(chat.ID)
   482  		if err != nil {
   483  			return err
   484  		}
   485  
   486  		chat.Alias = name
   487  		chat.Identicon = identicon
   488  	}
   489  
   490  	// Sync chat if it's a new public, 1-1 or group chat, but not a timeline chat
   491  	if !ok && chat.shouldBeSynced() {
   492  		if err := m.syncChat(context.Background(), chat, m.dispatchMessage); err != nil {
   493  			return err
   494  		}
   495  	}
   496  
   497  	err := m.persistence.SaveChat(*chat)
   498  	if err != nil {
   499  		return err
   500  	}
   501  	// We store the chat has it might not have been in the store in the first place
   502  	m.allChats.Store(chat.ID, chat)
   503  
   504  	return nil
   505  }
   506  
   507  func (m *Messenger) Join(chat *Chat) ([]*transport.Filter, error) {
   508  	switch chat.ChatType {
   509  	case ChatTypeOneToOne:
   510  		pk, err := chat.PublicKey()
   511  		if err != nil {
   512  			return nil, err
   513  		}
   514  
   515  		f, err := m.transport.JoinPrivate(pk)
   516  		if err != nil {
   517  			return nil, err
   518  		}
   519  
   520  		return []*transport.Filter{f}, nil
   521  	case ChatTypePrivateGroupChat:
   522  		members, err := chat.MembersAsPublicKeys()
   523  		if err != nil {
   524  			return nil, err
   525  		}
   526  		return m.transport.JoinGroup(members)
   527  	case ChatTypePublic, ChatTypeProfile, ChatTypeTimeline:
   528  		f, err := m.transport.JoinPublic(chat.ID)
   529  		if err != nil {
   530  			return nil, err
   531  		}
   532  		return []*transport.Filter{f}, nil
   533  	default:
   534  		return nil, errors.New("chat is neither public nor private")
   535  	}
   536  }
   537  
   538  // Deprecated: buildProfileChat shouldn't be used
   539  // and is only left here in case profile chat feature is re-introduced.
   540  func (m *Messenger) buildProfileChat(id string) *Chat {
   541  	// Return nil to prevent usage of deprecated function
   542  	if deprecation.ChatProfileDeprecated {
   543  		return nil
   544  	}
   545  
   546  	// Create the corresponding profile chat
   547  	profileChatID := buildProfileChatID(id)
   548  	profileChat, ok := m.allChats.Load(profileChatID)
   549  
   550  	if !ok {
   551  		profileChat = CreateProfileChat(id, m.getTimesource())
   552  	}
   553  
   554  	return profileChat
   555  
   556  }
   557  
   558  // Deprecated: ensureTimelineChat shouldn't be used
   559  // and is only left here in case profile chat feature is re-introduced.
   560  func (m *Messenger) ensureTimelineChat() error {
   561  	// Return error to prevent usage of deprecated function
   562  	if deprecation.ChatProfileDeprecated {
   563  		return errors.New("timeline chats are deprecated")
   564  	}
   565  
   566  	chat, err := m.persistence.Chat(timelineChatID)
   567  	if err != nil {
   568  		return err
   569  	}
   570  
   571  	if chat != nil {
   572  		return nil
   573  	}
   574  
   575  	chat = CreateTimelineChat(m.getTimesource())
   576  	m.allChats.Store(timelineChatID, chat)
   577  	return m.saveChat(chat)
   578  }
   579  
   580  // Deprecated: ensureMyOwnProfileChat shouldn't be used
   581  // and is only left here in case profile chat feature is re-introduced.
   582  func (m *Messenger) ensureMyOwnProfileChat() error {
   583  	// Return error to prevent usage of deprecated function
   584  	if deprecation.ChatProfileDeprecated {
   585  		return errors.New("profile chats are deprecated")
   586  	}
   587  
   588  	chatID := common.PubkeyToHex(&m.identity.PublicKey)
   589  	_, ok := m.allChats.Load(chatID)
   590  	if ok {
   591  		return nil
   592  	}
   593  
   594  	chat := m.buildProfileChat(chatID)
   595  
   596  	chat.Active = true
   597  
   598  	// Save topics
   599  	_, err := m.Join(chat)
   600  	if err != nil {
   601  		return err
   602  	}
   603  
   604  	return m.saveChat(chat)
   605  }
   606  
   607  func (m *Messenger) ClearHistory(request *requests.ClearHistory) (*MessengerResponse, error) {
   608  	if err := request.Validate(); err != nil {
   609  		return nil, err
   610  	}
   611  
   612  	return m.clearHistory(request.ID)
   613  }
   614  
   615  func (m *Messenger) clearHistory(id string) (*MessengerResponse, error) {
   616  	chat, ok := m.allChats.Load(id)
   617  	if !ok {
   618  		return nil, ErrChatNotFound
   619  	}
   620  
   621  	clock, _ := chat.NextClockAndTimestamp(m.transport)
   622  
   623  	err := m.persistence.ClearHistory(chat, clock)
   624  	if err != nil {
   625  		return nil, err
   626  	}
   627  
   628  	if chat.Public() {
   629  		err = m.transport.ClearProcessedMessageIDsCache()
   630  		if err != nil {
   631  			return nil, err
   632  		}
   633  	}
   634  
   635  	err = m.syncClearHistory(context.Background(), chat, m.dispatchMessage)
   636  	if err != nil {
   637  		return nil, err
   638  	}
   639  
   640  	m.allChats.Store(id, chat)
   641  
   642  	response := &MessengerResponse{}
   643  	response.AddChat(chat)
   644  	return response, nil
   645  }
   646  
   647  func (m *Messenger) FetchMessages(request *requests.FetchMessages) error {
   648  
   649  	if err := request.Validate(); err != nil {
   650  		return err
   651  	}
   652  
   653  	id := request.ID
   654  
   655  	chat, ok := m.allChats.Load(id)
   656  	if !ok {
   657  		return ErrChatNotFound
   658  	}
   659  
   660  	_, err := m.fetchMessages(chat.ID, oneMonthDuration)
   661  	if err != nil {
   662  		return err
   663  	}
   664  
   665  	return nil
   666  }