status-go/protocol/messenger_mailserver.go

945 lines
23 KiB
Go
Raw Normal View History

2021-03-25 15:15:22 +00:00
package protocol
import (
"context"
"fmt"
"math"
"sort"
"sync"
2021-07-27 11:52:10 +00:00
"time"
2021-03-25 15:15:22 +00:00
"github.com/pkg/errors"
"go.uber.org/zap"
2021-05-14 10:55:42 +00:00
"github.com/status-im/status-go/connection"
2021-03-25 15:15:22 +00:00
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/transport"
"github.com/status-im/status-go/services/mailservers"
)
2021-05-14 10:55:42 +00:00
// tolerance is how many seconds of potentially out-of-order messages we want to fetch
var tolerance uint32 = 60
var mailserverRequestTimeout = 30 * time.Second
2021-11-05 15:11:10 +00:00
var oneMonthInSeconds uint32 = 31 * 24 * 60 * 60
var mailserverMaxTries uint = 2
var mailserverMaxFailedRequests uint = 2
2021-05-14 10:55:42 +00:00
// maxTopicsPerRequest sets the batch size to limit the number of topics per store query
var maxTopicsPerRequest int = 10
var ErrNoFiltersForChat = errors.New("no filter registered for given chat")
2021-05-14 10:55:42 +00:00
func (m *Messenger) shouldSync() (bool, error) {
if m.mailserverCycle.activeMailserver == nil || !m.online() {
2021-05-14 10:55:42 +00:00
return false, nil
}
2021-03-25 15:15:22 +00:00
2021-05-14 10:55:42 +00:00
useMailserver, err := m.settings.CanUseMailservers()
2021-03-25 15:15:22 +00:00
if err != nil {
m.logger.Error("failed to get use mailservers", zap.Error(err))
2021-05-14 10:55:42 +00:00
return false, err
2021-03-25 15:15:22 +00:00
}
2021-05-14 10:55:42 +00:00
if !useMailserver {
return false, nil
}
if !m.connectionState.IsExpensive() {
return true, nil
}
syncingOnMobileNetwork, err := m.settings.CanSyncOnMobileNetwork()
if err != nil {
return false, err
}
return syncingOnMobileNetwork, nil
}
func (m *Messenger) scheduleSyncChat(chat *Chat) (bool, error) {
shouldSync, err := m.shouldSync()
if err != nil {
m.logger.Error("failed to get should sync", zap.Error(err))
return false, err
}
if !shouldSync {
return false, nil
2021-03-25 15:15:22 +00:00
}
go func() {
_, err := m.performMailserverRequest(func() (*MessengerResponse, error) {
response, err := m.syncChat(chat.ID)
2021-03-25 15:15:22 +00:00
if err != nil {
m.logger.Error("failed to sync chat", zap.Error(err))
return nil, err
}
2021-03-25 15:15:22 +00:00
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.MessengerResponse(response)
}
return response, nil
})
if err != nil {
m.logger.Error("failed to perform mailserver request", zap.Error(err))
2021-03-25 15:15:22 +00:00
}
}()
2021-05-14 10:55:42 +00:00
return true, nil
2021-03-25 15:15:22 +00:00
}
func (m *Messenger) connectToNewMailserverAndWait() error {
// Handle pinned mailservers
m.logger.Info("disconnecting mailserver")
pinnedMailserver, err := m.getPinnedMailserver()
2021-05-14 10:55:42 +00:00
if err != nil {
m.logger.Error("could not obtain the pinned mailserver", zap.Error(err))
return err
}
// If pinned mailserver is not nil, no need to disconnect and wait for it to be available
if pinnedMailserver == nil {
m.disconnectActiveMailserver()
2021-05-14 10:55:42 +00:00
}
2021-03-25 15:15:22 +00:00
return m.findNewMailserver()
}
func (m *Messenger) performMailserverRequest(fn func() (*MessengerResponse, error)) (*MessengerResponse, error) {
m.mailserverCycle.Lock()
defer m.mailserverCycle.Unlock()
var tries uint = 0
for tries < mailserverMaxTries {
if !m.isActiveMailserverAvailable() {
return nil, errors.New("mailserver not available")
}
m.logger.Info("trying performing mailserver requests", zap.Uint("try", tries))
activeMailserver := m.getActiveMailserver()
// Make sure we are connected to a mailserver
if activeMailserver == nil {
return nil, errors.New("mailserver not available")
}
// Peform request
response, err := fn()
if err == nil {
// Reset failed requests
activeMailserver.FailedRequests = 0
return response, nil
}
tries++
// Increment failed requests
activeMailserver.FailedRequests++
// Change mailserver
if activeMailserver.FailedRequests >= mailserverMaxFailedRequests {
return nil, errors.New("too many failed requests")
}
// Wait a couple of second not to spam
time.Sleep(2 * time.Second)
}
return nil, errors.New("failed to perform mailserver request")
2021-03-25 15:15:22 +00:00
}
2021-10-27 10:59:43 +00:00
2021-05-14 10:55:42 +00:00
func (m *Messenger) scheduleSyncFilters(filters []*transport.Filter) (bool, error) {
shouldSync, err := m.shouldSync()
2021-03-25 15:15:22 +00:00
if err != nil {
2021-05-14 10:55:42 +00:00
m.logger.Error("failed to get shouldSync", zap.Error(err))
return false, err
2021-03-25 15:15:22 +00:00
}
2021-05-14 10:55:42 +00:00
if !shouldSync {
return false, nil
2021-03-25 15:15:22 +00:00
}
go func() {
_, err := m.performMailserverRequest(func() (*MessengerResponse, error) {
response, err := m.syncFilters(filters)
2021-03-25 15:15:22 +00:00
if err != nil {
m.logger.Error("failed to sync filter", zap.Error(err))
return nil, err
}
2021-03-25 15:15:22 +00:00
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.MessengerResponse(response)
}
return response, nil
})
if err != nil {
m.logger.Error("failed to perform mailserver request", zap.Error(err))
2021-03-25 15:15:22 +00:00
}
}()
2021-05-14 10:55:42 +00:00
return true, nil
2021-03-25 15:15:22 +00:00
}
func (m *Messenger) calculateMailserverTo() uint32 {
return uint32(m.getTimesource().GetCurrentTime() / 1000)
}
func (m *Messenger) filtersForChat(chatID string) ([]*transport.Filter, error) {
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
var filters []*transport.Filter
if chat.OneToOne() {
// We sync our own topic and any eventual negotiated
publicKeys := []string{common.PubkeyToHex(&m.identity.PublicKey), chatID}
filters = m.transport.FiltersByIdentities(publicKeys)
} else if chat.PrivateGroupChat() {
var publicKeys []string
for _, m := range chat.Members {
publicKeys = append(publicKeys, m.ID)
}
filters = m.transport.FiltersByIdentities(publicKeys)
} else {
filter := m.transport.FilterByChatID(chatID)
if filter == nil {
return nil, ErrNoFiltersForChat
2021-03-25 15:15:22 +00:00
}
filters = []*transport.Filter{filter}
}
return filters, nil
}
func (m *Messenger) topicsForChat(chatID string) ([]types.TopicType, error) {
filters, err := m.filtersForChat(chatID)
if err != nil {
return nil, err
}
var topics []types.TopicType
for _, filter := range filters {
topics = append(topics, filter.Topic)
}
return topics, nil
}
// Assume is a public chat for now
func (m *Messenger) syncChat(chatID string) (*MessengerResponse, error) {
filters, err := m.filtersForChat(chatID)
if err != nil {
return nil, err
}
return m.syncFilters(filters)
}
2021-10-27 10:59:43 +00:00
func (m *Messenger) syncBackup() error {
filter := m.transport.PersonalTopicFilter()
if filter == nil {
return errors.New("personal topic filter not loaded")
}
to := m.calculateMailserverTo()
from := uint32(m.getTimesource().GetCurrentTime()/1000) - oneMonthInSeconds
batch := MailserverBatch{From: from, To: to, Topics: []types.TopicType{filter.Topic}}
err := m.processMailserverBatch(batch)
if err != nil {
return err
}
return m.settings.SetBackupFetched(true)
}
2021-06-01 09:29:37 +00:00
func (m *Messenger) defaultSyncPeriodFromNow() (uint32, error) {
defaultSyncPeriod, err := m.settings.GetDefaultSyncPeriod()
if err != nil {
return 0, err
}
2021-05-31 14:35:14 +00:00
return uint32(m.getTimesource().GetCurrentTime()/1000) - defaultSyncPeriod, nil
2021-03-25 15:15:22 +00:00
}
2021-06-01 09:29:37 +00:00
// capToDefaultSyncPeriod caps the sync period to the default
func (m *Messenger) capToDefaultSyncPeriod(period uint32) (uint32, error) {
d, err := m.defaultSyncPeriodFromNow()
if err != nil {
return 0, err
}
2021-03-25 15:15:22 +00:00
if d > period {
return d, nil
2021-03-25 15:15:22 +00:00
}
return period - tolerance, nil
2021-03-25 15:15:22 +00:00
}
func (m *Messenger) updateFiltersPriority(filters []*transport.Filter) {
for _, filter := range filters {
chatID := filter.ChatID
chat := m.Chat(chatID)
if chat != nil {
filter.Priority = chat.ReadMessagesAtClockValue
}
}
}
func (m *Messenger) resetFiltersPriority(filters []*transport.Filter) {
for _, filter := range filters {
filter.Priority = 0
}
}
func (m *Messenger) RequestAllHistoricMessagesWithRetries() (*MessengerResponse, error) {
return m.performMailserverRequest(m.RequestAllHistoricMessages)
}
2021-03-25 15:15:22 +00:00
// RequestAllHistoricMessages requests all the historic messages for any topic
func (m *Messenger) RequestAllHistoricMessages() (*MessengerResponse, error) {
2021-05-14 10:55:42 +00:00
shouldSync, err := m.shouldSync()
2021-03-25 15:15:22 +00:00
if err != nil {
return nil, err
}
2021-05-14 10:55:42 +00:00
if !shouldSync {
2021-03-25 15:15:22 +00:00
return nil, nil
}
2021-10-27 10:59:43 +00:00
backupFetched, err := m.settings.BackupFetched()
if err != nil {
return nil, err
}
if !backupFetched {
m.logger.Info("fetching backup")
err := m.syncBackup()
if err != nil {
return nil, err
}
m.logger.Info("backup fetched")
}
filters := m.transport.Filters()
m.updateFiltersPriority(filters)
defer m.resetFiltersPriority(filters)
response, err := m.syncFilters(filters)
if err != nil {
return nil, err
}
return response, nil
}
func getPrioritizedBatches() []int {
return []int{1, 5, 10}
2021-03-25 15:15:22 +00:00
}
func (m *Messenger) syncFiltersFrom(filters []*transport.Filter, lastRequest uint32) (*MessengerResponse, error) {
2021-03-25 15:15:22 +00:00
response := &MessengerResponse{}
topicInfo, err := m.mailserversDatabase.Topics()
if err != nil {
return nil, err
}
topicsData := make(map[string]mailservers.MailserverTopic)
for _, topic := range topicInfo {
topicsData[topic.Topic] = topic
}
batches := make(map[int]MailserverBatch)
to := m.calculateMailserverTo()
var syncedTopics []mailservers.MailserverTopic
sort.Slice(filters[:], func(i, j int) bool {
p1 := filters[i].Priority
p2 := filters[j].Priority
return p1 > p2
})
prioritizedBatches := getPrioritizedBatches()
currentBatch := 0
if len(filters) == 0 || filters[0].Priority == 0 {
currentBatch = len(prioritizedBatches)
}
defaultPeriodFromNow, err := m.defaultSyncPeriodFromNow()
if err != nil {
return nil, err
}
2021-03-25 15:15:22 +00:00
for _, filter := range filters {
if !filter.Listen || filter.Ephemeral {
continue
}
var chatID string
// If the filter has an identity, we use it as a chatID, otherwise is a public chat/community chat filter
if len(filter.Identity) != 0 {
chatID = filter.Identity
} else {
chatID = filter.ChatID
}
topicData, ok := topicsData[filter.Topic.String()]
var capToDefaultSyncPeriod = true
2021-03-25 15:15:22 +00:00
if !ok {
if lastRequest == 0 {
lastRequest = defaultPeriodFromNow
}
2021-03-25 15:15:22 +00:00
topicData = mailservers.MailserverTopic{
Topic: filter.Topic.String(),
LastRequest: int(defaultPeriodFromNow),
2021-03-25 15:15:22 +00:00
}
} else if lastRequest != 0 {
topicData.LastRequest = int(lastRequest)
capToDefaultSyncPeriod = false
2021-03-25 15:15:22 +00:00
}
batchID := topicData.LastRequest
if currentBatch < len(prioritizedBatches) {
batch, ok := batches[currentBatch]
if ok {
prevTopicData, ok := topicsData[batch.Topics[0].String()]
if (!ok && topicData.LastRequest != int(defaultPeriodFromNow)) ||
(ok && prevTopicData.LastRequest != topicData.LastRequest) {
currentBatch++
}
}
if currentBatch < len(prioritizedBatches) {
batchID = currentBatch
currentBatchCap := prioritizedBatches[currentBatch] - 1
if currentBatchCap == 0 {
currentBatch++
} else {
prioritizedBatches[currentBatch] = currentBatchCap
}
}
}
batch, ok := batches[batchID]
2021-03-25 15:15:22 +00:00
if !ok {
from := uint32(topicData.LastRequest)
if capToDefaultSyncPeriod {
from, err = m.capToDefaultSyncPeriod(uint32(topicData.LastRequest))
if err != nil {
return nil, err
}
}
2021-03-25 15:15:22 +00:00
batch = MailserverBatch{From: from, To: to}
}
batch.ChatIDs = append(batch.ChatIDs, chatID)
batch.Topics = append(batch.Topics, filter.Topic)
batches[batchID] = batch
2021-03-25 15:15:22 +00:00
// Set last request to the new `to`
topicData.LastRequest = int(to)
syncedTopics = append(syncedTopics, topicData)
}
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.HistoryRequestStarted(len(batches))
}
batchKeys := make([]int, 0, len(batches))
for k := range batches {
batchKeys = append(batchKeys, k)
}
sort.Ints(batchKeys)
var batches24h []MailserverBatch
keysToIterate := append([]int{}, batchKeys...)
for {
// For all batches
var tmpKeysToIterate []int
for _, k := range keysToIterate {
batch := batches[k]
dayBatch := MailserverBatch{
To: batch.To,
Cursor: batch.Cursor,
Topics: batch.Topics,
ChatIDs: batch.ChatIDs,
}
from := batch.To - 86400
if from > batch.From {
dayBatch.From = from
batches24h = append(batches24h, dayBatch)
// Replace og batch with new dates
batch.To = from
batches[k] = batch
tmpKeysToIterate = append(tmpKeysToIterate, k)
} else {
batches24h = append(batches24h, batch)
}
}
if len(tmpKeysToIterate) == 0 {
break
}
keysToIterate = tmpKeysToIterate
}
i := 0
for _, batch := range batches24h {
i++
2021-03-25 15:15:22 +00:00
err := m.processMailserverBatch(batch)
if err != nil {
m.logger.Error("error syncing topics", zap.Error(err))
2021-03-25 15:15:22 +00:00
return nil, err
}
}
2021-10-29 14:29:28 +00:00
m.logger.Debug("topics synced")
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.HistoryRequestCompleted()
}
2021-03-25 15:15:22 +00:00
err = m.mailserversDatabase.AddTopics(syncedTopics)
if err != nil {
return nil, err
}
var messagesToBeSaved []*common.Message
for _, batch := range batches {
for _, id := range batch.ChatIDs {
chat, ok := m.allChats.Load(id)
if !ok || !chat.Active || chat.Timeline() || chat.ProfileUpdates() {
continue
}
gap, err := m.calculateGapForChat(chat, batch.From)
if err != nil {
return nil, err
}
if chat.SyncedFrom == 0 || chat.SyncedFrom > batch.From {
chat.SyncedFrom = batch.From
}
chat.SyncedTo = to
err = m.persistence.SetSyncTimestamps(chat.SyncedFrom, chat.SyncedTo, chat.ID)
if err != nil {
return nil, err
}
response.AddChat(chat)
if gap != nil {
response.AddMessage(gap)
messagesToBeSaved = append(messagesToBeSaved, gap)
}
}
}
if len(messagesToBeSaved) > 0 {
err := m.persistence.SaveMessages(messagesToBeSaved)
if err != nil {
return nil, err
}
}
return response, nil
}
func (m *Messenger) syncFilters(filters []*transport.Filter) (*MessengerResponse, error) {
return m.syncFiltersFrom(filters, 0)
}
2021-03-25 15:15:22 +00:00
func (m *Messenger) calculateGapForChat(chat *Chat, from uint32) (*common.Message, error) {
// Chat was never synced, no gap necessary
if chat.SyncedTo == 0 {
return nil, nil
}
// If we filled the gap, nothing to do
if chat.SyncedTo >= from {
return nil, nil
}
timestamp := m.getTimesource().GetCurrentTime()
message := &common.Message{
ChatMessage: protobuf.ChatMessage{
ChatId: chat.ID,
Text: "Gap message",
MessageType: protobuf.MessageType_SYSTEM_MESSAGE_GAP,
ContentType: protobuf.ChatMessage_SYSTEM_MESSAGE_GAP,
Clock: uint64(from) * 1000,
Timestamp: timestamp,
},
GapParameters: &common.GapParameters{
From: chat.SyncedTo,
To: from,
},
From: common.PubkeyToHex(&m.identity.PublicKey),
WhisperTimestamp: timestamp,
LocalChatID: chat.ID,
Seen: true,
ID: types.EncodeHex(crypto.Keccak256([]byte(fmt.Sprintf("%s-%d-%d", chat.ID, chat.SyncedTo, from)))),
}
return message, m.persistence.SaveMessages([]*common.Message{message})
}
type work struct {
topics []types.TopicType
cursor []byte
storeCursor *types.StoreRequestCursor
}
2023-04-27 17:42:31 +00:00
type messageRequester interface {
SendMessagesRequestForTopics(
ctx context.Context,
peerID []byte,
from, to uint32,
previousCursor []byte,
previousStoreCursor *types.StoreRequestCursor,
topics []types.TopicType,
waitForResponse bool,
) (cursor []byte, storeCursor *types.StoreRequestCursor, err error)
}
func processMailserverBatch(ctx context.Context, messageRequester messageRequester, batch MailserverBatch, mailserverID []byte, logger *zap.Logger) error {
2021-10-29 14:29:28 +00:00
var topicStrings []string
for _, t := range batch.Topics {
topicStrings = append(topicStrings, t.String())
}
2023-04-27 17:42:31 +00:00
logger = logger.With(zap.Any("chatIDs", batch.ChatIDs), zap.String("fromString", time.Unix(int64(batch.From), 0).Format(time.RFC3339)), zap.String("toString", time.Unix(int64(batch.To), 0).Format(time.RFC3339)), zap.Any("topic", topicStrings), zap.Int64("from", int64(batch.From)), zap.Int64("to", int64(batch.To)))
2021-10-29 14:29:28 +00:00
logger.Info("syncing topic")
2021-07-27 11:52:10 +00:00
wg := sync.WaitGroup{}
workWg := sync.WaitGroup{}
workCh := make(chan work, 1000) // each batch item is split in 10 topics bunch and sent to this channel
workCompleteCh := make(chan struct{}) // once all batch items are processed, this channel is triggered
semaphore := make(chan int, 3) // limit the number of concurrent queries
2023-04-27 17:42:31 +00:00
errCh := make(chan error)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// Producer
wg.Add(1)
go func() {
2023-04-27 17:42:31 +00:00
defer func() {
logger.Debug("mailserver batch producer complete")
wg.Done()
2023-04-27 17:42:31 +00:00
}()
allWorks := int(math.Ceil(float64(len(batch.Topics)) / float64(maxTopicsPerRequest)))
workWg.Add(allWorks)
for i := 0; i < len(batch.Topics); i += maxTopicsPerRequest {
j := i + maxTopicsPerRequest
if j > len(batch.Topics) {
j = len(batch.Topics)
}
select {
2023-04-27 17:42:31 +00:00
case <-ctx.Done():
logger.Debug("processBatch producer - context done")
return
default:
logger.Debug("processBatch producer - creating work")
workCh <- work{
topics: batch.Topics[i:j],
}
time.Sleep(50 * time.Millisecond)
}
}
go func() {
workWg.Wait()
workCompleteCh <- struct{}{}
}()
logger.Debug("processBatch producer complete")
}()
var result error
loop:
for {
select {
case <-ctx.Done():
logger.Debug("processBatch cleanup - context done")
result = ctx.Err()
if errors.Is(result, context.Canceled) {
result = nil
}
break loop
case w, ok := <-workCh:
if !ok {
continue
}
logger.Debug("processBatch - received work")
semaphore <- 1
go func(w work) { // Consumer
defer func() {
workWg.Done()
<-semaphore
}()
queryCtx, queryCancel := context.WithTimeout(ctx, mailserverRequestTimeout)
cursor, storeCursor, err := messageRequester.SendMessagesRequestForTopics(queryCtx, mailserverID, batch.From, batch.To, w.cursor, w.storeCursor, w.topics, true)
queryCancel()
2021-03-25 15:15:22 +00:00
if err != nil {
logger.Debug("failed to send request", zap.Error(err))
2023-04-27 17:42:31 +00:00
errCh <- err
return
}
if len(cursor) != 0 || storeCursor != nil {
logger.Debug("processBatch producer - creating work (cursor)")
workWg.Add(1)
workCh <- work{
topics: w.topics,
cursor: cursor,
storeCursor: storeCursor,
}
}
}(w)
case err := <-errCh:
logger.Debug("processBatch - received error", zap.Error(err))
cancel() // Kill go routines
return err
case <-workCompleteCh:
logger.Debug("processBatch - all jobs complete")
cancel() // Kill go routines
}
2021-03-25 15:15:22 +00:00
}
wg.Wait()
// NOTE(camellos): Disabling for now, not critical and I'd rather take a bit more time
// to test it
//logger.Info("waiting until message processed")
//m.waitUntilP2PMessagesProcessed()
2023-04-27 17:42:31 +00:00
logger.Info("synced topic", zap.NamedError("hasError", result))
return result
}
func (m *Messenger) processMailserverBatch(batch MailserverBatch) error {
mailserverID, err := m.activeMailserverID()
if err != nil {
return err
}
return processMailserverBatch(m.ctx, m.transport, batch, mailserverID, m.logger)
2021-03-25 15:15:22 +00:00
}
type MailserverBatch struct {
From uint32
To uint32
Cursor string
Topics []types.TopicType
ChatIDs []string
}
func (m *Messenger) RequestHistoricMessagesForFilter(
ctx context.Context,
from, to uint32,
cursor []byte,
previousStoreCursor *types.StoreRequestCursor,
2021-03-25 15:15:22 +00:00
filter *transport.Filter,
waitForResponse bool,
) ([]byte, *types.StoreRequestCursor, error) {
activeMailserverID, err := m.activeMailserverID()
if err != nil {
return nil, nil, err
2021-03-25 15:15:22 +00:00
}
if activeMailserverID == nil {
m.cycleMailservers()
activeMailserverID, err = m.activeMailserverID()
if err != nil {
return nil, nil, err
}
if activeMailserverID == nil {
return nil, nil, errors.New("no mailserver selected")
}
}
return m.transport.SendMessagesRequestForFilter(ctx, activeMailserverID, from, to, cursor, previousStoreCursor, filter, waitForResponse)
2021-03-25 15:15:22 +00:00
}
func (m *Messenger) SyncChatFromSyncedFrom(chatID string) (uint32, error) {
var from uint32
_, err := m.performMailserverRequest(func() (*MessengerResponse, error) {
topics, err := m.topicsForChat(chatID)
if err != nil {
return nil, nil
}
2021-03-25 15:15:22 +00:00
chat, ok := m.allChats.Load(chatID)
if !ok {
return nil, ErrChatNotFound
}
2021-03-25 15:15:22 +00:00
defaultSyncPeriod, err := m.settings.GetDefaultSyncPeriod()
if err != nil {
return nil, err
}
batch := MailserverBatch{
ChatIDs: []string{chatID},
To: chat.SyncedFrom,
From: chat.SyncedFrom - defaultSyncPeriod,
Topics: topics,
}
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.HistoryRequestStarted(1)
}
2021-03-25 15:15:22 +00:00
err = m.processMailserverBatch(batch)
if err != nil {
return nil, err
}
2021-03-25 15:15:22 +00:00
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.HistoryRequestCompleted()
}
if chat.SyncedFrom == 0 || chat.SyncedFrom > batch.From {
chat.SyncedFrom = batch.From
}
m.logger.Debug("setting sync timestamps", zap.Int64("from", int64(batch.From)), zap.Int64("to", int64(chat.SyncedTo)), zap.String("chatID", chatID))
err = m.persistence.SetSyncTimestamps(batch.From, chat.SyncedTo, chat.ID)
from = batch.From
return nil, err
})
2021-03-25 15:15:22 +00:00
if err != nil {
return 0, err
}
return from, nil
2021-03-25 15:15:22 +00:00
}
func (m *Messenger) FillGaps(chatID string, messageIDs []string) error {
messages, err := m.persistence.MessagesByIDs(messageIDs)
if err != nil {
return err
}
_, ok := m.allChats.Load(chatID)
if !ok {
return errors.New("chat not existing")
}
topics, err := m.topicsForChat(chatID)
if err != nil {
return err
}
var lowestFrom, highestTo uint32
for _, message := range messages {
if message.GapParameters == nil {
return errors.New("can't sync non-gap message")
}
if lowestFrom == 0 || lowestFrom > message.GapParameters.From {
lowestFrom = message.GapParameters.From
}
if highestTo < message.GapParameters.To {
highestTo = message.GapParameters.To
}
}
batch := MailserverBatch{
ChatIDs: []string{chatID},
To: highestTo,
From: lowestFrom,
Topics: topics,
}
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.HistoryRequestStarted(1)
}
2021-03-25 15:15:22 +00:00
err = m.processMailserverBatch(batch)
if err != nil {
return err
}
if m.config.messengerSignalsHandler != nil {
m.config.messengerSignalsHandler.HistoryRequestCompleted()
}
2021-03-25 15:15:22 +00:00
return m.persistence.DeleteMessages(messageIDs)
}
func (m *Messenger) waitUntilP2PMessagesProcessed() { // nolint: unused
2021-11-26 12:30:35 +00:00
ticker := time.NewTicker(50 * time.Millisecond)
for { //nolint: gosimple
select {
case <-ticker.C:
if !m.transport.ProcessingP2PMessages() {
ticker.Stop()
return
}
}
}
}
2021-03-25 15:15:22 +00:00
func (m *Messenger) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) {
return m.transport.LoadFilters(filters)
}
func (m *Messenger) ToggleUseMailservers(value bool) error {
m.mailserverCycle.Lock()
defer m.mailserverCycle.Unlock()
err := m.settings.SetUseMailservers(value)
if err != nil {
return err
}
if value {
m.cycleMailservers()
return nil
}
m.disconnectActiveMailserver()
return nil
}
func (m *Messenger) SetPinnedMailservers(mailservers map[string]string) error {
err := m.settings.SetPinnedMailservers(mailservers)
if err != nil {
return err
}
m.cycleMailservers()
return nil
}
2021-03-25 15:15:22 +00:00
func (m *Messenger) RemoveFilters(filters []*transport.Filter) error {
return m.transport.RemoveFilters(filters)
}
2021-05-14 10:55:42 +00:00
func (m *Messenger) ConnectionChanged(state connection.State) {
m.transport.ConnectionChanged(state)
2021-10-21 12:39:19 +00:00
if !m.connectionState.Offline && state.Offline {
m.sender.StopDatasync()
}
if m.connectionState.Offline && !state.Offline {
m.sender.StartDatasync()
}
2021-05-14 10:55:42 +00:00
m.connectionState = state
}