2019-11-21 16:19:22 +00:00
|
|
|
package protocol
|
2019-07-17 22:25:42 +00:00
|
|
|
|
|
|
|
import (
|
2020-12-10 10:12:51 +00:00
|
|
|
"bytes"
|
2019-07-17 22:25:42 +00:00
|
|
|
"context"
|
|
|
|
"crypto/ecdsa"
|
2020-07-14 14:07:19 +00:00
|
|
|
"database/sql"
|
2020-08-07 13:49:37 +00:00
|
|
|
"encoding/hex"
|
2022-08-24 12:06:48 +00:00
|
|
|
"encoding/json"
|
2020-12-02 14:00:28 +00:00
|
|
|
"fmt"
|
2022-08-08 15:22:22 +00:00
|
|
|
"image"
|
2020-05-13 13:16:17 +00:00
|
|
|
"io/ioutil"
|
2020-12-15 14:43:41 +00:00
|
|
|
"math"
|
2019-12-02 15:34:05 +00:00
|
|
|
"math/rand"
|
2020-05-13 13:16:17 +00:00
|
|
|
"os"
|
2020-07-07 13:55:24 +00:00
|
|
|
"reflect"
|
2022-09-20 09:55:59 +00:00
|
|
|
"strings"
|
2019-12-02 15:34:05 +00:00
|
|
|
"sync"
|
2019-07-17 22:25:42 +00:00
|
|
|
"time"
|
|
|
|
|
2022-07-05 19:49:44 +00:00
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
|
|
"github.com/golang/protobuf/proto"
|
2022-08-24 15:14:09 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"go.uber.org/zap"
|
2022-07-05 19:49:44 +00:00
|
|
|
|
2021-11-26 12:30:35 +00:00
|
|
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
2022-08-24 15:14:09 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
2022-06-15 14:49:31 +00:00
|
|
|
"github.com/ethereum/go-ethereum/event"
|
2022-01-12 16:02:01 +00:00
|
|
|
"github.com/ethereum/go-ethereum/p2p"
|
2021-03-31 16:23:45 +00:00
|
|
|
"github.com/status-im/status-go/appdatabase"
|
2021-09-01 12:02:18 +00:00
|
|
|
"github.com/status-im/status-go/appmetrics"
|
2021-05-14 10:55:42 +00:00
|
|
|
"github.com/status-im/status-go/connection"
|
2022-06-15 14:49:31 +00:00
|
|
|
"github.com/status-im/status-go/contracts"
|
2019-11-23 17:57:05 +00:00
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2020-11-05 17:35:20 +00:00
|
|
|
userimage "github.com/status-im/status-go/images"
|
2020-11-25 00:34:32 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts"
|
2021-03-31 16:23:45 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
2022-03-23 18:47:00 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts/settings"
|
2021-09-01 12:02:18 +00:00
|
|
|
"github.com/status-im/status-go/protocol/anonmetrics"
|
2020-06-17 18:55:49 +00:00
|
|
|
"github.com/status-im/status-go/protocol/audio"
|
2020-07-22 07:41:40 +00:00
|
|
|
"github.com/status-im/status-go/protocol/common"
|
2020-11-18 09:16:51 +00:00
|
|
|
"github.com/status-im/status-go/protocol/communities"
|
2019-11-21 16:19:22 +00:00
|
|
|
"github.com/status-im/status-go/protocol/encryption"
|
|
|
|
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
|
|
|
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
2021-01-11 10:32:51 +00:00
|
|
|
"github.com/status-im/status-go/protocol/ens"
|
2022-08-02 12:56:26 +00:00
|
|
|
"github.com/status-im/status-go/protocol/identity"
|
2019-11-21 16:19:22 +00:00
|
|
|
"github.com/status-im/status-go/protocol/identity/alias"
|
|
|
|
"github.com/status-im/status-go/protocol/identity/identicon"
|
2020-05-13 13:16:17 +00:00
|
|
|
"github.com/status-im/status-go/protocol/images"
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
2020-07-22 07:41:40 +00:00
|
|
|
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
|
|
|
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
2022-03-10 21:05:17 +00:00
|
|
|
"github.com/status-im/status-go/protocol/requests"
|
2019-11-21 16:19:22 +00:00
|
|
|
"github.com/status-im/status-go/protocol/sqlite"
|
2020-01-13 19:17:30 +00:00
|
|
|
"github.com/status-im/status-go/protocol/transport"
|
2022-07-05 19:49:44 +00:00
|
|
|
"github.com/status-im/status-go/protocol/verification"
|
2022-02-23 14:34:16 +00:00
|
|
|
"github.com/status-im/status-go/server"
|
2022-06-15 14:49:31 +00:00
|
|
|
"github.com/status-im/status-go/services/browsers"
|
2022-01-12 16:02:01 +00:00
|
|
|
"github.com/status-im/status-go/services/ext/mailservers"
|
|
|
|
mailserversDB "github.com/status-im/status-go/services/mailservers"
|
2022-09-14 10:46:11 +00:00
|
|
|
"github.com/status-im/status-go/services/wallet"
|
2021-11-03 12:38:37 +00:00
|
|
|
"github.com/status-im/status-go/telemetry"
|
2019-07-17 22:25:42 +00:00
|
|
|
)
|
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
const maxChunkSizeMessages = 1000
|
|
|
|
const maxChunkSizeBytes = 1500000
|
|
|
|
|
2022-09-28 11:42:17 +00:00
|
|
|
// todo: kozieiev: get rid of wakutransp word
|
2020-12-15 16:29:44 +00:00
|
|
|
type chatContext string
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
|
2020-12-15 16:29:44 +00:00
|
|
|
const (
|
|
|
|
PubKeyStringLength = 132
|
|
|
|
|
|
|
|
transactionSentTxt = "Transaction sent"
|
|
|
|
|
|
|
|
publicChat chatContext = "public-chat"
|
|
|
|
privateChat chatContext = "private-chat"
|
2022-05-27 11:21:02 +00:00
|
|
|
|
|
|
|
maxChatMessageImageSize = 400000
|
|
|
|
resizeTargetImageSize = 350000
|
|
|
|
idealTargetImageSize = 50000
|
2020-12-15 16:29:44 +00:00
|
|
|
)
|
2020-02-10 11:22:37 +00:00
|
|
|
|
2021-11-17 09:11:51 +00:00
|
|
|
const messageResendMinDelay = 30
|
|
|
|
const messageResendMaxCount = 3
|
2020-12-15 14:43:41 +00:00
|
|
|
|
2020-11-18 09:16:51 +00:00
|
|
|
var communityAdvertiseIntervalSecond int64 = 60 * 60
|
|
|
|
|
2021-01-08 15:21:25 +00:00
|
|
|
// messageCacheIntervalMs is how long we should keep processed messages in the cache, in ms
|
|
|
|
var messageCacheIntervalMs uint64 = 1000 * 60 * 60 * 48
|
|
|
|
|
2019-07-17 22:25:42 +00:00
|
|
|
// Messenger is a entity managing chats and messages.
|
|
|
|
// It acts as a bridge between the application and encryption
|
|
|
|
// layers.
|
|
|
|
// It needs to expose an interface to manage installations
|
|
|
|
// because installations are managed by the user.
|
|
|
|
// Similarly, it needs to expose an interface to manage
|
|
|
|
// mailservers because they can also be managed by the user.
|
|
|
|
type Messenger struct {
|
2022-08-24 12:06:48 +00:00
|
|
|
node types.Node
|
|
|
|
server *p2p.Server
|
|
|
|
peerStore *mailservers.PeerStore
|
|
|
|
config *config
|
|
|
|
identity *ecdsa.PrivateKey
|
|
|
|
persistence *sqlitePersistence
|
|
|
|
transport *transport.Transport
|
|
|
|
encryptor *encryption.Protocol
|
|
|
|
sender *common.MessageSender
|
|
|
|
ensVerifier *ens.Verifier
|
|
|
|
anonMetricsClient *anonmetrics.Client
|
|
|
|
anonMetricsServer *anonmetrics.Server
|
|
|
|
pushNotificationClient *pushnotificationclient.Client
|
|
|
|
pushNotificationServer *pushnotificationserver.Server
|
|
|
|
communitiesManager *communities.Manager
|
|
|
|
logger *zap.Logger
|
|
|
|
|
|
|
|
outputCSV bool
|
|
|
|
csvFile *os.File
|
|
|
|
|
2022-06-02 12:17:52 +00:00
|
|
|
verifyTransactionClient EthClient
|
|
|
|
featureFlags common.FeatureFlags
|
|
|
|
shutdownTasks []func() error
|
|
|
|
shouldPublishContactCode bool
|
|
|
|
systemMessagesTranslations *systemMessageTranslationsMap
|
|
|
|
allChats *chatMap
|
|
|
|
allContacts *contactMap
|
|
|
|
allInstallations *installationMap
|
|
|
|
modifiedInstallations *stringBoolMap
|
|
|
|
installationID string
|
|
|
|
mailserverCycle mailserverCycle
|
|
|
|
database *sql.DB
|
|
|
|
multiAccounts *multiaccounts.Database
|
|
|
|
mailservers *mailserversDB.Database
|
|
|
|
settings *accounts.Database
|
|
|
|
account *multiaccounts.Account
|
|
|
|
mailserversDatabase *mailserversDB.Database
|
|
|
|
browserDatabase *browsers.Database
|
2022-06-15 14:49:31 +00:00
|
|
|
httpServer *server.MediaServer
|
2022-06-02 12:17:52 +00:00
|
|
|
quit chan struct{}
|
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
importingCommunities map[string]bool
|
|
|
|
|
2022-06-02 12:17:52 +00:00
|
|
|
requestedCommunitiesLock sync.RWMutex
|
|
|
|
requestedCommunities map[string]*transport.Filter
|
|
|
|
|
2022-04-22 07:42:22 +00:00
|
|
|
connectionState connection.State
|
|
|
|
telemetryClient *telemetry.Client
|
2022-06-02 12:17:52 +00:00
|
|
|
contractMaker *contracts.ContractMaker
|
2022-04-22 07:42:22 +00:00
|
|
|
downloadHistoryArchiveTasksWaitGroup sync.WaitGroup
|
2022-07-05 19:49:44 +00:00
|
|
|
verificationDatabase *verification.Persistence
|
2022-09-14 10:46:11 +00:00
|
|
|
savedAddressesManager *wallet.SavedAddressesManager
|
2022-07-05 19:49:44 +00:00
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) Determine if/how the remaining usage of this mutex can be removed
|
2022-04-22 07:42:22 +00:00
|
|
|
mutex sync.Mutex
|
|
|
|
mailPeersMutex sync.Mutex
|
|
|
|
handleMessagesMutex sync.Mutex
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 16:02:01 +00:00
|
|
|
type connStatus int
|
|
|
|
|
|
|
|
const (
|
|
|
|
disconnected connStatus = iota + 1
|
|
|
|
connecting
|
|
|
|
connected
|
|
|
|
)
|
|
|
|
|
|
|
|
type peerStatus struct {
|
|
|
|
status connStatus
|
|
|
|
canConnectAfter time.Time
|
|
|
|
lastConnectionAttempt time.Time
|
2022-01-31 10:33:56 +00:00
|
|
|
mailserver mailserversDB.Mailserver
|
2022-01-12 16:02:01 +00:00
|
|
|
}
|
|
|
|
type mailserverCycle struct {
|
|
|
|
sync.RWMutex
|
2022-03-21 14:18:36 +00:00
|
|
|
activeMailserver *mailserversDB.Mailserver
|
|
|
|
peers map[string]peerStatus
|
|
|
|
events chan *p2p.PeerEvent
|
|
|
|
subscription event.Subscription
|
|
|
|
availabilitySubscriptions []chan struct{}
|
2022-01-12 16:02:01 +00:00
|
|
|
}
|
|
|
|
|
2019-07-30 06:14:13 +00:00
|
|
|
type dbConfig struct {
|
2022-09-27 20:27:20 +00:00
|
|
|
dbPath string
|
|
|
|
dbKey string
|
|
|
|
dbKDFIterations int
|
2019-07-30 06:14:13 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
type EnvelopeEventsInterceptor struct {
|
|
|
|
EnvelopeEventsHandler transport.EnvelopeEventsHandler
|
|
|
|
Messenger *Messenger
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnvelopeSent triggered when envelope delivered at least to 1 peer.
|
|
|
|
func (interceptor EnvelopeEventsInterceptor) EnvelopeSent(identifiers [][]byte) {
|
|
|
|
if interceptor.Messenger != nil {
|
|
|
|
var ids []string
|
|
|
|
for _, identifierBytes := range identifiers {
|
|
|
|
ids = append(ids, types.EncodeHex(identifierBytes))
|
|
|
|
}
|
|
|
|
|
|
|
|
err := interceptor.Messenger.processSentMessages(ids)
|
|
|
|
if err != nil {
|
|
|
|
interceptor.Messenger.logger.Info("Messenger failed to process sent messages", zap.Error(err))
|
2021-11-17 09:11:51 +00:00
|
|
|
} else {
|
|
|
|
interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers)
|
2020-12-15 14:43:41 +00:00
|
|
|
}
|
2021-11-17 09:11:51 +00:00
|
|
|
} else {
|
|
|
|
// NOTE(rasom): In case if interceptor.Messenger is not nil and
|
|
|
|
// some error occurred on processing sent message we don't want
|
|
|
|
// to send envelop.sent signal to the client, thus `else` cause
|
|
|
|
// is necessary.
|
|
|
|
interceptor.EnvelopeEventsHandler.EnvelopeSent(identifiers)
|
2020-12-15 14:43:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnvelopeExpired triggered when envelope is expired but wasn't delivered to any peer.
|
|
|
|
func (interceptor EnvelopeEventsInterceptor) EnvelopeExpired(identifiers [][]byte, err error) {
|
|
|
|
//we don't track expired events in Messenger, so just redirect to handler
|
|
|
|
interceptor.EnvelopeEventsHandler.EnvelopeExpired(identifiers, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MailServerRequestCompleted triggered when the mailserver sends a message to notify that the request has been completed
|
|
|
|
func (interceptor EnvelopeEventsInterceptor) MailServerRequestCompleted(requestID types.Hash, lastEnvelopeHash types.Hash, cursor []byte, err error) {
|
|
|
|
//we don't track mailserver requests in Messenger, so just redirect to handler
|
|
|
|
interceptor.EnvelopeEventsHandler.MailServerRequestCompleted(requestID, lastEnvelopeHash, cursor, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MailServerRequestExpired triggered when the mailserver request expires
|
|
|
|
func (interceptor EnvelopeEventsInterceptor) MailServerRequestExpired(hash types.Hash) {
|
|
|
|
//we don't track mailserver requests in Messenger, so just redirect to handler
|
|
|
|
interceptor.EnvelopeEventsHandler.MailServerRequestExpired(hash)
|
|
|
|
}
|
|
|
|
|
2019-07-17 22:25:42 +00:00
|
|
|
func NewMessenger(
|
2021-11-03 12:38:37 +00:00
|
|
|
nodeName string,
|
2019-07-17 22:25:42 +00:00
|
|
|
identity *ecdsa.PrivateKey,
|
2019-11-23 17:57:05 +00:00
|
|
|
node types.Node,
|
2019-07-17 22:25:42 +00:00
|
|
|
installationID string,
|
2022-01-12 16:02:01 +00:00
|
|
|
peerStore *mailservers.PeerStore,
|
2019-07-17 22:25:42 +00:00
|
|
|
opts ...Option,
|
|
|
|
) (*Messenger, error) {
|
|
|
|
var messenger *Messenger
|
|
|
|
|
|
|
|
c := config{}
|
|
|
|
|
|
|
|
for _, opt := range opts {
|
|
|
|
if err := opt(&c); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger := c.logger
|
|
|
|
if c.logger == nil {
|
|
|
|
var err error
|
|
|
|
if logger, err = zap.NewDevelopment(); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create a logger")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 15:34:05 +00:00
|
|
|
if c.systemMessagesTranslations == nil {
|
|
|
|
c.systemMessagesTranslations = defaultSystemMessagesTranslations
|
|
|
|
}
|
|
|
|
|
2019-07-30 06:14:13 +00:00
|
|
|
// Configure the database.
|
|
|
|
database := c.db
|
2019-09-26 09:26:33 +00:00
|
|
|
if c.db == nil && c.dbConfig == (dbConfig{}) {
|
|
|
|
return nil, errors.New("database instance or database path needs to be provided")
|
|
|
|
}
|
|
|
|
if c.db == nil {
|
2022-09-27 20:27:20 +00:00
|
|
|
logger.Info("opening a database", zap.String("dbPath", c.dbConfig.dbPath), zap.Int("KDFIterations", c.dbConfig.dbKDFIterations))
|
2019-09-26 09:26:33 +00:00
|
|
|
var err error
|
2022-09-27 20:27:20 +00:00
|
|
|
database, err = appdatabase.InitializeDB(c.dbConfig.dbPath, c.dbConfig.dbKey, c.dbConfig.dbKDFIterations)
|
2019-07-30 06:14:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to initialize database from the db config")
|
|
|
|
}
|
|
|
|
}
|
2019-09-26 09:26:33 +00:00
|
|
|
|
2021-02-15 23:18:08 +00:00
|
|
|
// Apply any post database creation changes to the database
|
|
|
|
c.db = database
|
|
|
|
for _, opt := range c.afterDbCreatedHooks {
|
|
|
|
if err := opt(&c); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-30 06:14:13 +00:00
|
|
|
// Apply migrations for all components.
|
2020-01-13 19:17:30 +00:00
|
|
|
err := sqlite.Migrate(database)
|
2019-07-30 06:14:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to apply migrations")
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-30 06:14:13 +00:00
|
|
|
// Initialize transport layer.
|
2021-06-16 20:19:45 +00:00
|
|
|
var transp *transport.Transport
|
|
|
|
|
|
|
|
if waku, err := node.GetWaku(nil); err == nil && waku != nil {
|
|
|
|
transp, err = transport.NewTransport(
|
|
|
|
waku,
|
|
|
|
identity,
|
|
|
|
database,
|
|
|
|
"waku_keys",
|
|
|
|
nil,
|
|
|
|
c.envelopesMonitorConfig,
|
|
|
|
logger,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create Transport")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logger.Info("failed to find Waku service; trying WakuV2", zap.Error(err))
|
|
|
|
wakuV2, err := node.GetWakuV2(nil)
|
|
|
|
if err != nil || wakuV2 == nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to find Whisper and Waku V1/V2 services")
|
|
|
|
}
|
|
|
|
transp, err = transport.NewTransport(
|
|
|
|
wakuV2,
|
|
|
|
identity,
|
|
|
|
database,
|
|
|
|
"wakuv2_keys",
|
|
|
|
nil,
|
|
|
|
c.envelopesMonitorConfig,
|
|
|
|
logger,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create Transport")
|
|
|
|
}
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2019-07-30 06:14:13 +00:00
|
|
|
// Initialize encryption layer.
|
|
|
|
encryptionProtocol := encryption.New(
|
|
|
|
database,
|
2019-07-17 22:25:42 +00:00
|
|
|
installationID,
|
|
|
|
logger,
|
|
|
|
)
|
2019-07-26 07:17:29 +00:00
|
|
|
|
2021-06-23 14:13:48 +00:00
|
|
|
sender, err := common.NewMessageSender(
|
2019-09-02 09:29:06 +00:00
|
|
|
identity,
|
2019-08-27 12:04:15 +00:00
|
|
|
database,
|
2019-09-02 09:29:06 +00:00
|
|
|
encryptionProtocol,
|
2020-01-13 19:17:30 +00:00
|
|
|
transp,
|
2019-08-29 06:33:46 +00:00
|
|
|
logger,
|
2019-09-02 09:29:06 +00:00
|
|
|
c.featureFlags,
|
2019-07-26 07:17:29 +00:00
|
|
|
)
|
2019-08-27 12:04:15 +00:00
|
|
|
if err != nil {
|
2021-06-23 14:13:48 +00:00
|
|
|
return nil, errors.Wrap(err, "failed to create messageSender")
|
2019-08-27 12:04:15 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 12:02:18 +00:00
|
|
|
// Initialise anon metrics client
|
|
|
|
var anonMetricsClient *anonmetrics.Client
|
|
|
|
if c.anonMetricsClientConfig != nil &&
|
|
|
|
c.anonMetricsClientConfig.ShouldSend &&
|
|
|
|
c.anonMetricsClientConfig.Active == anonmetrics.ActiveClientPhrase {
|
|
|
|
|
|
|
|
anonMetricsClient = anonmetrics.NewClient(sender)
|
|
|
|
anonMetricsClient.Config = c.anonMetricsClientConfig
|
|
|
|
anonMetricsClient.Identity = identity
|
|
|
|
anonMetricsClient.DB = appmetrics.NewDB(database)
|
|
|
|
anonMetricsClient.Logger = logger
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialise anon metrics server
|
|
|
|
var anonMetricsServer *anonmetrics.Server
|
|
|
|
if c.anonMetricsServerConfig != nil &&
|
|
|
|
c.anonMetricsServerConfig.Enabled &&
|
|
|
|
c.anonMetricsServerConfig.Active == anonmetrics.ActiveServerPhrase {
|
|
|
|
|
|
|
|
server, err := anonmetrics.NewServer(c.anonMetricsServerConfig.PostgresURI)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to create anonmetrics.Server")
|
|
|
|
}
|
|
|
|
|
|
|
|
anonMetricsServer = server
|
|
|
|
anonMetricsServer.Config = c.anonMetricsServerConfig
|
|
|
|
anonMetricsServer.Logger = logger
|
|
|
|
}
|
|
|
|
|
2021-11-03 12:38:37 +00:00
|
|
|
var telemetryClient *telemetry.Client
|
|
|
|
if c.telemetryServerURL != "" {
|
|
|
|
telemetryClient = telemetry.NewClient(logger, c.telemetryServerURL, c.account.KeyUID, nodeName)
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// Initialize push notification server
|
|
|
|
var pushNotificationServer *pushnotificationserver.Server
|
2020-08-20 07:26:00 +00:00
|
|
|
if c.pushNotificationServerConfig != nil && c.pushNotificationServerConfig.Enabled {
|
2020-07-14 14:07:19 +00:00
|
|
|
c.pushNotificationServerConfig.Identity = identity
|
2020-07-22 07:41:40 +00:00
|
|
|
pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(database)
|
2021-06-23 14:13:48 +00:00
|
|
|
pushNotificationServer = pushnotificationserver.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, sender)
|
2020-07-06 08:54:22 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// Initialize push notification client
|
|
|
|
pushNotificationClientPersistence := pushnotificationclient.NewPersistence(database)
|
2020-07-15 12:25:01 +00:00
|
|
|
pushNotificationClientConfig := c.pushNotificationClientConfig
|
|
|
|
if pushNotificationClientConfig == nil {
|
2020-07-22 07:41:40 +00:00
|
|
|
pushNotificationClientConfig = &pushnotificationclient.Config{}
|
2020-07-07 13:55:24 +00:00
|
|
|
}
|
2020-07-15 12:25:01 +00:00
|
|
|
|
2022-03-28 10:10:40 +00:00
|
|
|
sqlitePersistence := newSQLitePersistence(database)
|
2020-07-22 07:41:40 +00:00
|
|
|
// Overriding until we handle different identities
|
2020-07-15 12:25:01 +00:00
|
|
|
pushNotificationClientConfig.Identity = identity
|
|
|
|
pushNotificationClientConfig.Logger = logger
|
|
|
|
pushNotificationClientConfig.InstallationID = installationID
|
|
|
|
|
2021-06-23 14:13:48 +00:00
|
|
|
pushNotificationClient := pushnotificationclient.New(pushNotificationClientPersistence, pushNotificationClientConfig, sender, sqlitePersistence)
|
2020-07-06 08:54:22 +00:00
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
ensVerifier := ens.New(node, logger, transp, database, c.verifyENSURL, c.verifyENSContractAddress)
|
|
|
|
|
2022-10-14 09:26:10 +00:00
|
|
|
communitiesManager, err := communities.NewManager(identity, database, encryptionProtocol, logger, ensVerifier, transp, c.torrentConfig)
|
2020-11-18 09:16:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-03-23 18:47:00 +00:00
|
|
|
|
|
|
|
settings, err := accounts.NewDB(database)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-10 17:19:34 +00:00
|
|
|
|
2022-01-12 16:02:01 +00:00
|
|
|
mailservers := mailserversDB.NewDB(database)
|
2022-02-10 17:19:34 +00:00
|
|
|
|
2022-09-14 10:46:11 +00:00
|
|
|
savedAddressesManager := wallet.NewSavedAddressesManager(c.db)
|
|
|
|
|
2019-07-17 22:25:42 +00:00
|
|
|
messenger = &Messenger{
|
2020-07-31 09:46:38 +00:00
|
|
|
config: &c,
|
2019-11-23 17:57:05 +00:00
|
|
|
node: node,
|
2019-07-17 22:25:42 +00:00
|
|
|
identity: identity,
|
2021-03-09 12:48:15 +00:00
|
|
|
persistence: sqlitePersistence,
|
2020-01-13 19:17:30 +00:00
|
|
|
transport: transp,
|
2019-07-17 22:25:42 +00:00
|
|
|
encryptor: encryptionProtocol,
|
2021-06-23 14:13:48 +00:00
|
|
|
sender: sender,
|
2021-09-01 12:02:18 +00:00
|
|
|
anonMetricsClient: anonMetricsClient,
|
|
|
|
anonMetricsServer: anonMetricsServer,
|
2021-11-03 12:38:37 +00:00
|
|
|
telemetryClient: telemetryClient,
|
2020-06-30 08:30:58 +00:00
|
|
|
pushNotificationClient: pushNotificationClient,
|
2020-07-03 10:08:47 +00:00
|
|
|
pushNotificationServer: pushNotificationServer,
|
2020-11-18 09:16:51 +00:00
|
|
|
communitiesManager: communitiesManager,
|
2021-01-11 10:32:51 +00:00
|
|
|
ensVerifier: ensVerifier,
|
2019-07-17 22:25:42 +00:00
|
|
|
featureFlags: c.featureFlags,
|
2019-12-02 15:34:05 +00:00
|
|
|
systemMessagesTranslations: c.systemMessagesTranslations,
|
2021-03-29 15:41:30 +00:00
|
|
|
allChats: new(chatMap),
|
|
|
|
allContacts: new(contactMap),
|
|
|
|
allInstallations: new(installationMap),
|
2020-01-10 18:59:01 +00:00
|
|
|
installationID: installationID,
|
2021-03-29 15:41:30 +00:00
|
|
|
modifiedInstallations: new(stringBoolMap),
|
2020-01-10 18:59:01 +00:00
|
|
|
verifyTransactionClient: c.verifyTransactionClient,
|
2020-07-14 14:07:19 +00:00
|
|
|
database: database,
|
2020-11-24 13:13:46 +00:00
|
|
|
multiAccounts: c.multiAccount,
|
2021-04-07 12:57:14 +00:00
|
|
|
settings: settings,
|
2022-01-12 16:02:01 +00:00
|
|
|
peerStore: peerStore,
|
2022-07-05 19:49:44 +00:00
|
|
|
verificationDatabase: verification.NewPersistence(database),
|
2022-01-12 16:02:01 +00:00
|
|
|
mailservers: mailservers,
|
|
|
|
mailserverCycle: mailserverCycle{
|
2022-03-21 14:18:36 +00:00
|
|
|
peers: make(map[string]peerStatus),
|
|
|
|
availabilitySubscriptions: make([]chan struct{}, 0),
|
2022-01-12 16:02:01 +00:00
|
|
|
},
|
2022-06-02 12:17:52 +00:00
|
|
|
mailserversDatabase: c.mailserversDatabase,
|
|
|
|
account: c.account,
|
|
|
|
quit: make(chan struct{}),
|
|
|
|
requestedCommunitiesLock: sync.RWMutex{},
|
|
|
|
requestedCommunities: make(map[string]*transport.Filter),
|
2022-09-29 11:50:23 +00:00
|
|
|
importingCommunities: make(map[string]bool),
|
2022-06-02 12:17:52 +00:00
|
|
|
browserDatabase: c.browserDatabase,
|
|
|
|
httpServer: c.httpServer,
|
|
|
|
contractMaker: &contracts.ContractMaker{
|
|
|
|
RPCClient: c.rpcClient,
|
|
|
|
},
|
2019-07-17 22:25:42 +00:00
|
|
|
shutdownTasks: []func() error{
|
2021-01-11 10:32:51 +00:00
|
|
|
ensVerifier.Stop,
|
2020-07-10 13:26:06 +00:00
|
|
|
pushNotificationClient.Stop,
|
2020-11-18 09:16:51 +00:00
|
|
|
communitiesManager.Stop,
|
2020-07-31 09:08:09 +00:00
|
|
|
encryptionProtocol.Stop,
|
2020-01-13 19:17:30 +00:00
|
|
|
transp.ResetFilters,
|
|
|
|
transp.Stop,
|
2021-06-23 14:13:48 +00:00
|
|
|
func() error { sender.Stop(); return nil },
|
2019-07-17 22:25:42 +00:00
|
|
|
// Currently this often fails, seems like it's safe to ignore them
|
|
|
|
// https://github.com/uber-go/zap/issues/328
|
|
|
|
func() error { _ = logger.Sync; return nil },
|
2021-01-14 22:15:13 +00:00
|
|
|
database.Close,
|
2019-07-17 22:25:42 +00:00
|
|
|
},
|
2022-09-14 10:46:11 +00:00
|
|
|
logger: logger,
|
|
|
|
savedAddressesManager: savedAddressesManager,
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2022-08-24 12:06:48 +00:00
|
|
|
if c.outputMessagesCSV {
|
|
|
|
messenger.outputCSV = c.outputMessagesCSV
|
|
|
|
csvFile, err := os.Create("messages-" + fmt.Sprint(time.Now().Unix()) + ".csv")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = csvFile.Write([]byte("timestamp\tmessageID\tfrom\ttopic\tchatID\tmessageType\tmessage\n"))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
messenger.csvFile = csvFile
|
|
|
|
messenger.shutdownTasks = append(messenger.shutdownTasks, csvFile.Close)
|
|
|
|
}
|
|
|
|
|
2021-09-01 12:02:18 +00:00
|
|
|
if anonMetricsClient != nil {
|
|
|
|
messenger.shutdownTasks = append(messenger.shutdownTasks, anonMetricsClient.Stop)
|
|
|
|
}
|
|
|
|
if anonMetricsServer != nil {
|
|
|
|
messenger.shutdownTasks = append(messenger.shutdownTasks, anonMetricsServer.Stop)
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
if c.envelopesMonitorConfig != nil {
|
|
|
|
interceptor := EnvelopeEventsInterceptor{c.envelopesMonitorConfig.EnvelopeEventsHandler, messenger}
|
|
|
|
err := messenger.transport.SetEnvelopeEventsHandler(interceptor)
|
|
|
|
if err != nil {
|
|
|
|
logger.Info("Unable to set envelopes event handler", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
2019-07-17 22:25:42 +00:00
|
|
|
|
|
|
|
return messenger, nil
|
|
|
|
}
|
|
|
|
|
2022-01-12 16:02:01 +00:00
|
|
|
func (m *Messenger) SetP2PServer(server *p2p.Server) {
|
|
|
|
m.server = server
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
func (m *Messenger) processSentMessages(ids []string) error {
|
2021-11-17 09:11:51 +00:00
|
|
|
if m.connectionState.Offline {
|
|
|
|
return errors.New("Can't mark message as sent while offline")
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
for _, id := range ids {
|
|
|
|
rawMessage, err := m.persistence.RawMessageByID(id)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Can't get raw message with id %v", id)
|
|
|
|
}
|
|
|
|
|
|
|
|
rawMessage.Sent = true
|
|
|
|
|
|
|
|
err = m.persistence.SaveRawMessage(rawMessage)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Can't save raw message marked as sent")
|
|
|
|
}
|
2021-11-17 09:11:51 +00:00
|
|
|
|
2022-01-31 10:33:56 +00:00
|
|
|
err = m.UpdateMessageOutgoingStatus(id, common.OutgoingStatusSent)
|
2021-11-17 09:11:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-12-15 14:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-11-17 09:11:51 +00:00
|
|
|
func shouldResendMessage(message *common.RawMessage, t common.TimeSource) (bool, error) {
|
|
|
|
if !(message.MessageType == protobuf.ApplicationMetadataMessage_EMOJI_REACTION ||
|
|
|
|
message.MessageType == protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) {
|
|
|
|
return false, errors.Errorf("Should resend only specific types of messages, can't resend %v", message.MessageType)
|
2020-12-15 14:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if message.Sent {
|
|
|
|
return false, errors.New("Should resend only non-sent messages")
|
|
|
|
}
|
|
|
|
|
2021-11-17 09:11:51 +00:00
|
|
|
if message.SendCount > messageResendMaxCount {
|
2020-12-15 14:43:41 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//exponential backoff depends on how many attempts to send message already made
|
2021-11-17 09:11:51 +00:00
|
|
|
backoff := uint64(math.Pow(2, float64(message.SendCount-1))) * messageResendMinDelay * uint64(time.Second.Milliseconds())
|
2020-12-15 14:43:41 +00:00
|
|
|
backoffElapsed := t.GetCurrentTime() > (message.LastSent + backoff)
|
|
|
|
return backoffElapsed, nil
|
|
|
|
}
|
|
|
|
|
2021-11-17 09:11:51 +00:00
|
|
|
func (m *Messenger) resendExpiredMessages() error {
|
|
|
|
if m.connectionState.Offline {
|
|
|
|
return errors.New("offline")
|
|
|
|
}
|
|
|
|
|
|
|
|
ids, err := m.persistence.ExpiredMessagesIDs(messageResendMaxCount)
|
2020-12-15 14:43:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Can't get expired reactions from db")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range ids {
|
|
|
|
rawMessage, err := m.persistence.RawMessageByID(id)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Can't get raw message with id %v", id)
|
|
|
|
}
|
|
|
|
|
2021-11-17 09:11:51 +00:00
|
|
|
chat, ok := m.allChats.Load(rawMessage.LocalChatID)
|
|
|
|
if !ok {
|
|
|
|
return ErrChatNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
if !(chat.Public() || chat.CommunityChat()) {
|
|
|
|
return errors.New("Only public chats and community chats messages are resent")
|
|
|
|
}
|
|
|
|
|
|
|
|
ok, err = shouldResendMessage(rawMessage, m.getTimesource())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok {
|
2020-12-15 14:43:41 +00:00
|
|
|
err = m.persistence.SaveRawMessage(rawMessage)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Can't save raw message marked as non-expired")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.reSendRawMessage(context.Background(), rawMessage.ID)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Can't resend expired message with id %v", rawMessage.ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-02-10 17:19:34 +00:00
|
|
|
func (m *Messenger) ToForeground() {
|
2022-02-23 14:34:16 +00:00
|
|
|
if m.httpServer != nil {
|
|
|
|
m.httpServer.ToForeground()
|
2022-02-10 17:19:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) ToBackground() {
|
2022-02-23 14:34:16 +00:00
|
|
|
if m.httpServer != nil {
|
|
|
|
m.httpServer.ToBackground()
|
2022-02-10 17:19:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-14 22:15:13 +00:00
|
|
|
func (m *Messenger) Start() (*MessengerResponse, error) {
|
2020-07-14 14:07:19 +00:00
|
|
|
m.logger.Info("starting messenger", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))))
|
2020-07-10 13:26:06 +00:00
|
|
|
// Start push notification server
|
|
|
|
if m.pushNotificationServer != nil {
|
|
|
|
if err := m.pushNotificationServer.Start(); err != nil {
|
2021-01-14 22:15:13 +00:00
|
|
|
return nil, err
|
2020-07-10 13:26:06 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-22 07:41:40 +00:00
|
|
|
|
|
|
|
// Start push notification client
|
2020-07-10 13:26:06 +00:00
|
|
|
if m.pushNotificationClient != nil {
|
2020-08-18 15:07:48 +00:00
|
|
|
m.handlePushNotificationClientRegistrations(m.pushNotificationClient.SubscribeToRegistrations())
|
|
|
|
|
2020-07-10 13:26:06 +00:00
|
|
|
if err := m.pushNotificationClient.Start(); err != nil {
|
2021-01-14 22:15:13 +00:00
|
|
|
return nil, err
|
2020-07-10 13:26:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-01 12:02:18 +00:00
|
|
|
// Start anonymous metrics client
|
|
|
|
if m.anonMetricsClient != nil {
|
|
|
|
if err := m.anonMetricsClient.Start(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
ensSubscription := m.ensVerifier.Subscribe()
|
|
|
|
|
|
|
|
// Subscrbe
|
|
|
|
if err := m.ensVerifier.Start(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := m.communitiesManager.Start(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-31 12:22:05 +00:00
|
|
|
// set shared secret handles
|
2021-06-23 14:13:48 +00:00
|
|
|
m.sender.SetHandleSharedSecrets(m.handleSharedSecrets)
|
2020-07-31 12:22:05 +00:00
|
|
|
|
2020-07-31 09:08:09 +00:00
|
|
|
subscriptions, err := m.encryptor.Start(m.identity)
|
|
|
|
if err != nil {
|
2021-01-14 22:15:13 +00:00
|
|
|
return nil, err
|
2020-07-31 09:08:09 +00:00
|
|
|
}
|
2020-07-31 12:22:05 +00:00
|
|
|
|
|
|
|
// handle stored shared secrets
|
|
|
|
err = m.handleSharedSecrets(subscriptions.SharedSecrets)
|
|
|
|
if err != nil {
|
2021-01-14 22:15:13 +00:00
|
|
|
return nil, err
|
2020-07-31 12:22:05 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 09:08:09 +00:00
|
|
|
m.handleEncryptionLayerSubscriptions(subscriptions)
|
2020-11-18 09:16:51 +00:00
|
|
|
m.handleCommunitiesSubscription(m.communitiesManager.Subscribe())
|
2022-03-21 14:18:36 +00:00
|
|
|
m.handleCommunitiesHistoryArchivesSubscription(m.communitiesManager.Subscribe())
|
2020-08-27 12:38:59 +00:00
|
|
|
m.handleConnectionChange(m.online())
|
2021-01-11 10:32:51 +00:00
|
|
|
m.handleENSVerificationSubscription(ensSubscription)
|
2020-08-27 12:38:59 +00:00
|
|
|
m.watchConnectionChange()
|
2021-11-17 09:11:51 +00:00
|
|
|
m.watchExpiredMessages()
|
2020-12-21 08:41:50 +00:00
|
|
|
m.watchIdentityImageChanges()
|
2021-07-22 17:41:49 +00:00
|
|
|
m.broadcastLatestUserStatus()
|
2022-08-02 23:08:01 +00:00
|
|
|
m.timeoutAutomaticStatusUpdates()
|
2021-10-11 15:39:52 +00:00
|
|
|
m.startBackupLoop()
|
2021-11-29 13:11:55 +00:00
|
|
|
err = m.startAutoMessageLoop()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-03-23 18:47:00 +00:00
|
|
|
m.startSyncSettingsLoop()
|
2021-07-22 17:41:49 +00:00
|
|
|
|
2021-01-14 22:15:13 +00:00
|
|
|
if err := m.cleanTopics(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-25 15:15:22 +00:00
|
|
|
response := &MessengerResponse{}
|
2021-01-14 22:15:13 +00:00
|
|
|
|
2022-01-31 10:33:56 +00:00
|
|
|
mailservers, err := m.allMailservers()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-01-14 22:15:13 +00:00
|
|
|
}
|
2021-11-29 13:11:55 +00:00
|
|
|
|
2022-01-31 10:33:56 +00:00
|
|
|
response.Mailservers = mailservers
|
|
|
|
err = m.StartMailserverCycle()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-01-12 16:02:01 +00:00
|
|
|
}
|
|
|
|
|
2022-12-05 17:30:07 +00:00
|
|
|
if m.config.torrentConfig != nil && m.config.torrentConfig.Enabled && m.communitiesManager.TorrentClientStarted() {
|
2022-03-21 14:18:36 +00:00
|
|
|
adminCommunities, err := m.communitiesManager.Created()
|
|
|
|
if err == nil && len(adminCommunities) > 0 {
|
|
|
|
available := m.SubscribeMailserverAvailable()
|
|
|
|
go func() {
|
|
|
|
<-available
|
|
|
|
m.InitHistoryArchiveTasks(adminCommunities)
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-09 13:07:57 +00:00
|
|
|
if m.httpServer != nil {
|
|
|
|
err = m.httpServer.Start()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-10 17:19:34 +00:00
|
|
|
}
|
|
|
|
|
2022-06-09 15:21:57 +00:00
|
|
|
err = m.GarbageCollectRemovedBookmarks()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-09-14 10:46:11 +00:00
|
|
|
err = m.garbageCollectRemovedSavedAddresses()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-11-17 21:19:17 +00:00
|
|
|
err = m.setInstallationHostname()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-14 22:15:13 +00:00
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
2022-02-10 22:55:03 +00:00
|
|
|
func (m *Messenger) IdentityPublicKey() *ecdsa.PublicKey {
|
|
|
|
return &m.identity.PublicKey
|
|
|
|
}
|
|
|
|
|
2021-01-14 22:15:13 +00:00
|
|
|
// cleanTopics remove any topic that does not have a Listen flag set
|
|
|
|
func (m *Messenger) cleanTopics() error {
|
|
|
|
if m.mailserversDatabase == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
var filters []*transport.Filter
|
|
|
|
for _, f := range m.transport.Filters() {
|
|
|
|
if f.Listen && !f.Ephemeral {
|
|
|
|
filters = append(filters, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m.logger.Debug("keeping topics", zap.Any("filters", filters))
|
|
|
|
|
|
|
|
return m.mailserversDatabase.SetTopics(filters)
|
2020-07-31 09:08:09 +00:00
|
|
|
}
|
|
|
|
|
2020-08-27 12:38:59 +00:00
|
|
|
// handle connection change is called each time we go from offline/online or viceversa
|
|
|
|
func (m *Messenger) handleConnectionChange(online bool) {
|
|
|
|
if online {
|
|
|
|
if m.pushNotificationClient != nil {
|
|
|
|
m.pushNotificationClient.Online()
|
|
|
|
}
|
2020-12-21 08:41:50 +00:00
|
|
|
|
|
|
|
if m.shouldPublishContactCode {
|
2021-01-25 13:49:13 +00:00
|
|
|
if err := m.publishContactCode(); err != nil {
|
2020-12-21 08:41:50 +00:00
|
|
|
m.logger.Error("could not publish on contact code", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m.shouldPublishContactCode = false
|
|
|
|
}
|
2022-01-31 10:33:56 +00:00
|
|
|
go func() {
|
|
|
|
_, err := m.RequestAllHistoricMessagesWithRetries()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Warn("failed to fetch historic messages", zap.Error(err))
|
|
|
|
}
|
|
|
|
}()
|
2020-12-21 08:41:50 +00:00
|
|
|
|
2020-08-27 12:38:59 +00:00
|
|
|
} else {
|
|
|
|
if m.pushNotificationClient != nil {
|
|
|
|
m.pushNotificationClient.Offline()
|
|
|
|
}
|
2022-01-12 16:02:01 +00:00
|
|
|
|
2020-08-27 12:38:59 +00:00
|
|
|
}
|
2022-01-12 16:02:01 +00:00
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
m.ensVerifier.SetOnline(online)
|
2020-08-27 12:38:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) online() bool {
|
2021-08-30 14:57:28 +00:00
|
|
|
switch m.transport.WakuVersion() {
|
|
|
|
case 2:
|
|
|
|
return m.transport.PeerCount() > 0
|
|
|
|
default:
|
|
|
|
return m.node.PeersCount() > 0
|
2021-07-21 19:02:50 +00:00
|
|
|
}
|
2020-08-27 12:38:59 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 15:07:48 +00:00
|
|
|
func (m *Messenger) buildContactCodeAdvertisement() (*protobuf.ContactCodeAdvertisement, error) {
|
|
|
|
if m.pushNotificationClient == nil || !m.pushNotificationClient.Enabled() {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
m.logger.Debug("adding push notification info to contact code bundle")
|
|
|
|
info, err := m.pushNotificationClient.MyPushNotificationQueryInfo()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(info) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
return &protobuf.ContactCodeAdvertisement{
|
|
|
|
PushNotificationInfo: info,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:13 +00:00
|
|
|
// publishContactCode sends a public message wrapped in the encryption
|
2020-07-31 12:22:05 +00:00
|
|
|
// layer, which will propagate our bundle
|
2021-01-25 13:49:13 +00:00
|
|
|
func (m *Messenger) publishContactCode() error {
|
2020-07-31 13:46:27 +00:00
|
|
|
var payload []byte
|
2020-08-18 15:07:48 +00:00
|
|
|
m.logger.Debug("sending contact code")
|
|
|
|
contactCodeAdvertisement, err := m.buildContactCodeAdvertisement()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("could not build contact code advertisement", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2020-11-18 12:41:36 +00:00
|
|
|
if contactCodeAdvertisement == nil {
|
|
|
|
contactCodeAdvertisement = &protobuf.ContactCodeAdvertisement{}
|
|
|
|
}
|
2021-02-17 23:14:48 +00:00
|
|
|
|
2021-01-25 13:49:13 +00:00
|
|
|
err = m.attachChatIdentity(contactCodeAdvertisement)
|
2020-11-05 17:35:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-17 23:14:48 +00:00
|
|
|
if contactCodeAdvertisement.ChatIdentity != nil {
|
|
|
|
m.logger.Debug("attached chat identity", zap.Int("images len", len(contactCodeAdvertisement.ChatIdentity.Images)))
|
|
|
|
} else {
|
|
|
|
m.logger.Debug("no attached chat identity")
|
|
|
|
}
|
|
|
|
|
2020-11-18 12:41:36 +00:00
|
|
|
payload, err = proto.Marshal(contactCodeAdvertisement)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-07-31 13:46:27 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 12:22:05 +00:00
|
|
|
contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
|
|
|
|
rawMessage := common.RawMessage{
|
|
|
|
LocalChatID: contactCodeTopic,
|
2020-08-18 15:07:48 +00:00
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT,
|
2020-07-31 13:46:27 +00:00
|
|
|
Payload: payload,
|
2020-07-31 12:22:05 +00:00
|
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
2022-02-17 15:13:10 +00:00
|
|
|
|
2021-06-23 14:13:48 +00:00
|
|
|
_, err = m.sender.SendPublic(ctx, contactCodeTopic, rawMessage)
|
2020-07-31 12:22:05 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Warn("failed to send a contact code", zap.Error(err))
|
|
|
|
}
|
2022-02-17 15:13:10 +00:00
|
|
|
|
2022-07-08 10:25:46 +00:00
|
|
|
joinedCommunities, err := m.communitiesManager.Joined()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, community := range joinedCommunities {
|
|
|
|
rawMessage.LocalChatID = community.MemberUpdateChannelID()
|
|
|
|
_, err = m.sender.SendPublic(ctx, rawMessage.LocalChatID, rawMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-17 23:14:48 +00:00
|
|
|
m.logger.Debug("contact code sent")
|
2020-07-31 12:22:05 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-05 17:35:20 +00:00
|
|
|
// contactCodeAdvertisement attaches a protobuf.ChatIdentity to the given protobuf.ContactCodeAdvertisement,
|
2020-11-09 15:16:36 +00:00
|
|
|
// if the `shouldPublish` conditions are met
|
2021-01-25 13:49:13 +00:00
|
|
|
func (m *Messenger) attachChatIdentity(cca *protobuf.ContactCodeAdvertisement) error {
|
2020-11-09 15:16:36 +00:00
|
|
|
contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
|
|
|
|
shouldPublish, err := m.shouldPublishChatIdentity(contactCodeTopic)
|
2020-11-05 17:35:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:13 +00:00
|
|
|
if !shouldPublish {
|
|
|
|
return nil
|
|
|
|
}
|
2020-11-05 17:35:20 +00:00
|
|
|
|
2021-01-25 13:49:13 +00:00
|
|
|
cca.ChatIdentity, err = m.createChatIdentity(privateChat)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-12-10 10:12:51 +00:00
|
|
|
|
2021-01-25 13:49:13 +00:00
|
|
|
img, err := m.multiAccounts.GetIdentityImage(m.account.KeyUID, userimage.SmallDimName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-02-17 15:13:10 +00:00
|
|
|
|
|
|
|
displayName, err := m.settings.DisplayName()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-01-25 13:49:13 +00:00
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
bio, err := m.settings.Bio()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-02 12:56:26 +00:00
|
|
|
socialLinks, err := m.settings.GetSocialLinks()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
identityHash, err := m.getIdentityHash(displayName, bio, img, &socialLinks)
|
2022-08-02 12:56:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.persistence.SaveWhenChatIdentityLastPublished(contactCodeTopic, identityHash)
|
2021-01-25 13:49:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-11-05 17:35:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-20 09:14:48 +00:00
|
|
|
// handleStandaloneChatIdentity sends a standalone ChatIdentity message to a public or private channel if the publish criteria is met
|
2020-11-09 15:16:36 +00:00
|
|
|
func (m *Messenger) handleStandaloneChatIdentity(chat *Chat) error {
|
2021-10-20 09:45:54 +00:00
|
|
|
if chat.ChatType != ChatTypePublic && chat.ChatType != ChatTypeOneToOne {
|
2020-11-09 15:16:36 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
shouldPublishChatIdentity, err := m.shouldPublishChatIdentity(chat.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !shouldPublishChatIdentity {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-12-15 16:29:44 +00:00
|
|
|
ci, err := m.createChatIdentity(publicChat)
|
2020-11-09 15:16:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-05 17:35:20 +00:00
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
payload, err := proto.Marshal(ci)
|
2020-11-05 17:35:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
rawMessage := common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_CHAT_IDENTITY,
|
|
|
|
Payload: payload,
|
|
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
2021-10-20 09:14:48 +00:00
|
|
|
if chat.ChatType == ChatTypePublic {
|
|
|
|
_, err = m.sender.SendPublic(ctx, chat.ID, rawMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pk, err := chat.PublicKey()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = m.sender.SendPrivate(ctx, pk, &rawMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-11-05 17:35:20 +00:00
|
|
|
}
|
|
|
|
|
2020-12-10 10:12:51 +00:00
|
|
|
img, err := m.multiAccounts.GetIdentityImage(m.account.KeyUID, userimage.SmallDimName)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-02-17 15:13:10 +00:00
|
|
|
|
|
|
|
displayName, err := m.settings.DisplayName()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-12-10 10:12:51 +00:00
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
bio, err := m.settings.Bio()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-02 12:56:26 +00:00
|
|
|
socialLinks, err := m.settings.GetSocialLinks()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
identityHash, err := m.getIdentityHash(displayName, bio, img, &socialLinks)
|
2022-08-02 12:56:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.persistence.SaveWhenChatIdentityLastPublished(chat.ID, identityHash)
|
2020-11-09 15:16:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
func (m *Messenger) getIdentityHash(displayName, bio string, img *userimage.IdentityImage, socialLinks *identity.SocialLinks) ([]byte, error) {
|
2022-08-02 12:56:26 +00:00
|
|
|
socialLinksData, err := socialLinks.Serialize()
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
2022-02-17 15:13:10 +00:00
|
|
|
if img == nil {
|
2022-08-05 11:22:35 +00:00
|
|
|
return crypto.Keccak256([]byte(displayName), []byte(bio), socialLinksData), nil
|
2022-02-17 15:13:10 +00:00
|
|
|
}
|
2022-08-05 11:22:35 +00:00
|
|
|
return crypto.Keccak256(img.Payload, []byte(displayName), []byte(bio), socialLinksData), nil
|
2022-02-17 15:13:10 +00:00
|
|
|
}
|
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
// shouldPublishChatIdentity returns true if the last time the ChatIdentity was attached was more than 24 hours ago
|
2020-11-24 23:16:19 +00:00
|
|
|
func (m *Messenger) shouldPublishChatIdentity(chatID string) (bool, error) {
|
2021-01-25 13:49:13 +00:00
|
|
|
if m.account == nil {
|
|
|
|
return false, nil
|
|
|
|
}
|
2020-12-10 08:42:36 +00:00
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
// Check we have at least one image or a display name
|
2020-12-10 10:12:51 +00:00
|
|
|
img, err := m.multiAccounts.GetIdentityImage(m.account.KeyUID, userimage.SmallDimName)
|
2020-12-10 08:42:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
displayName, err := m.settings.DisplayName()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if img == nil && displayName == "" {
|
2020-12-10 08:42:36 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2020-12-10 10:12:51 +00:00
|
|
|
lp, hash, err := m.persistence.GetWhenChatIdentityLastPublished(chatID)
|
2020-11-09 15:16:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
bio, err := m.settings.Bio()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2022-08-02 12:56:26 +00:00
|
|
|
socialLinks, err := m.settings.GetSocialLinks()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
identityHash, err := m.getIdentityHash(displayName, bio, img, &socialLinks)
|
2022-08-02 12:56:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !bytes.Equal(hash, identityHash) {
|
2020-12-10 10:12:51 +00:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
2021-02-17 23:14:48 +00:00
|
|
|
// Note: If Alice does not add bob as a contact she will not update her contact code with images
|
2020-12-16 18:28:34 +00:00
|
|
|
return lp == 0 || time.Now().Unix()-lp > 24*60*60, nil
|
2020-11-09 15:16:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// createChatIdentity creates a context based protobuf.ChatIdentity.
|
|
|
|
// context 'public-chat' will attach only the 'thumbnail' IdentityImage
|
2020-11-18 12:41:36 +00:00
|
|
|
// context 'private-chat' will attach all IdentityImage
|
2020-12-15 16:29:44 +00:00
|
|
|
func (m *Messenger) createChatIdentity(context chatContext) (*protobuf.ChatIdentity, error) {
|
2020-12-09 14:03:43 +00:00
|
|
|
m.logger.Info(fmt.Sprintf("account keyUID '%s'", m.account.KeyUID))
|
2020-12-02 14:00:28 +00:00
|
|
|
m.logger.Info(fmt.Sprintf("context '%s'", context))
|
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
displayName, err := m.settings.DisplayName()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-08-05 11:22:35 +00:00
|
|
|
bio, err := m.settings.Bio()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-08-02 12:56:26 +00:00
|
|
|
socialLinks, err := m.settings.GetSocialLinks()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
ci := &protobuf.ChatIdentity{
|
2022-02-17 15:13:10 +00:00
|
|
|
Clock: m.transport.GetCurrentTime(),
|
|
|
|
EnsName: "", // TODO add ENS name handling to dedicate PR
|
|
|
|
DisplayName: displayName,
|
2022-08-05 11:22:35 +00:00
|
|
|
Description: bio,
|
2022-08-11 14:11:28 +00:00
|
|
|
SocialLinks: socialLinks.ToProtobuf(),
|
2020-11-05 17:35:20 +00:00
|
|
|
}
|
2022-02-17 15:13:10 +00:00
|
|
|
|
|
|
|
err = m.attachIdentityImagesToChatIdentity(context, ci)
|
2021-02-09 13:58:21 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ci, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// adaptIdentityImageToProtobuf Adapts a images.IdentityImage to protobuf.IdentityImage
|
|
|
|
func (m *Messenger) adaptIdentityImageToProtobuf(img *userimage.IdentityImage) *protobuf.IdentityImage {
|
|
|
|
return &protobuf.IdentityImage{
|
|
|
|
Payload: img.Payload,
|
|
|
|
SourceType: protobuf.IdentityImage_RAW_PAYLOAD, // TODO add ENS avatar handling to dedicated PR
|
|
|
|
ImageType: images.ImageType(img.Payload),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) attachIdentityImagesToChatIdentity(context chatContext, ci *protobuf.ChatIdentity) error {
|
|
|
|
s, err := m.getSettings()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-05 17:35:20 +00:00
|
|
|
|
2022-03-23 18:47:00 +00:00
|
|
|
if s.ProfilePicturesShowTo == settings.ProfilePicturesShowToNone {
|
2021-02-12 15:35:34 +00:00
|
|
|
m.logger.Info(fmt.Sprintf("settings.ProfilePicturesShowTo is set to '%d', skipping attaching IdentityImages", s.ProfilePicturesShowTo))
|
2021-02-09 16:09:50 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
ciis := make(map[string]*protobuf.IdentityImage)
|
|
|
|
|
|
|
|
switch context {
|
2020-12-15 16:29:44 +00:00
|
|
|
case publicChat:
|
|
|
|
m.logger.Info(fmt.Sprintf("handling %s ChatIdentity", context))
|
2020-12-02 14:00:28 +00:00
|
|
|
|
2020-12-09 14:03:43 +00:00
|
|
|
img, err := m.multiAccounts.GetIdentityImage(m.account.KeyUID, userimage.SmallDimName)
|
2020-11-09 15:16:36 +00:00
|
|
|
if err != nil {
|
2021-02-09 13:58:21 +00:00
|
|
|
return err
|
2020-11-09 15:16:36 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
if img == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-12-15 16:29:44 +00:00
|
|
|
m.logger.Debug(fmt.Sprintf("%s images.IdentityImage '%s'", context, spew.Sdump(img)))
|
2020-12-02 14:00:28 +00:00
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
ciis[userimage.SmallDimName] = m.adaptIdentityImageToProtobuf(img)
|
2020-12-15 16:29:44 +00:00
|
|
|
m.logger.Debug(fmt.Sprintf("%s protobuf.IdentityImage '%s'", context, spew.Sdump(ciis)))
|
2020-11-09 15:16:36 +00:00
|
|
|
ci.Images = ciis
|
|
|
|
|
2020-12-15 16:29:44 +00:00
|
|
|
case privateChat:
|
|
|
|
m.logger.Info(fmt.Sprintf("handling %s ChatIdentity", context))
|
|
|
|
|
2020-12-09 14:03:43 +00:00
|
|
|
imgs, err := m.multiAccounts.GetIdentityImages(m.account.KeyUID)
|
2020-11-09 15:16:36 +00:00
|
|
|
if err != nil {
|
2021-02-09 13:58:21 +00:00
|
|
|
return err
|
2020-11-09 15:16:36 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 16:29:44 +00:00
|
|
|
m.logger.Debug(fmt.Sprintf("%s images.IdentityImage '%s'", context, spew.Sdump(imgs)))
|
2020-12-02 14:00:28 +00:00
|
|
|
|
2020-11-09 15:16:36 +00:00
|
|
|
for _, img := range imgs {
|
|
|
|
ciis[img.Name] = m.adaptIdentityImageToProtobuf(img)
|
|
|
|
}
|
2020-12-15 16:29:44 +00:00
|
|
|
m.logger.Debug(fmt.Sprintf("%s protobuf.IdentityImage '%s'", context, spew.Sdump(ciis)))
|
2020-11-09 15:16:36 +00:00
|
|
|
ci.Images = ciis
|
2020-12-15 16:29:44 +00:00
|
|
|
|
|
|
|
default:
|
2021-02-09 13:58:21 +00:00
|
|
|
return fmt.Errorf("unknown ChatIdentity context '%s'", context)
|
2020-11-09 15:16:36 +00:00
|
|
|
}
|
|
|
|
|
2022-03-23 18:47:00 +00:00
|
|
|
if s.ProfilePicturesShowTo == settings.ProfilePicturesShowToContactsOnly {
|
2021-02-15 14:51:49 +00:00
|
|
|
err := EncryptIdentityImagesWithContactPubKeys(ci.Images, m)
|
2021-02-09 16:09:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-31 12:22:05 +00:00
|
|
|
// handleSharedSecrets process the negotiated secrets received from the encryption layer
|
|
|
|
func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) error {
|
2020-07-31 09:46:38 +00:00
|
|
|
for _, secret := range secrets {
|
|
|
|
fSecret := types.NegotiatedSecret{
|
|
|
|
PublicKey: secret.Identity,
|
|
|
|
Key: secret.Key,
|
|
|
|
}
|
2021-05-14 10:55:42 +00:00
|
|
|
_, err := m.transport.ProcessNegotiatedSecret(fSecret)
|
2020-07-31 09:46:38 +00:00
|
|
|
if err != nil {
|
2020-07-31 12:22:05 +00:00
|
|
|
return err
|
2020-07-31 09:46:38 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-31 12:22:05 +00:00
|
|
|
return nil
|
2020-07-31 09:46:38 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 12:22:05 +00:00
|
|
|
// handleInstallations adds the installations in the installations map
|
|
|
|
func (m *Messenger) handleInstallations(installations []*multidevice.Installation) {
|
2020-07-31 09:08:09 +00:00
|
|
|
for _, installation := range installations {
|
|
|
|
if installation.Identity == contactIDFromPublicKey(&m.identity.PublicKey) {
|
2021-03-29 15:41:30 +00:00
|
|
|
if _, ok := m.allInstallations.Load(installation.ID); !ok {
|
|
|
|
m.allInstallations.Store(installation.ID, installation)
|
|
|
|
m.modifiedInstallations.Store(installation.ID, true)
|
2020-07-31 09:08:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 09:46:38 +00:00
|
|
|
// handleEncryptionLayerSubscriptions handles events from the encryption layer
|
2020-07-31 09:08:09 +00:00
|
|
|
func (m *Messenger) handleEncryptionLayerSubscriptions(subscriptions *encryption.Subscriptions) {
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
2020-07-31 12:22:05 +00:00
|
|
|
case <-subscriptions.SendContactCode:
|
2021-01-25 13:49:13 +00:00
|
|
|
if err := m.publishContactCode(); err != nil {
|
2020-07-31 12:22:05 +00:00
|
|
|
m.logger.Error("failed to publish contact code", zap.Error(err))
|
2020-07-31 09:46:38 +00:00
|
|
|
}
|
2021-01-08 15:21:25 +00:00
|
|
|
// we also piggy-back to clean up cached messages
|
|
|
|
if err := m.transport.CleanMessagesProcessed(m.getTimesource().GetCurrentTime() - messageCacheIntervalMs); err != nil {
|
|
|
|
m.logger.Error("failed to clean processed messages", zap.Error(err))
|
|
|
|
}
|
2020-07-31 09:46:38 +00:00
|
|
|
|
2020-07-31 09:08:09 +00:00
|
|
|
case <-subscriptions.Quit:
|
|
|
|
m.logger.Debug("quitting encryption subscription loop")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2020-01-29 19:40:06 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
func (m *Messenger) handleENSVerified(records []*ens.VerificationRecord) {
|
|
|
|
var contacts []*Contact
|
|
|
|
for _, record := range records {
|
|
|
|
m.logger.Info("handling record", zap.Any("record", record))
|
2021-03-29 15:41:30 +00:00
|
|
|
contact, ok := m.allContacts.Load(record.PublicKey)
|
2021-01-11 10:32:51 +00:00
|
|
|
if !ok {
|
|
|
|
m.logger.Info("contact not found")
|
|
|
|
continue
|
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
contact.ENSVerified = record.Verified
|
2022-02-17 15:13:10 +00:00
|
|
|
contact.EnsName = record.Name
|
2021-01-11 10:32:51 +00:00
|
|
|
contacts = append(contacts, contact)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
m.logger.Info("handled records", zap.Any("contacts", contacts))
|
|
|
|
if len(contacts) != 0 {
|
|
|
|
if err := m.persistence.SaveContacts(contacts); err != nil {
|
|
|
|
m.logger.Error("failed to save contacts", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
m.logger.Info("calling on contacts")
|
|
|
|
if m.config.onContactENSVerified != nil {
|
|
|
|
m.logger.Info("called on contacts")
|
|
|
|
response := &MessengerResponse{Contacts: contacts}
|
|
|
|
m.config.onContactENSVerified(response)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
func (m *Messenger) handleENSVerificationSubscription(c chan []*ens.VerificationRecord) {
|
2020-11-18 09:16:51 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
2021-01-11 10:32:51 +00:00
|
|
|
case records, more := <-c:
|
2020-11-18 09:16:51 +00:00
|
|
|
if !more {
|
2021-01-11 10:32:51 +00:00
|
|
|
m.logger.Info("No more records, quitting")
|
2020-11-18 09:16:51 +00:00
|
|
|
return
|
|
|
|
}
|
2021-01-11 10:32:51 +00:00
|
|
|
if len(records) != 0 {
|
|
|
|
m.logger.Info("handling records", zap.Any("records", records))
|
|
|
|
m.handleENSVerified(records)
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
case <-m.quit:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2020-08-27 12:38:59 +00:00
|
|
|
// watchConnectionChange checks the connection status and call handleConnectionChange when this changes
|
|
|
|
func (m *Messenger) watchConnectionChange() {
|
|
|
|
m.logger.Debug("watching connection changes")
|
|
|
|
state := m.online()
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-time.After(200 * time.Millisecond):
|
|
|
|
newState := m.online()
|
|
|
|
if state != newState {
|
|
|
|
state = newState
|
|
|
|
m.logger.Debug("connection changed", zap.Bool("online", state))
|
|
|
|
m.handleConnectionChange(state)
|
|
|
|
}
|
|
|
|
case <-m.quit:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2020-12-15 14:43:41 +00:00
|
|
|
}()
|
|
|
|
}
|
2020-08-27 12:38:59 +00:00
|
|
|
|
2021-11-17 09:11:51 +00:00
|
|
|
// watchExpiredMessages regularly checks for expired emojis and invoke their resending
|
|
|
|
func (m *Messenger) watchExpiredMessages() {
|
|
|
|
m.logger.Debug("watching expired messages")
|
2020-12-15 14:43:41 +00:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
if m.online() {
|
2021-11-17 09:11:51 +00:00
|
|
|
err := m.resendExpiredMessages()
|
2020-12-15 14:43:41 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Debug("Error when resending expired emoji reactions", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case <-m.quit:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2020-08-27 12:38:59 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2020-12-21 08:41:50 +00:00
|
|
|
// watchIdentityImageChanges checks for identity images changes and publishes to the contact code when it happens
|
|
|
|
func (m *Messenger) watchIdentityImageChanges() {
|
|
|
|
m.logger.Debug("watching identity image changes")
|
|
|
|
if m.multiAccounts == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
channel := m.multiAccounts.SubscribeToIdentityImageChanges()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-channel:
|
2022-03-24 09:35:56 +00:00
|
|
|
err := m.syncProfilePictures()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to sync profile pictures to paired devices", zap.Error(err))
|
|
|
|
}
|
|
|
|
err = m.PublishIdentityImage()
|
2021-10-07 15:02:01 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to publish identity image", zap.Error(err))
|
2020-12-21 08:41:50 +00:00
|
|
|
}
|
|
|
|
case <-m.quit:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2021-10-07 15:02:01 +00:00
|
|
|
func (m *Messenger) PublishIdentityImage() error {
|
|
|
|
// Reset last published time for ChatIdentity so new contact can receive data
|
|
|
|
err := m.resetLastPublishedTimeForChatIdentity()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to reset publish time", zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If not online, we schedule it
|
|
|
|
if !m.online() {
|
|
|
|
m.shouldPublishContactCode = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.publishContactCode()
|
|
|
|
}
|
|
|
|
|
2020-08-18 15:07:48 +00:00
|
|
|
// handlePushNotificationClientRegistration handles registration events
|
|
|
|
func (m *Messenger) handlePushNotificationClientRegistrations(c chan struct{}) {
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
_, more := <-c
|
|
|
|
if !more {
|
|
|
|
return
|
|
|
|
}
|
2021-01-25 13:49:13 +00:00
|
|
|
if err := m.publishContactCode(); err != nil {
|
2020-08-18 15:07:48 +00:00
|
|
|
m.logger.Error("failed to publish contact code", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2019-08-29 06:33:46 +00:00
|
|
|
// Init analyzes chats and contacts in order to setup filters
|
|
|
|
// which are responsible for retrieving messages.
|
|
|
|
func (m *Messenger) Init() error {
|
2019-12-02 15:34:05 +00:00
|
|
|
|
|
|
|
// Seed the for color generation
|
|
|
|
rand.Seed(time.Now().Unix())
|
|
|
|
|
2019-08-29 06:33:46 +00:00
|
|
|
logger := m.logger.With(zap.String("site", "Init"))
|
|
|
|
|
|
|
|
var (
|
|
|
|
publicChatIDs []string
|
|
|
|
publicKeys []*ecdsa.PublicKey
|
|
|
|
)
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
joinedCommunities, err := m.communitiesManager.Joined()
|
2020-11-18 09:16:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-01-11 10:32:51 +00:00
|
|
|
for _, org := range joinedCommunities {
|
2020-11-18 09:16:51 +00:00
|
|
|
// the org advertise on the public topic derived by the pk
|
2022-12-12 22:24:36 +00:00
|
|
|
publicChatIDs = append(publicChatIDs, org.DefaultFilters()...)
|
2022-03-08 15:25:00 +00:00
|
|
|
|
|
|
|
// This is for status-go versions that didn't have `CommunitySettings`
|
|
|
|
// We need to ensure communities that existed before community settings
|
|
|
|
// were introduced will have community settings as well
|
|
|
|
exists, err := m.communitiesManager.CommunitySettingsExist(org.ID())
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to check if community settings exist", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2022-04-08 11:34:39 +00:00
|
|
|
|
|
|
|
if !exists {
|
|
|
|
communitySettings := communities.CommunitySettings{
|
|
|
|
CommunityID: org.IDString(),
|
|
|
|
HistoryArchiveSupportEnabled: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.communitiesManager.SaveCommunitySettings(communitySettings)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to save community settings", zap.Error(err))
|
|
|
|
}
|
2022-03-08 15:25:00 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-04-08 11:34:39 +00:00
|
|
|
// In case we do have settings, but the history archive support is disabled
|
|
|
|
// for this community, we enable it, as this should be the default for all
|
|
|
|
// non-admin communities
|
|
|
|
communitySettings, err := m.communitiesManager.GetCommunitySettingsByID(org.ID())
|
2022-03-08 15:25:00 +00:00
|
|
|
if err != nil {
|
2022-04-08 11:34:39 +00:00
|
|
|
logger.Warn("failed to fetch community settings", zap.Error(err))
|
2022-03-08 15:25:00 +00:00
|
|
|
continue
|
|
|
|
}
|
2022-04-08 11:34:39 +00:00
|
|
|
|
|
|
|
if !org.IsAdmin() && !communitySettings.HistoryArchiveSupportEnabled {
|
|
|
|
communitySettings.HistoryArchiveSupportEnabled = true
|
|
|
|
err = m.communitiesManager.UpdateCommunitySettings(*communitySettings)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to update community settings", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// Init filters for the communities we are an admin of
|
|
|
|
var adminCommunitiesPks []*ecdsa.PrivateKey
|
|
|
|
adminCommunities, err := m.communitiesManager.Created()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range adminCommunities {
|
|
|
|
adminCommunitiesPks = append(adminCommunitiesPks, c.PrivateKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.transport.InitCommunityFilters(adminCommunitiesPks)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-08-29 06:33:46 +00:00
|
|
|
// Get chat IDs and public keys from the existing chats.
|
|
|
|
// TODO: Get only active chats by the query.
|
2019-12-02 15:34:05 +00:00
|
|
|
chats, err := m.persistence.Chats()
|
2019-08-29 06:33:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, chat := range chats {
|
2020-02-07 11:56:30 +00:00
|
|
|
if err := chat.Validate(); err != nil {
|
|
|
|
logger.Warn("failed to validate chat", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-09-02 08:36:07 +00:00
|
|
|
if err = m.initChatFirstMessageTimestamp(chat); err != nil {
|
|
|
|
logger.Warn("failed to init first message timestamp", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allChats.Store(chat.ID, chat)
|
2021-08-25 08:59:53 +00:00
|
|
|
|
2020-11-06 10:57:05 +00:00
|
|
|
if !chat.Active || chat.Timeline() {
|
2019-08-29 06:33:46 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-11-06 10:57:05 +00:00
|
|
|
|
2019-08-29 06:33:46 +00:00
|
|
|
switch chat.ChatType {
|
2020-10-20 15:10:28 +00:00
|
|
|
case ChatTypePublic, ChatTypeProfile:
|
2019-08-29 06:33:46 +00:00
|
|
|
publicChatIDs = append(publicChatIDs, chat.ID)
|
2020-11-18 09:16:51 +00:00
|
|
|
case ChatTypeCommunityChat:
|
2022-05-27 09:14:40 +00:00
|
|
|
// TODO not public chat now
|
2020-11-18 09:16:51 +00:00
|
|
|
publicChatIDs = append(publicChatIDs, chat.ID)
|
2019-08-29 06:33:46 +00:00
|
|
|
case ChatTypeOneToOne:
|
2019-12-02 15:34:05 +00:00
|
|
|
pk, err := chat.PublicKey()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
publicKeys = append(publicKeys, pk)
|
2019-08-29 06:33:46 +00:00
|
|
|
case ChatTypePrivateGroupChat:
|
|
|
|
for _, member := range chat.Members {
|
|
|
|
publicKey, err := member.PublicKey()
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "invalid public key for member %s in chat %s", member.ID, chat.Name)
|
|
|
|
}
|
|
|
|
publicKeys = append(publicKeys, publicKey)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return errors.New("invalid chat type")
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 15:15:22 +00:00
|
|
|
// upsert timeline chat
|
|
|
|
err = m.ensureTimelineChat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-07-22 17:41:49 +00:00
|
|
|
// upsert profile chat
|
2021-03-25 15:15:22 +00:00
|
|
|
err = m.ensureMyOwnProfileChat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-29 06:33:46 +00:00
|
|
|
|
|
|
|
// Get chat IDs and public keys from the contacts.
|
2019-12-02 15:34:05 +00:00
|
|
|
contacts, err := m.persistence.Contacts()
|
2019-08-29 06:33:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-04-07 12:57:14 +00:00
|
|
|
for idx, contact := range contacts {
|
|
|
|
m.allContacts.Store(contact.ID, contacts[idx])
|
2019-08-29 06:33:46 +00:00
|
|
|
// We only need filters for contacts added by us and not blocked.
|
2021-10-01 14:50:16 +00:00
|
|
|
if !contact.Added || contact.Blocked {
|
2019-08-29 06:33:46 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
publicKey, err := contact.PublicKey()
|
|
|
|
if err != nil {
|
|
|
|
logger.Error("failed to get contact's public key", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
publicKeys = append(publicKeys, publicKey)
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
installations, err := m.encryptor.GetOurInstallations(&m.identity.PublicKey)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, installation := range installations {
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allInstallations.Store(installation.ID, installation)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
2019-09-02 09:29:06 +00:00
|
|
|
_, err = m.transport.InitFilters(publicChatIDs, publicKeys)
|
2019-08-29 06:33:46 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-07-17 22:25:42 +00:00
|
|
|
// Shutdown takes care of ensuring a clean shutdown of Messenger
|
|
|
|
func (m *Messenger) Shutdown() (err error) {
|
2021-01-14 22:15:13 +00:00
|
|
|
close(m.quit)
|
2022-04-22 07:42:22 +00:00
|
|
|
m.downloadHistoryArchiveTasksWaitGroup.Wait()
|
2021-01-14 22:15:13 +00:00
|
|
|
for i, task := range m.shutdownTasks {
|
|
|
|
m.logger.Debug("running shutdown task", zap.Int("n", i))
|
2019-07-17 22:25:42 +00:00
|
|
|
if tErr := task(); tErr != nil {
|
2021-01-14 22:15:13 +00:00
|
|
|
m.logger.Info("shutdown task failed", zap.Error(tErr))
|
2019-07-17 22:25:42 +00:00
|
|
|
if err == nil {
|
|
|
|
// First error appeared.
|
|
|
|
err = tErr
|
|
|
|
} else {
|
|
|
|
// We return all errors. They will be concatenated in the order of occurrence,
|
|
|
|
// however, they will also be returned as a single error.
|
|
|
|
err = errors.Wrap(err, tErr.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) EnableInstallation(id string) error {
|
2021-03-29 15:41:30 +00:00
|
|
|
installation, ok := m.allInstallations.Load(id)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return errors.New("no installation found")
|
|
|
|
}
|
|
|
|
|
|
|
|
err := m.encryptor.EnableInstallation(&m.identity.PublicKey, id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
installation.Enabled = true
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
|
|
m.allInstallations.Store(id, installation)
|
2020-01-10 18:59:01 +00:00
|
|
|
return nil
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) DisableInstallation(id string) error {
|
2021-03-29 15:41:30 +00:00
|
|
|
installation, ok := m.allInstallations.Load(id)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return errors.New("no installation found")
|
|
|
|
}
|
|
|
|
|
|
|
|
err := m.encryptor.DisableInstallation(&m.identity.PublicKey, id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
installation.Enabled = false
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
|
|
m.allInstallations.Store(id, installation)
|
2020-01-10 18:59:01 +00:00
|
|
|
return nil
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
func (m *Messenger) Installations() []*multidevice.Installation {
|
2021-03-29 15:41:30 +00:00
|
|
|
installations := make([]*multidevice.Installation, m.allInstallations.Len())
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
var i = 0
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
|
2020-01-10 18:59:01 +00:00
|
|
|
installations[i] = installation
|
|
|
|
i++
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
2020-01-10 18:59:01 +00:00
|
|
|
return installations
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
func (m *Messenger) setInstallationMetadata(id string, data *multidevice.InstallationMetadata) error {
|
2021-03-29 15:41:30 +00:00
|
|
|
installation, ok := m.allInstallations.Load(id)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return errors.New("no installation found")
|
|
|
|
}
|
|
|
|
|
|
|
|
installation.InstallationMetadata = data
|
2019-07-17 22:25:42 +00:00
|
|
|
return m.encryptor.SetInstallationMetadata(&m.identity.PublicKey, id, data)
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
func (m *Messenger) SetInstallationMetadata(id string, data *multidevice.InstallationMetadata) error {
|
|
|
|
return m.setInstallationMetadata(id, data)
|
|
|
|
}
|
|
|
|
|
2019-07-17 22:25:42 +00:00
|
|
|
// NOT IMPLEMENTED
|
|
|
|
func (m *Messenger) SelectMailserver(id string) error {
|
|
|
|
return ErrNotImplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOT IMPLEMENTED
|
|
|
|
func (m *Messenger) AddMailserver(enode string) error {
|
|
|
|
return ErrNotImplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOT IMPLEMENTED
|
|
|
|
func (m *Messenger) RemoveMailserver(id string) error {
|
|
|
|
return ErrNotImplemented
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOT IMPLEMENTED
|
|
|
|
func (m *Messenger) Mailservers() ([]string, error) {
|
|
|
|
return nil, ErrNotImplemented
|
|
|
|
}
|
|
|
|
|
2022-09-02 08:36:07 +00:00
|
|
|
func (m *Messenger) initChatFirstMessageTimestamp(chat *Chat) error {
|
|
|
|
if !chat.CommunityChat() || chat.FirstMessageTimestamp != FirstMessageTimestampUndefined {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
oldestMessageTimestamp, hasAnyMessage, err := m.persistence.OldestMessageWhisperTimestampByChatID(chat.ID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if hasAnyMessage {
|
|
|
|
if oldestMessageTimestamp == FirstMessageTimestampUndefined {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return m.updateChatFirstMessageTimestamp(chat, whisperToUnixTimestamp(oldestMessageTimestamp), &MessengerResponse{})
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.updateChatFirstMessageTimestamp(chat, FirstMessageTimestampNoMessage, &MessengerResponse{})
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
func (m *Messenger) addMessagesAndChat(chat *Chat, messages []*common.Message, response *MessengerResponse) (*MessengerResponse, error) {
|
|
|
|
response.AddChat(chat)
|
|
|
|
response.AddMessages(messages)
|
|
|
|
err := m.persistence.SaveMessages(response.Messages())
|
2020-01-17 12:39:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return response, m.saveChat(chat)
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
|
|
|
|
2020-08-18 15:07:48 +00:00
|
|
|
func (m *Messenger) reregisterForPushNotifications() error {
|
|
|
|
m.logger.Info("contact state changed, re-registering for push notification")
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
return m.pushNotificationClient.Reregister(m.pushNotificationOptions())
|
2020-08-18 15:07:48 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
// pull a message from the database and send it again
|
|
|
|
func (m *Messenger) reSendRawMessage(ctx context.Context, messageID string) error {
|
2020-01-10 18:59:01 +00:00
|
|
|
message, err := m.persistence.RawMessageByID(messageID)
|
2019-09-26 07:01:17 +00:00
|
|
|
if err != nil {
|
2020-01-10 18:59:01 +00:00
|
|
|
return err
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(message.LocalChatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return errors.New("chat not found")
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:22:22 +00:00
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
2020-07-15 06:31:39 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: message.Payload,
|
|
|
|
MessageType: message.MessageType,
|
|
|
|
Recipients: message.Recipients,
|
|
|
|
ResendAutomatically: message.ResendAutomatically,
|
2020-12-15 14:43:41 +00:00
|
|
|
SendCount: message.SendCount,
|
2020-01-10 18:59:01 +00:00
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-12-15 14:43:41 +00:00
|
|
|
// ReSendChatMessage pulls a message from the database and sends it again
|
|
|
|
func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) error {
|
|
|
|
return m.reSendRawMessage(ctx, messageID)
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
func (m *Messenger) hasPairedDevices() bool {
|
2021-08-06 15:40:23 +00:00
|
|
|
logger := m.logger.Named("hasPairedDevices")
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
var count int
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
|
|
|
|
if installation.Enabled {
|
2020-02-10 11:22:37 +00:00
|
|
|
count++
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
2021-08-06 15:40:23 +00:00
|
|
|
logger.Debug("installations info",
|
|
|
|
zap.Int("Number of installations", m.allInstallations.Len()),
|
|
|
|
zap.Int("Number of enabled installations", count))
|
2020-01-10 18:59:01 +00:00
|
|
|
return count > 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// sendToPairedDevices will check if we have any paired devices and send to them if necessary
|
2020-07-28 13:22:22 +00:00
|
|
|
func (m *Messenger) sendToPairedDevices(ctx context.Context, spec common.RawMessage) error {
|
2020-01-10 18:59:01 +00:00
|
|
|
hasPairedDevices := m.hasPairedDevices()
|
|
|
|
// We send a message to any paired device
|
|
|
|
if hasPairedDevices {
|
2021-06-23 14:13:48 +00:00
|
|
|
_, err := m.sender.SendPrivate(ctx, &m.identity.PublicKey, &spec)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
2019-07-17 22:25:42 +00:00
|
|
|
|
2020-07-28 13:22:22 +00:00
|
|
|
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec common.RawMessage) ([]byte, error) {
|
2020-01-10 18:59:01 +00:00
|
|
|
var err error
|
|
|
|
var id []byte
|
|
|
|
|
2021-06-23 14:13:48 +00:00
|
|
|
id, err = m.sender.SendPairInstallation(ctx, &m.identity.PublicKey, spec)
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
spec.ID = types.EncodeHex(id)
|
2020-02-10 11:22:37 +00:00
|
|
|
spec.SendCount++
|
2020-07-28 13:22:22 +00:00
|
|
|
err = m.persistence.SaveRawMessage(&spec)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return id, nil
|
|
|
|
}
|
|
|
|
|
2022-05-27 09:14:40 +00:00
|
|
|
func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMessage) (common.RawMessage, error) {
|
2020-01-10 18:59:01 +00:00
|
|
|
var err error
|
|
|
|
var id []byte
|
2022-05-27 09:14:40 +00:00
|
|
|
logger := m.logger.With(zap.String("site", "dispatchMessage"), zap.String("chatID", rawMessage.LocalChatID))
|
|
|
|
chat, ok := m.allChats.Load(rawMessage.LocalChatID)
|
2019-12-02 15:34:05 +00:00
|
|
|
if !ok {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, errors.New("no chat found")
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 14:10:48 +00:00
|
|
|
switch chat.ChatType {
|
|
|
|
case ChatTypeOneToOne:
|
2019-12-02 15:34:05 +00:00
|
|
|
publicKey, err := chat.PublicKey()
|
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2021-02-23 15:47:45 +00:00
|
|
|
|
|
|
|
//SendPrivate will alter message identity and possibly datasyncid, so we save an unchanged
|
|
|
|
//message for sending to paired devices later
|
2022-05-27 09:14:40 +00:00
|
|
|
specCopyForPairedDevices := rawMessage
|
|
|
|
if !common.IsPubKeyEqual(publicKey, &m.identity.PublicKey) || rawMessage.SkipEncryption {
|
|
|
|
id, err = m.sender.SendPrivate(ctx, publicKey, &rawMessage)
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
|
2021-02-23 15:47:45 +00:00
|
|
|
err = m.sendToPairedDevices(ctx, specCopyForPairedDevices)
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2019-10-14 14:10:48 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2020-10-20 15:10:28 +00:00
|
|
|
case ChatTypePublic, ChatTypeProfile:
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("sending public message", zap.String("chatName", chat.Name))
|
2022-05-27 09:14:40 +00:00
|
|
|
id, err = m.sender.SendPublic(ctx, chat.ID, rawMessage)
|
2019-10-14 14:10:48 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
2020-11-18 09:16:51 +00:00
|
|
|
case ChatTypeCommunityChat:
|
|
|
|
// TODO: add grant
|
|
|
|
canPost, err := m.communitiesManager.CanPost(&m.identity.PublicKey, chat.CommunityID, chat.CommunityChatID(), nil)
|
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
2020-12-22 16:20:12 +00:00
|
|
|
|
|
|
|
// We allow emoji reactions by anyone
|
2022-05-27 09:14:40 +00:00
|
|
|
if rawMessage.MessageType != protobuf.ApplicationMetadataMessage_EMOJI_REACTION && !canPost {
|
2020-11-18 09:16:51 +00:00
|
|
|
m.logger.Error("can't post on chat", zap.String("chat-id", chat.ID), zap.String("chat-name", chat.Name))
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, errors.New("can't post on chat")
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
logger.Debug("sending community chat message", zap.String("chatName", chat.Name))
|
2022-05-27 09:14:40 +00:00
|
|
|
isEncrypted, err := m.communitiesManager.IsEncrypted(chat.CommunityID)
|
2020-11-18 09:16:51 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
|
|
|
}
|
|
|
|
if !isEncrypted {
|
|
|
|
id, err = m.sender.SendPublic(ctx, chat.ID, rawMessage)
|
|
|
|
} else {
|
|
|
|
rawMessage.CommunityID, err = types.DecodeHex(chat.CommunityID)
|
|
|
|
|
|
|
|
if err == nil {
|
2022-09-05 12:08:44 +00:00
|
|
|
id, err = m.sender.SendCommunityMessage(ctx, rawMessage)
|
2022-05-27 09:14:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return rawMessage, err
|
2020-11-18 09:16:51 +00:00
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
case ChatTypePrivateGroupChat:
|
|
|
|
logger.Debug("sending group message", zap.String("chatName", chat.Name))
|
2022-05-27 09:14:40 +00:00
|
|
|
if rawMessage.Recipients == nil {
|
|
|
|
rawMessage.Recipients, err = chat.MembersAsPublicKeys()
|
2022-05-20 10:53:28 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-15 17:47:30 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
hasPairedDevices := m.hasPairedDevices()
|
|
|
|
|
|
|
|
if !hasPairedDevices {
|
2021-03-09 12:48:15 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
// Filter out my key from the recipients
|
|
|
|
n := 0
|
2022-05-27 09:14:40 +00:00
|
|
|
for _, recipient := range rawMessage.Recipients {
|
2020-07-06 08:54:22 +00:00
|
|
|
if !common.IsPubKeyEqual(recipient, &m.identity.PublicKey) {
|
2022-05-27 09:14:40 +00:00
|
|
|
rawMessage.Recipients[n] = recipient
|
2020-01-10 18:59:01 +00:00
|
|
|
n++
|
|
|
|
}
|
|
|
|
}
|
2022-05-27 09:14:40 +00:00
|
|
|
rawMessage.Recipients = rawMessage.Recipients[:n]
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
// We won't really send the message out if there's no recipients
|
2022-05-27 09:14:40 +00:00
|
|
|
if len(rawMessage.Recipients) == 0 {
|
|
|
|
rawMessage.Sent = true
|
2021-01-22 13:59:45 +00:00
|
|
|
}
|
|
|
|
|
2021-01-15 17:47:30 +00:00
|
|
|
// We skip wrapping in some cases (emoji reactions for example)
|
2022-05-27 09:14:40 +00:00
|
|
|
if !rawMessage.SkipGroupMessageWrap {
|
|
|
|
rawMessage.MessageType = protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE
|
2021-01-15 17:47:30 +00:00
|
|
|
}
|
2021-01-22 13:59:45 +00:00
|
|
|
|
2022-05-27 09:14:40 +00:00
|
|
|
id, err = m.sender.SendGroup(ctx, rawMessage.Recipients, rawMessage)
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
|
2019-10-14 14:10:48 +00:00
|
|
|
default:
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, errors.New("chat type not supported")
|
2019-10-14 14:10:48 +00:00
|
|
|
}
|
2022-05-27 09:14:40 +00:00
|
|
|
rawMessage.ID = types.EncodeHex(id)
|
|
|
|
rawMessage.SendCount++
|
|
|
|
rawMessage.LastSent = m.getTimesource().GetCurrentTime()
|
|
|
|
err = m.persistence.SaveRawMessage(&rawMessage)
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, err
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
|
|
|
|
2022-05-27 09:14:40 +00:00
|
|
|
return rawMessage, nil
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
// SendChatMessage takes a minimal message and sends it based on the corresponding chat
|
2020-09-01 13:27:01 +00:00
|
|
|
func (m *Messenger) SendChatMessage(ctx context.Context, message *common.Message) (*MessengerResponse, error) {
|
2020-12-01 09:43:46 +00:00
|
|
|
return m.sendChatMessage(ctx, message)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendChatMessages takes a array of messages and sends it based on the corresponding chats
|
|
|
|
func (m *Messenger) SendChatMessages(ctx context.Context, messages []*common.Message) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2020-12-01 09:43:46 +00:00
|
|
|
for _, message := range messages {
|
2021-03-29 15:41:30 +00:00
|
|
|
messageResponse, err := m.SendChatMessage(ctx, message)
|
2020-12-01 09:43:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
err = response.Merge(messageResponse)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &response, nil
|
|
|
|
}
|
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
func (m *Messenger) OpenAndAdjustImage(inputImage userimage.CroppedImage, crop bool) ([]byte, error) {
|
|
|
|
file, err := os.Open(inputImage.ImagePath)
|
2022-02-17 15:13:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-08-08 15:22:22 +00:00
|
|
|
defer file.Close()
|
2022-02-17 15:13:10 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
payload, err := ioutil.ReadAll(file)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-27 11:21:02 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
img, err := userimage.Decode(inputImage.ImagePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-13 13:16:17 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
if crop {
|
|
|
|
cropRect := image.Rectangle{
|
|
|
|
Min: image.Point{X: inputImage.X, Y: inputImage.Y},
|
|
|
|
Max: image.Point{X: inputImage.X + inputImage.Width, Y: inputImage.Y + inputImage.Height},
|
2022-05-27 11:21:02 +00:00
|
|
|
}
|
2022-08-08 15:22:22 +00:00
|
|
|
img, err = userimage.Crop(img, cropRect)
|
2022-05-27 11:21:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-05-13 13:16:17 +00:00
|
|
|
}
|
2022-08-08 15:22:22 +00:00
|
|
|
}
|
2022-05-27 11:21:02 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
bb := bytes.NewBuffer([]byte{})
|
2022-09-02 13:59:52 +00:00
|
|
|
err = userimage.CompressToFileLimits(bb, img, userimage.FileSizeLimits{Ideal: idealTargetImageSize, Max: resizeTargetImageSize})
|
2022-05-27 11:21:02 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-05-27 11:21:02 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
// We keep the smallest one
|
|
|
|
if len(payload) > len(bb.Bytes()) {
|
|
|
|
payload = bb.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(payload) > maxChatMessageImageSize {
|
|
|
|
return nil, errors.New("image too large")
|
|
|
|
}
|
|
|
|
|
|
|
|
return payload, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendChatMessage takes a minimal message and sends it based on the corresponding chat
|
|
|
|
func (m *Messenger) sendChatMessage(ctx context.Context, message *common.Message) (*MessengerResponse, error) {
|
|
|
|
displayName, err := m.settings.DisplayName()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
message.DisplayName = displayName
|
|
|
|
if len(message.ImagePath) != 0 {
|
2022-05-27 11:21:02 +00:00
|
|
|
|
2022-08-08 15:22:22 +00:00
|
|
|
payload, err := m.OpenAndAdjustImage(userimage.CroppedImage{ImagePath: message.ImagePath}, false)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2022-05-27 11:21:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 13:16:17 +00:00
|
|
|
image := protobuf.ImageMessage{
|
|
|
|
Payload: payload,
|
|
|
|
Type: images.ImageType(payload),
|
|
|
|
}
|
|
|
|
message.Payload = &protobuf.ChatMessage_Image{Image: &image}
|
|
|
|
|
2020-11-18 09:16:51 +00:00
|
|
|
} else if len(message.CommunityID) != 0 {
|
|
|
|
community, err := m.communitiesManager.GetByIDString(message.CommunityID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if community == nil {
|
|
|
|
return nil, errors.New("community not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
wrappedCommunity, err := community.ToBytes()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
message.Payload = &protobuf.ChatMessage_Community{Community: wrappedCommunity}
|
|
|
|
|
|
|
|
message.ContentType = protobuf.ChatMessage_COMMUNITY
|
|
|
|
} else if len(message.AudioPath) != 0 {
|
2020-06-17 18:55:49 +00:00
|
|
|
file, err := os.Open(message.AudioPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
payload, err := ioutil.ReadAll(file)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
2020-06-23 14:30:39 +00:00
|
|
|
audioMessage := message.GetAudio()
|
|
|
|
if audioMessage == nil {
|
|
|
|
return nil, errors.New("no audio has been passed")
|
2020-06-17 18:55:49 +00:00
|
|
|
}
|
2020-06-23 14:30:39 +00:00
|
|
|
audioMessage.Payload = payload
|
|
|
|
audioMessage.Type = audio.Type(payload)
|
|
|
|
message.Payload = &protobuf.ChatMessage_Audio{Audio: audioMessage}
|
2020-07-16 06:21:02 +00:00
|
|
|
err = os.Remove(message.AudioPath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-06-17 18:55:49 +00:00
|
|
|
}
|
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(message.ChatId)
|
2019-12-02 15:34:05 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
err = m.handleStandaloneChatIdentity(chat)
|
2020-11-09 15:16:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = extendMessageFromChat(message, chat, &m.identity.PublicKey, m.getTimesource())
|
2019-07-26 07:17:29 +00:00
|
|
|
if err != nil {
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
return nil, err
|
2019-07-26 07:17:29 +00:00
|
|
|
}
|
|
|
|
|
2020-07-26 22:06:58 +00:00
|
|
|
encodedMessage, err := m.encodeChatEntity(chat, message)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-09-26 07:01:17 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage := common.RawMessage{
|
2020-07-22 07:41:40 +00:00
|
|
|
LocalChatID: chat.ID,
|
2020-09-02 14:11:16 +00:00
|
|
|
SendPushNotification: m.featureFlags.PushNotifications,
|
2020-07-22 07:41:40 +00:00
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
|
|
|
ResendAutomatically: true,
|
2021-01-22 13:59:45 +00:00
|
|
|
}
|
2022-05-27 09:14:40 +00:00
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err = m.dispatchMessage(ctx, rawMessage)
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
if rawMessage.Sent {
|
|
|
|
message.OutgoingStatus = common.OutgoingStatusSent
|
|
|
|
}
|
|
|
|
message.ID = rawMessage.ID
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.getTimesource())
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-07-17 22:25:42 +00:00
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-03 13:11:55 +00:00
|
|
|
msg, err := m.pullMessagesAndResponsesFromDB([]*common.Message{message})
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-02-10 17:19:34 +00:00
|
|
|
|
2022-09-02 08:36:07 +00:00
|
|
|
if err := m.updateChatFirstMessageTimestamp(chat, whisperToUnixTimestamp(message.WhisperTimestamp), &response); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
|
2022-09-02 08:36:07 +00:00
|
|
|
response.SetMessages(msg)
|
2021-01-11 10:32:51 +00:00
|
|
|
response.AddChat(chat)
|
2022-01-18 16:31:34 +00:00
|
|
|
|
2021-10-29 14:29:28 +00:00
|
|
|
m.logger.Debug("sent message", zap.String("id", message.ID))
|
2022-02-10 17:19:34 +00:00
|
|
|
m.prepareMessages(response.messages)
|
|
|
|
|
2019-12-02 15:34:05 +00:00
|
|
|
return &response, m.saveChat(chat)
|
2019-07-17 22:25:42 +00:00
|
|
|
}
|
|
|
|
|
2022-09-02 08:36:07 +00:00
|
|
|
func whisperToUnixTimestamp(whisperTimestamp uint64) uint32 {
|
|
|
|
return uint32(whisperTimestamp / 1000)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) updateChatFirstMessageTimestamp(chat *Chat, timestamp uint32, response *MessengerResponse) error {
|
|
|
|
// Currently supported only for communities
|
|
|
|
if !chat.CommunityChat() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
community, err := m.communitiesManager.GetByIDString(chat.CommunityID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if community.IsAdmin() && chat.UpdateFirstMessageTimestamp(timestamp) {
|
|
|
|
community, changes, err := m.communitiesManager.EditChatFirstMessageTimestamp(community.ID(), chat.ID, chat.FirstMessageTimestamp)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
response.AddCommunity(community)
|
|
|
|
response.CommunityChanges = append(response.CommunityChanges, changes)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-03-10 21:05:17 +00:00
|
|
|
func (m *Messenger) ShareImageMessage(request *requests.ShareImageMessage) (*MessengerResponse, error) {
|
|
|
|
if err := request.Validate(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
response := &MessengerResponse{}
|
|
|
|
|
|
|
|
msg, err := m.persistence.MessageByID(request.MessageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var messages []*common.Message
|
|
|
|
for _, pk := range request.Users {
|
|
|
|
message := &common.Message{}
|
|
|
|
message.ChatId = pk.String()
|
|
|
|
message.Payload = msg.Payload
|
|
|
|
message.Text = "This message has been shared with you"
|
|
|
|
message.ContentType = protobuf.ChatMessage_IMAGE
|
|
|
|
messages = append(messages, message)
|
|
|
|
|
|
|
|
r, err := m.CreateOneToOneChat(&requests.CreateOneToOneChat{ID: pk})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := response.Merge(r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sendMessagesResponse, err := m.SendChatMessages(context.Background(), messages)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := response.Merge(sendMessagesResponse); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
2022-03-24 09:35:56 +00:00
|
|
|
func (m *Messenger) syncProfilePictures() error {
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
keyUID := m.account.KeyUID
|
|
|
|
images, err := m.multiAccounts.GetIdentityImages(keyUID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-18 10:42:51 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
2022-03-24 09:35:56 +00:00
|
|
|
pictures := make([]*protobuf.SyncProfilePicture, len(images))
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
for i, image := range images {
|
|
|
|
p := &protobuf.SyncProfilePicture{}
|
|
|
|
p.Name = image.Name
|
|
|
|
p.Payload = image.Payload
|
|
|
|
p.Width = uint32(image.Width)
|
|
|
|
p.Height = uint32(image.Height)
|
|
|
|
p.FileSize = uint32(image.FileSize)
|
|
|
|
p.ResizeTarget = uint32(image.ResizeTarget)
|
|
|
|
if image.Clock == 0 {
|
|
|
|
p.Clock = clock
|
|
|
|
} else {
|
|
|
|
p.Clock = image.Clock
|
|
|
|
}
|
|
|
|
pictures[i] = p
|
|
|
|
}
|
|
|
|
|
|
|
|
message := &protobuf.SyncProfilePictures{}
|
|
|
|
message.KeyUid = keyUID
|
|
|
|
message.Pictures = pictures
|
|
|
|
|
|
|
|
encodedMessage, err := proto.Marshal(message)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_PROFILE_PICTURE,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2020-01-15 07:25:09 +00:00
|
|
|
// SyncDevices sends all public chats and contacts to paired devices
|
2020-11-24 13:42:36 +00:00
|
|
|
// TODO remove use of photoPath in contacts
|
2021-03-29 15:41:30 +00:00
|
|
|
func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string) (err error) {
|
2020-01-15 07:25:09 +00:00
|
|
|
myID := contactIDFromPublicKey(&m.identity.PublicKey)
|
|
|
|
|
2022-02-17 15:13:10 +00:00
|
|
|
displayName, err := m.settings.DisplayName()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, photoPath); err != nil {
|
2020-01-15 07:25:09 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
|
2021-10-05 17:26:02 +00:00
|
|
|
isPublicChat := !chat.Timeline() && !chat.ProfileUpdates() && chat.Public()
|
|
|
|
if isPublicChat && chat.Active {
|
2021-03-29 15:41:30 +00:00
|
|
|
err = m.syncPublicChat(ctx, chat)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
2020-01-15 07:25:09 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
|
2022-08-24 11:31:04 +00:00
|
|
|
if (isPublicChat || chat.OneToOne() || chat.PrivateGroupChat()) && !chat.Active && chat.DeletedAtClockValue > 0 {
|
2021-12-07 14:34:43 +00:00
|
|
|
pending, err := m.persistence.HasPendingNotificationsForChat(chat.ID)
|
2021-10-05 17:26:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
2021-12-07 14:34:43 +00:00
|
|
|
|
|
|
|
if !pending {
|
|
|
|
err = m.syncChatRemoving(ctx, chatID)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2021-10-05 17:26:02 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 10:33:32 +00:00
|
|
|
if (isPublicChat || chat.OneToOne() || chat.PrivateGroupChat() || chat.CommunityChat()) && chat.Active {
|
|
|
|
err := m.syncChatMessagesRead(ctx, chatID, chat.ReadMessagesAtClockValue)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-10 10:00:59 +00:00
|
|
|
if isPublicChat && chat.Active && chat.DeletedAtClockValue > 0 {
|
|
|
|
err = m.syncClearHistory(ctx, chat)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2020-01-15 07:25:09 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
2021-09-03 08:26:05 +00:00
|
|
|
if contact.ID != myID &&
|
2021-10-01 14:50:16 +00:00
|
|
|
(contact.LocalNickname != "" || contact.Added || contact.Blocked) {
|
2021-03-29 15:41:30 +00:00
|
|
|
if err = m.syncContact(ctx, contact); err != nil {
|
|
|
|
return false
|
2020-01-15 07:25:09 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
2020-01-15 07:25:09 +00:00
|
|
|
|
2021-08-06 15:40:23 +00:00
|
|
|
cs, err := m.communitiesManager.JoinedAndPendingCommunitiesWithRequests()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, c := range cs {
|
|
|
|
if err = m.syncCommunity(ctx, c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
bookmarks, err := m.browserDatabase.GetBookmarks()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, b := range bookmarks {
|
|
|
|
if err = m.SyncBookmark(ctx, b); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:49:44 +00:00
|
|
|
trustedUsers, err := m.verificationDatabase.GetAllTrustStatus()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for id, ts := range trustedUsers {
|
|
|
|
if err = m.SyncTrustedUser(ctx, id, ts); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
verificationRequests, err := m.verificationDatabase.GetVerificationRequests()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for i := range verificationRequests {
|
|
|
|
if err = m.SyncVerificationRequest(ctx, &verificationRequests[i]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 18:47:00 +00:00
|
|
|
err = m.syncSettings()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-03-24 09:35:56 +00:00
|
|
|
err = m.syncProfilePictures()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-05-18 10:42:51 +00:00
|
|
|
accounts, err := m.settings.GetAccounts()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-08-25 17:20:26 +00:00
|
|
|
|
|
|
|
ids, err := m.persistence.LatestContactRequestIDs()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for id, state := range ids {
|
|
|
|
if state == common.ContactRequestStateAccepted || state == common.ContactRequestStateDismissed {
|
2022-09-08 14:37:22 +00:00
|
|
|
accepted := state == common.ContactRequestStateAccepted
|
2022-08-25 17:20:26 +00:00
|
|
|
err := m.syncContactRequestDecision(ctx, id, accepted)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-14 10:46:11 +00:00
|
|
|
err = m.syncWallets(accounts)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
savedAddresses, err := m.savedAddressesManager.GetRawSavedAddresses()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range savedAddresses {
|
|
|
|
sa := savedAddresses[i]
|
|
|
|
|
|
|
|
err = m.syncSavedAddress(ctx, sa)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2022-05-18 10:42:51 +00:00
|
|
|
}
|
|
|
|
|
2022-07-06 16:12:49 +00:00
|
|
|
func (m *Messenger) SaveAccounts(accs []*accounts.Account) error {
|
|
|
|
err := m.settings.SaveAccounts(accs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return m.syncWallets(accs)
|
|
|
|
}
|
2022-05-18 10:42:51 +00:00
|
|
|
|
2022-07-06 16:12:49 +00:00
|
|
|
func (m *Messenger) DeleteAccount(address types.Address) error {
|
2022-07-11 17:41:43 +00:00
|
|
|
|
|
|
|
acc, err := m.settings.GetAccountByAddress(address)
|
2022-07-06 16:12:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-05-18 10:42:51 +00:00
|
|
|
|
2022-07-11 17:41:43 +00:00
|
|
|
err = m.settings.DeleteAccount(address)
|
2022-07-06 16:12:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
acc.Removed = true
|
|
|
|
|
|
|
|
accs := []*accounts.Account{acc}
|
|
|
|
return m.syncWallets(accs)
|
2022-05-18 10:42:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// syncWallets syncs all wallets with paired devices
|
|
|
|
func (m *Messenger) syncWallets(accs []*accounts.Account) error {
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
accountMessages := make([]*protobuf.SyncWalletAccount, 0)
|
|
|
|
for _, acc := range accs {
|
2022-07-06 16:12:49 +00:00
|
|
|
if acc.Type != accounts.AccountTypeWatch &&
|
|
|
|
acc.Type != accounts.AccountTypeGenerated {
|
2022-05-18 10:42:51 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
var accountClock uint64
|
|
|
|
if acc.Clock == 0 {
|
|
|
|
accountClock = clock
|
|
|
|
} else {
|
|
|
|
accountClock = acc.Clock
|
|
|
|
}
|
|
|
|
syncMessage := &protobuf.SyncWalletAccount{
|
|
|
|
Clock: accountClock,
|
|
|
|
Address: acc.Address.Bytes(),
|
|
|
|
Wallet: acc.Wallet,
|
|
|
|
Chat: acc.Chat,
|
2022-08-25 15:09:08 +00:00
|
|
|
Type: acc.Type.String(),
|
2022-05-18 10:42:51 +00:00
|
|
|
Storage: acc.Storage,
|
|
|
|
Path: acc.Path,
|
|
|
|
PublicKey: acc.PublicKey,
|
|
|
|
Name: acc.Name,
|
|
|
|
Color: acc.Color,
|
|
|
|
Hidden: acc.Hidden,
|
|
|
|
Removed: acc.Removed,
|
|
|
|
}
|
|
|
|
accountMessages = append(accountMessages, syncMessage)
|
|
|
|
}
|
|
|
|
|
|
|
|
message := &protobuf.SyncWalletAccounts{
|
|
|
|
Accounts: accountMessages,
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedMessage, err := proto.Marshal(message)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_WALLET_ACCOUNT,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
2020-01-15 07:25:09 +00:00
|
|
|
}
|
|
|
|
|
2022-08-07 14:25:03 +00:00
|
|
|
func (m *Messenger) syncContactRequestDecision(ctx context.Context, requestID string, accepted bool) error {
|
|
|
|
m.logger.Info("syncContactRequestDecision", zap.Any("from", requestID))
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
var status protobuf.SyncContactRequestDecision_DecisionStatus
|
|
|
|
if accepted {
|
|
|
|
status = protobuf.SyncContactRequestDecision_ACCEPTED
|
|
|
|
} else {
|
|
|
|
status = protobuf.SyncContactRequestDecision_DECLINED
|
|
|
|
}
|
|
|
|
|
|
|
|
message := &protobuf.SyncContactRequestDecision{
|
|
|
|
RequestId: requestID,
|
|
|
|
Clock: clock,
|
|
|
|
DecisionStatus: status,
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedMessage, err := proto.Marshal(message)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-08-06 15:40:23 +00:00
|
|
|
func (m *Messenger) getLastClockWithRelatedChat() (uint64, *Chat) {
|
|
|
|
chatID := contactIDFromPublicKey(&m.identity.PublicKey)
|
|
|
|
|
|
|
|
chat, ok := m.allChats.Load(chatID)
|
|
|
|
if !ok {
|
|
|
|
chat = OneToOneFromPublicKey(&m.identity.PublicKey, m.getTimesource())
|
|
|
|
// We don't want to show the chat to the user
|
|
|
|
chat.Active = false
|
|
|
|
}
|
|
|
|
|
|
|
|
m.allChats.Store(chat.ID, chat)
|
|
|
|
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
|
|
|
|
|
|
|
return clock, chat
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
// SendPairInstallation sends a pair installation message
|
|
|
|
func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerResponse, error) {
|
|
|
|
var err error
|
|
|
|
var response MessengerResponse
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
installation, ok := m.allInstallations.Load(m.installationID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("no installation found")
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
if installation.InstallationMetadata == nil {
|
|
|
|
return nil, errors.New("no installation metadata")
|
|
|
|
}
|
|
|
|
|
2021-08-06 15:40:23 +00:00
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
pairMessage := &protobuf.PairInstallation{
|
|
|
|
Clock: clock,
|
|
|
|
Name: installation.InstallationMetadata.Name,
|
|
|
|
InstallationId: installation.ID,
|
|
|
|
DeviceType: installation.InstallationMetadata.DeviceType}
|
|
|
|
encodedMessage, err := proto.Marshal(pairMessage)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:22:22 +00:00
|
|
|
_, err = m.dispatchPairInstallationMessage(ctx, common.RawMessage{
|
2021-08-06 15:40:23 +00:00
|
|
|
LocalChatID: chat.ID,
|
2020-01-10 18:59:01 +00:00
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
response.AddChat(chat)
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
chat.LastClockValue = clock
|
|
|
|
err = m.saveChat(chat)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &response, nil
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 07:25:09 +00:00
|
|
|
// syncPublicChat sync a public chat with paired devices
|
|
|
|
func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error {
|
|
|
|
var err error
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
2021-08-06 15:40:23 +00:00
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
2020-01-15 07:25:09 +00:00
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncInstallationPublicChat{
|
|
|
|
Clock: clock,
|
|
|
|
Id: publicChat.ID,
|
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:22:22 +00:00
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
2021-08-06 15:40:23 +00:00
|
|
|
LocalChatID: chat.ID,
|
2020-01-15 07:25:09 +00:00
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2022-02-10 10:00:59 +00:00
|
|
|
func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat) error {
|
|
|
|
var err error
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncClearHistory{
|
|
|
|
ChatId: publicChat.ID,
|
|
|
|
ClearedAt: publicChat.DeletedAtClockValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CLEAR_HISTORY,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2021-10-05 17:26:02 +00:00
|
|
|
func (m *Messenger) syncChatRemoving(ctx context.Context, id string) error {
|
|
|
|
var err error
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncChatRemoved{
|
|
|
|
Clock: clock,
|
|
|
|
Id: id,
|
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_REMOVED,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
// syncContact sync as contact with paired devices
|
|
|
|
func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
|
|
|
|
var err error
|
2021-09-03 08:26:05 +00:00
|
|
|
if contact.IsSyncing {
|
|
|
|
return nil
|
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2021-08-06 15:40:23 +00:00
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-09-03 08:26:05 +00:00
|
|
|
var ensName string
|
|
|
|
if contact.ENSVerified {
|
2022-02-17 15:13:10 +00:00
|
|
|
ensName = contact.EnsName
|
2021-09-03 08:26:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
oneToOneChat, ok := m.allChats.Load(contact.ID)
|
|
|
|
muted := false
|
|
|
|
if ok {
|
|
|
|
muted = oneToOneChat.Muted
|
|
|
|
}
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncInstallationContactV2{
|
2021-10-22 14:20:42 +00:00
|
|
|
LastUpdatedLocally: contact.LastUpdatedLocally,
|
|
|
|
LastUpdated: contact.LastUpdated,
|
|
|
|
Id: contact.ID,
|
|
|
|
EnsName: ensName,
|
|
|
|
LocalNickname: contact.LocalNickname,
|
|
|
|
Added: contact.Added,
|
|
|
|
Blocked: contact.Blocked,
|
|
|
|
Muted: muted,
|
|
|
|
Removed: contact.Removed,
|
2022-07-05 19:49:44 +00:00
|
|
|
VerificationStatus: int64(contact.VerificationStatus),
|
|
|
|
TrustStatus: int64(contact.TrustStatus),
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
2021-09-03 08:26:05 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:22:22 +00:00
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
2021-08-06 15:40:23 +00:00
|
|
|
LocalChatID: chat.ID,
|
2020-01-10 18:59:01 +00:00
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2021-08-06 15:40:23 +00:00
|
|
|
func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Community) error {
|
|
|
|
logger := m.logger.Named("syncCommunity")
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
logger.Debug("device has no paired devices")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
logger.Debug("device has paired device(s)")
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
2022-06-01 07:55:48 +00:00
|
|
|
communitySettings, err := m.communitiesManager.GetCommunitySettingsByID(community.ID())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
syncMessage, err := community.ToSyncCommunityProtobuf(clock, communitySettings)
|
2021-08-06 15:40:23 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-11-07 17:30:00 +00:00
|
|
|
encodedKeys, err := m.encryptor.GetAllHREncodedKeys(community.ID())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
syncMessage.EncryptionKeys = encodedKeys
|
|
|
|
|
2021-08-06 15:40:23 +00:00
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_COMMUNITY,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logger.Debug("message dispatched")
|
|
|
|
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
func (m *Messenger) SyncBookmark(ctx context.Context, bookmark *browsers.Bookmark) error {
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncBookmark{
|
2022-06-08 18:39:53 +00:00
|
|
|
Clock: clock,
|
|
|
|
Url: bookmark.URL,
|
|
|
|
Name: bookmark.Name,
|
|
|
|
ImageUrl: bookmark.ImageURL,
|
|
|
|
Removed: bookmark.Removed,
|
|
|
|
DeletedAt: bookmark.DeletedAt,
|
2022-01-17 03:42:11 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_BOOKMARK,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:49:44 +00:00
|
|
|
func (m *Messenger) SyncTrustedUser(ctx context.Context, publicKey string, ts verification.TrustStatus) error {
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncTrustedUser{
|
|
|
|
Clock: clock,
|
|
|
|
Id: publicKey,
|
|
|
|
Status: protobuf.SyncTrustedUser_TrustStatus(ts),
|
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_TRUSTED_USER,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) SyncVerificationRequest(ctx context.Context, vr *verification.Request) error {
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
clock, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncVerificationRequest{
|
2022-08-31 14:41:58 +00:00
|
|
|
Id: vr.ID,
|
2022-07-05 19:49:44 +00:00
|
|
|
Clock: clock,
|
|
|
|
From: vr.From,
|
|
|
|
To: vr.To,
|
|
|
|
Challenge: vr.Challenge,
|
|
|
|
Response: vr.Response,
|
|
|
|
RequestedAt: vr.RequestedAt,
|
|
|
|
RepliedAt: vr.RepliedAt,
|
|
|
|
VerificationStatus: protobuf.SyncVerificationRequest_VerificationStatus(vr.RequestStatus),
|
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_VERIFICATION_REQUEST,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
chat.LastClockValue = clock
|
|
|
|
return m.saveChat(chat)
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
// RetrieveAll retrieves messages from all filters, processes them and returns a
|
|
|
|
// MessengerResponse to the client
|
|
|
|
func (m *Messenger) RetrieveAll() (*MessengerResponse, error) {
|
|
|
|
chatWithMessages, err := m.transport.RetrieveRawAll()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2022-04-22 07:42:22 +00:00
|
|
|
return m.handleRetrievedMessages(chatWithMessages, true)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
2021-08-03 19:27:15 +00:00
|
|
|
func (m *Messenger) GetStats() types.StatsSummary {
|
|
|
|
return m.transport.GetStats()
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
type CurrentMessageState struct {
|
|
|
|
// Message is the protobuf message received
|
|
|
|
Message protobuf.ChatMessage
|
|
|
|
// MessageID is the ID of the message
|
|
|
|
MessageID string
|
|
|
|
// WhisperTimestamp is the whisper timestamp of the message
|
|
|
|
WhisperTimestamp uint64
|
|
|
|
// Contact is the contact associated with the author of the message
|
|
|
|
Contact *Contact
|
|
|
|
// PublicKey is the public key of the author of the message
|
|
|
|
PublicKey *ecdsa.PublicKey
|
|
|
|
}
|
|
|
|
|
|
|
|
type ReceivedMessageState struct {
|
|
|
|
// State on the message being processed
|
|
|
|
CurrentMessageState *CurrentMessageState
|
|
|
|
// AllChats in memory
|
2021-03-29 15:41:30 +00:00
|
|
|
AllChats *chatMap
|
2020-01-10 18:59:01 +00:00
|
|
|
// All contacts in memory
|
2021-03-29 15:41:30 +00:00
|
|
|
AllContacts *contactMap
|
2020-02-10 11:22:37 +00:00
|
|
|
// List of contacts modified
|
2021-03-29 15:41:30 +00:00
|
|
|
ModifiedContacts *stringBoolMap
|
2020-01-10 18:59:01 +00:00
|
|
|
// All installations in memory
|
2021-03-29 15:41:30 +00:00
|
|
|
AllInstallations *installationMap
|
2020-11-18 09:16:51 +00:00
|
|
|
// List of communities modified
|
2021-03-29 15:41:30 +00:00
|
|
|
ModifiedInstallations *stringBoolMap
|
2020-01-10 18:59:01 +00:00
|
|
|
// Map of existing messages
|
|
|
|
ExistingMessagesMap map[string]bool
|
2020-07-27 12:27:48 +00:00
|
|
|
// EmojiReactions is a list of emoji reactions for the current batch
|
|
|
|
// indexed by from-message-id-emoji-type
|
|
|
|
EmojiReactions map[string]*EmojiReaction
|
2020-08-07 13:49:37 +00:00
|
|
|
// GroupChatInvitations is a list of invitation requests or rejections
|
|
|
|
GroupChatInvitations map[string]*GroupChatInvitation
|
2020-01-10 18:59:01 +00:00
|
|
|
// Response to the client
|
|
|
|
Response *MessengerResponse
|
2020-02-07 11:30:26 +00:00
|
|
|
// Timesource is a time source for clock values/timestamps.
|
2022-07-05 19:49:44 +00:00
|
|
|
Timesource common.TimeSource
|
|
|
|
AllBookmarks map[string]*browsers.Bookmark
|
|
|
|
AllVerificationRequests []*verification.Request
|
|
|
|
AllTrustStatus map[string]verification.TrustStatus
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 15:47:45 +00:00
|
|
|
func (m *Messenger) markDeliveredMessages(acks [][]byte) {
|
|
|
|
for _, ack := range acks {
|
2021-03-09 12:48:15 +00:00
|
|
|
//get message ID from database by datasync ID, with at-least-one
|
|
|
|
// semantic
|
|
|
|
messageIDBytes, err := m.persistence.MarkAsConfirmed(ack, true)
|
2021-02-23 15:47:45 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Info("got datasync acknowledge for message we don't have in db", zap.String("ack", hex.EncodeToString(ack)))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-03-09 12:48:15 +00:00
|
|
|
messageID := messageIDBytes.String()
|
2021-02-23 15:47:45 +00:00
|
|
|
//mark messages as delivered
|
2022-01-31 10:33:56 +00:00
|
|
|
|
2021-02-23 15:47:45 +00:00
|
|
|
err = m.UpdateMessageOutgoingStatus(messageID, common.OutgoingStatusDelivered)
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Debug("Can't set message status as delivered", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
|
|
|
//send signal to client that message status updated
|
2021-04-19 12:09:46 +00:00
|
|
|
if m.config.messengerSignalsHandler != nil {
|
2021-02-23 15:47:45 +00:00
|
|
|
message, err := m.persistence.MessageByID(messageID)
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Debug("Can't get message from database", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2021-04-19 12:09:46 +00:00
|
|
|
m.config.messengerSignalsHandler.MessageDelivered(message.LocalChatID, messageID)
|
2021-02-23 15:47:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 16:23:45 +00:00
|
|
|
// addNewMessageNotification takes a common.Message and generates a new NotificationBody and appends it to the
|
2021-02-23 07:37:08 +00:00
|
|
|
// []Response.Notifications if the message is m.New
|
2021-10-07 04:42:27 +00:00
|
|
|
func (r *ReceivedMessageState) addNewMessageNotification(publicKey ecdsa.PublicKey, m *common.Message, responseTo *common.Message, profilePicturesVisibility int) error {
|
2021-02-23 07:37:08 +00:00
|
|
|
if !m.New {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
pubKey, err := m.GetSenderPubKey()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
contactID := contactIDFromPublicKey(pubKey)
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := r.AllChats.Load(m.LocalChatID)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("chat ID '%s' not present", m.LocalChatID)
|
|
|
|
}
|
|
|
|
|
|
|
|
contact, ok := r.AllContacts.Load(contactID)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("contact ID '%s' not present", contactID)
|
|
|
|
}
|
2021-02-22 16:12:59 +00:00
|
|
|
|
2022-02-24 21:36:09 +00:00
|
|
|
if !chat.Muted {
|
|
|
|
if showMessageNotification(publicKey, m, chat, responseTo) {
|
|
|
|
notification, err := NewMessageNotification(m.ID, m, chat, contact, r.AllContacts, profilePicturesVisibility)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.Response.AddNotification(notification)
|
2021-03-31 16:23:45 +00:00
|
|
|
}
|
2021-02-22 16:12:59 +00:00
|
|
|
}
|
2021-02-23 07:37:08 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-05-29 17:05:25 +00:00
|
|
|
// addNewActivityCenterNotification takes a common.Message and generates a new ActivityCenterNotification and appends it to the
|
|
|
|
// []Response.ActivityCenterNotifications if the message is m.New
|
|
|
|
func (r *ReceivedMessageState) addNewActivityCenterNotification(publicKey ecdsa.PublicKey, m *Messenger, message *common.Message, responseTo *common.Message) error {
|
|
|
|
if !message.New {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
chat, ok := r.AllChats.Load(message.LocalChatID)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("chat ID '%s' not present", message.LocalChatID)
|
|
|
|
}
|
|
|
|
|
2021-06-11 20:14:05 +00:00
|
|
|
isNotification, notificationType := showMentionOrReplyActivityCenterNotification(publicKey, message, chat, responseTo)
|
|
|
|
if isNotification {
|
2021-05-29 17:05:25 +00:00
|
|
|
notification := &ActivityCenterNotification{
|
2021-07-15 20:21:44 +00:00
|
|
|
ID: types.FromHex(message.ID),
|
|
|
|
Name: chat.Name,
|
|
|
|
Message: message,
|
|
|
|
ReplyMessage: responseTo,
|
|
|
|
Type: notificationType,
|
|
|
|
Timestamp: message.WhisperTimestamp,
|
|
|
|
ChatID: chat.ID,
|
2022-10-25 22:06:20 +00:00
|
|
|
CommunityID: message.CommunityID,
|
2021-07-15 20:21:44 +00:00
|
|
|
Author: message.From,
|
2021-05-29 17:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := m.persistence.SaveActivityCenterNotification(notification)
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Warn("failed to save notification", zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.Response.AddActivityCenterNotification(notification)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-18 16:31:34 +00:00
|
|
|
func (m *Messenger) buildMessageState() *ReceivedMessageState {
|
|
|
|
return &ReceivedMessageState{
|
2020-01-10 18:59:01 +00:00
|
|
|
AllChats: m.allChats,
|
|
|
|
AllContacts: m.allContacts,
|
2021-03-29 15:41:30 +00:00
|
|
|
ModifiedContacts: new(stringBoolMap),
|
2020-01-10 18:59:01 +00:00
|
|
|
AllInstallations: m.allInstallations,
|
|
|
|
ModifiedInstallations: m.modifiedInstallations,
|
|
|
|
ExistingMessagesMap: make(map[string]bool),
|
2020-07-27 12:27:48 +00:00
|
|
|
EmojiReactions: make(map[string]*EmojiReaction),
|
2020-08-07 13:49:37 +00:00
|
|
|
GroupChatInvitations: make(map[string]*GroupChatInvitation),
|
2022-01-18 16:31:34 +00:00
|
|
|
Response: &MessengerResponse{},
|
2020-01-20 16:44:32 +00:00
|
|
|
Timesource: m.getTimesource(),
|
2022-01-17 03:42:11 +00:00
|
|
|
AllBookmarks: make(map[string]*browsers.Bookmark),
|
2022-07-05 19:49:44 +00:00
|
|
|
AllTrustStatus: make(map[string]verification.TrustStatus),
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2022-01-18 16:31:34 +00:00
|
|
|
}
|
|
|
|
|
2022-08-24 12:06:48 +00:00
|
|
|
func (m *Messenger) outputToCSV(timestamp uint32, messageID types.HexBytes, from string, topic types.TopicType, chatID string, msgType protobuf.ApplicationMetadataMessage_Type, parsedMessage interface{}) {
|
|
|
|
if !m.outputCSV {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
msgJSON, err := json.Marshal(parsedMessage)
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("could not marshall message", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
line := fmt.Sprintf("%d\t%s\t%s\t%s\t%s\t%s\t%s\n", timestamp, messageID.String(), from, topic.String(), chatID, msgType, msgJSON)
|
|
|
|
_, err = m.csvFile.Write([]byte(line))
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("could not write to csv", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-29 11:50:23 +00:00
|
|
|
func (m *Messenger) handleImportedMessages(messagesToHandle map[transport.Filter][]*types.Message) error {
|
|
|
|
|
|
|
|
messageState := m.buildMessageState()
|
|
|
|
|
|
|
|
logger := m.logger.With(zap.String("site", "handleImportedMessages"))
|
|
|
|
|
|
|
|
for filter, messages := range messagesToHandle {
|
|
|
|
for _, shhMessage := range messages {
|
|
|
|
|
|
|
|
statusMessages, _, err := m.sender.HandleMessages(shhMessage, true)
|
|
|
|
if err != nil {
|
|
|
|
logger.Info("failed to decode messages", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, msg := range statusMessages {
|
|
|
|
logger := logger.With(zap.String("message-id", msg.ID.String()))
|
|
|
|
logger.Debug("processing message")
|
|
|
|
|
|
|
|
publicKey := msg.SigPubKey()
|
|
|
|
senderID := contactIDFromPublicKey(publicKey)
|
|
|
|
|
|
|
|
// Don't process duplicates
|
|
|
|
messageID := msg.TransportMessage.ThirdPartyID
|
|
|
|
exists, err := m.messageExists(messageID, messageState.ExistingMessagesMap)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to check message exists", zap.Error(err))
|
|
|
|
}
|
|
|
|
if exists {
|
|
|
|
logger.Debug("messageExists", zap.String("messageID", messageID))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var contact *Contact
|
|
|
|
if c, ok := messageState.AllContacts.Load(senderID); ok {
|
|
|
|
contact = c
|
|
|
|
} else {
|
|
|
|
c, err := buildContact(senderID, publicKey)
|
|
|
|
if err != nil {
|
|
|
|
logger.Info("failed to build contact", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
contact = c
|
|
|
|
messageState.AllContacts.Store(senderID, contact)
|
|
|
|
}
|
|
|
|
messageState.CurrentMessageState = &CurrentMessageState{
|
|
|
|
MessageID: messageID,
|
|
|
|
WhisperTimestamp: uint64(msg.TransportMessage.Timestamp) * 1000,
|
|
|
|
Contact: contact,
|
|
|
|
PublicKey: publicKey,
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg.ParsedMessage != nil {
|
|
|
|
|
|
|
|
logger.Debug("Handling parsed message")
|
|
|
|
|
|
|
|
switch msg.ParsedMessage.Interface().(type) {
|
|
|
|
|
|
|
|
case protobuf.ChatMessage:
|
|
|
|
logger.Debug("Handling ChatMessage")
|
|
|
|
messageState.CurrentMessageState.Message = msg.ParsedMessage.Interface().(protobuf.ChatMessage)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, messageState.CurrentMessageState.Message)
|
|
|
|
err = m.HandleChatMessage(messageState)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle ChatMessage", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
importMessageAuthors := messageState.Response.DiscordMessageAuthors()
|
|
|
|
if len(importMessageAuthors) > 0 {
|
|
|
|
err := m.persistence.SaveDiscordMessageAuthors(importMessageAuthors)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
importMessagesToSave := messageState.Response.DiscordMessages()
|
|
|
|
importMessagesCount := len(importMessagesToSave)
|
|
|
|
if importMessagesCount > 0 {
|
|
|
|
if importMessagesCount <= maxChunkSizeMessages {
|
|
|
|
err := m.persistence.SaveDiscordMessages(importMessagesToSave)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We need to process the messages in chunks otherwise we'll
|
|
|
|
// block the database for too long
|
|
|
|
chunks := chunkSlice(importMessagesToSave, maxChunkSizeMessages)
|
|
|
|
chunksCount := len(chunks)
|
|
|
|
for i, msgs := range chunks {
|
|
|
|
// We can't defer Unlock here because we want to
|
|
|
|
// unlock after every iteration to leave room for
|
|
|
|
// other processes to access the database
|
|
|
|
m.handleMessagesMutex.Lock()
|
|
|
|
err := m.persistence.SaveDiscordMessages(msgs)
|
|
|
|
if err != nil {
|
|
|
|
m.handleMessagesMutex.Unlock()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
m.handleMessagesMutex.Unlock()
|
|
|
|
// We slow down the saving of message chunks to keep the database responsive
|
|
|
|
if i < chunksCount-1 {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
messageAttachmentsToSave := messageState.Response.DiscordMessageAttachments()
|
|
|
|
if len(messageAttachmentsToSave) > 0 {
|
|
|
|
chunks := chunkAttachmentsByByteSize(messageAttachmentsToSave, maxChunkSizeBytes)
|
|
|
|
chunksCount := len(chunks)
|
|
|
|
for i, attachments := range chunks {
|
|
|
|
m.handleMessagesMutex.Lock()
|
|
|
|
err := m.persistence.SaveDiscordMessageAttachments(attachments)
|
|
|
|
if err != nil {
|
|
|
|
m.handleMessagesMutex.Unlock()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// We slow down the saving of message chunks to keep the database responsive
|
|
|
|
m.handleMessagesMutex.Unlock()
|
|
|
|
if i < chunksCount-1 {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
messagesToSave := messageState.Response.Messages()
|
|
|
|
messagesCount := len(messagesToSave)
|
|
|
|
if messagesCount > 0 {
|
|
|
|
if messagesCount <= maxChunkSizeMessages {
|
|
|
|
err := m.SaveMessages(messagesToSave)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
chunks := chunkSlice(messagesToSave, maxChunkSizeMessages)
|
|
|
|
chunksCount := len(chunks)
|
|
|
|
for i, msgs := range chunks {
|
|
|
|
m.handleMessagesMutex.Lock()
|
|
|
|
err := m.SaveMessages(msgs)
|
|
|
|
if err != nil {
|
|
|
|
m.handleMessagesMutex.Unlock()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
m.handleMessagesMutex.Unlock()
|
|
|
|
// We slow down the saving of message chunks to keep the database responsive
|
|
|
|
if i < chunksCount-1 {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-18 16:31:34 +00:00
|
|
|
func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message, storeWakuMessages bool) (*MessengerResponse, error) {
|
|
|
|
|
|
|
|
m.handleMessagesMutex.Lock()
|
|
|
|
defer m.handleMessagesMutex.Unlock()
|
|
|
|
|
|
|
|
messageState := m.buildMessageState()
|
|
|
|
response := messageState.Response
|
2019-12-02 15:34:05 +00:00
|
|
|
|
|
|
|
logger := m.logger.With(zap.String("site", "RetrieveAll"))
|
2021-02-23 15:47:45 +00:00
|
|
|
|
2022-03-09 09:58:05 +00:00
|
|
|
adminCommunitiesChatIDs, err := m.communitiesManager.GetAdminCommunitiesChatIDs()
|
|
|
|
if err != nil {
|
|
|
|
logger.Info("failed to retrieve admin communities", zap.Error(err))
|
|
|
|
}
|
|
|
|
|
2021-11-03 12:38:37 +00:00
|
|
|
for filter, messages := range chatWithMessages {
|
2021-01-08 15:21:25 +00:00
|
|
|
var processedMessages []string
|
2019-10-09 14:22:53 +00:00
|
|
|
for _, shhMessage := range messages {
|
2021-10-29 14:29:28 +00:00
|
|
|
logger := logger.With(zap.String("hash", types.EncodeHex(shhMessage.Hash)))
|
2021-01-08 15:21:25 +00:00
|
|
|
// Indicates tha all messages in the batch have been processed correctly
|
|
|
|
allMessagesProcessed := true
|
2022-03-09 09:58:05 +00:00
|
|
|
|
2022-04-22 07:42:22 +00:00
|
|
|
if adminCommunitiesChatIDs[filter.ChatID] && storeWakuMessages {
|
2022-03-09 09:58:05 +00:00
|
|
|
logger.Debug("storing waku message")
|
|
|
|
err := m.communitiesManager.StoreWakuMessage(shhMessage)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to store waku message", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 14:13:48 +00:00
|
|
|
statusMessages, acks, err := m.sender.HandleMessages(shhMessage, true)
|
2019-09-02 09:29:06 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Info("failed to decode messages", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2021-11-03 12:38:37 +00:00
|
|
|
|
|
|
|
if m.telemetryClient != nil {
|
|
|
|
go m.telemetryClient.PushReceivedMessages(filter, shhMessage, statusMessages)
|
|
|
|
}
|
2021-02-23 15:47:45 +00:00
|
|
|
m.markDeliveredMessages(acks)
|
2019-09-26 07:01:17 +00:00
|
|
|
|
2020-03-09 06:19:23 +00:00
|
|
|
logger.Debug("processing messages further", zap.Int("count", len(statusMessages)))
|
|
|
|
|
2019-11-15 08:52:28 +00:00
|
|
|
for _, msg := range statusMessages {
|
2021-10-29 14:29:28 +00:00
|
|
|
logger := logger.With(zap.String("message-id", msg.ID.String()))
|
2022-11-07 14:57:57 +00:00
|
|
|
logger.Info("processing message")
|
2019-12-02 15:34:05 +00:00
|
|
|
publicKey := msg.SigPubKey()
|
|
|
|
|
2020-07-31 12:22:05 +00:00
|
|
|
m.handleInstallations(msg.Installations)
|
|
|
|
err := m.handleSharedSecrets(msg.SharedSecrets)
|
|
|
|
if err != nil {
|
|
|
|
// log and continue, non-critical error
|
|
|
|
logger.Warn("failed to handle shared secrets")
|
|
|
|
}
|
|
|
|
|
2020-01-15 07:25:09 +00:00
|
|
|
senderID := contactIDFromPublicKey(publicKey)
|
2022-08-24 12:06:48 +00:00
|
|
|
|
|
|
|
// Check for messages from blocked users
|
2021-10-01 14:50:16 +00:00
|
|
|
if contact, ok := messageState.AllContacts.Load(senderID); ok && contact.Blocked {
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
continue
|
|
|
|
}
|
2021-02-23 15:47:45 +00:00
|
|
|
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
// Don't process duplicates
|
2019-12-02 15:34:05 +00:00
|
|
|
messageID := types.EncodeHex(msg.ID)
|
2021-06-07 12:38:13 +00:00
|
|
|
exists, err := m.messageExists(messageID, messageState.ExistingMessagesMap)
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to check message exists", zap.Error(err))
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
}
|
2019-12-02 15:34:05 +00:00
|
|
|
if exists {
|
2020-08-18 15:07:48 +00:00
|
|
|
logger.Debug("messageExists", zap.String("messageID", messageID))
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var contact *Contact
|
2021-03-29 15:41:30 +00:00
|
|
|
if c, ok := messageState.AllContacts.Load(senderID); ok {
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
contact = c
|
|
|
|
} else {
|
2020-12-22 10:49:25 +00:00
|
|
|
c, err := buildContact(senderID, publicKey)
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Info("failed to build contact", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
contact = c
|
2021-03-29 15:41:30 +00:00
|
|
|
messageState.AllContacts.Store(senderID, contact)
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
messageState.CurrentMessageState = &CurrentMessageState{
|
2019-12-02 15:34:05 +00:00
|
|
|
MessageID: messageID,
|
|
|
|
WhisperTimestamp: uint64(msg.TransportMessage.Timestamp) * 1000,
|
|
|
|
Contact: contact,
|
|
|
|
PublicKey: publicKey,
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
}
|
|
|
|
|
2019-11-15 08:52:28 +00:00
|
|
|
if msg.ParsedMessage != nil {
|
2021-02-23 15:47:45 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling parsed message")
|
2022-08-24 12:06:48 +00:00
|
|
|
|
2020-07-27 10:13:22 +00:00
|
|
|
switch msg.ParsedMessage.Interface().(type) {
|
2019-12-02 15:34:05 +00:00
|
|
|
case protobuf.MembershipUpdateMessage:
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling MembershipUpdateMessage")
|
2020-07-27 10:13:22 +00:00
|
|
|
rawMembershipUpdate := msg.ParsedMessage.Interface().(protobuf.MembershipUpdateMessage)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, rawMembershipUpdate)
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, _ := messageState.AllChats.Load(rawMembershipUpdate.ChatId)
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleMembershipUpdate(messageState, chat, rawMembershipUpdate, m.systemMessagesTranslations)
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
if err != nil {
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Warn("failed to handle MembershipUpdate", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.ChatMessage:
|
|
|
|
logger.Debug("Handling ChatMessage")
|
2020-07-27 10:13:22 +00:00
|
|
|
messageState.CurrentMessageState.Message = msg.ParsedMessage.Interface().(protobuf.ChatMessage)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, messageState.CurrentMessageState.Message)
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleChatMessage(messageState)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle ChatMessage", zap.Error(err))
|
2021-06-07 11:45:06 +00:00
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.EditMessage:
|
|
|
|
logger.Debug("Handling EditMessage")
|
|
|
|
editProto := msg.ParsedMessage.Interface().(protobuf.EditMessage)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, editProto)
|
2021-06-07 11:45:06 +00:00
|
|
|
editMessage := EditMessage{
|
|
|
|
EditMessage: editProto,
|
|
|
|
From: contact.ID,
|
|
|
|
ID: messageID,
|
|
|
|
SigPubKey: publicKey,
|
|
|
|
}
|
|
|
|
err = m.HandleEditMessage(response, editMessage)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle EditMessage", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-25 11:46:43 +00:00
|
|
|
|
2021-07-26 21:06:32 +00:00
|
|
|
case protobuf.DeleteMessage:
|
|
|
|
logger.Debug("Handling DeleteMessage")
|
|
|
|
deleteProto := msg.ParsedMessage.Interface().(protobuf.DeleteMessage)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, deleteProto)
|
2021-07-26 21:06:32 +00:00
|
|
|
deleteMessage := DeleteMessage{
|
|
|
|
DeleteMessage: deleteProto,
|
|
|
|
From: contact.ID,
|
|
|
|
ID: messageID,
|
|
|
|
SigPubKey: publicKey,
|
|
|
|
}
|
|
|
|
|
2021-08-19 13:16:45 +00:00
|
|
|
err = m.HandleDeleteMessage(messageState, deleteMessage)
|
2021-07-26 21:06:32 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle DeleteMessage", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-09-28 11:42:17 +00:00
|
|
|
case protobuf.DeleteForMeMessage:
|
|
|
|
logger.Debug("Handling DeleteForMeMessage")
|
|
|
|
deleteForMeProto := msg.ParsedMessage.Interface().(protobuf.DeleteForMeMessage)
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, deleteForMeProto)
|
|
|
|
deleteForMeMessage := DeleteForMeMessage{
|
|
|
|
DeleteForMeMessage: deleteForMeProto,
|
|
|
|
From: contact.ID,
|
|
|
|
ID: messageID,
|
|
|
|
SigPubKey: publicKey,
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.HandleDeleteForMeMessage(messageState, deleteForMeMessage)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle DeleteForMeMessage", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-05-14 21:22:50 +00:00
|
|
|
case protobuf.PinMessage:
|
|
|
|
pinMessage := msg.ParsedMessage.Interface().(protobuf.PinMessage)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, pinMessage)
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandlePinMessage(messageState, pinMessage)
|
2021-05-14 21:22:50 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle PinMessage", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.PairInstallation:
|
2020-07-06 08:54:22 +00:00
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
2020-07-27 10:13:22 +00:00
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.PairInstallation)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling PairInstallation", zap.Any("message", p))
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandlePairInstallation(messageState, p)
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Warn("failed to handle PairInstallation", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-07-22 17:41:49 +00:00
|
|
|
case protobuf.StatusUpdate:
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.StatusUpdate)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2021-07-22 17:41:49 +00:00
|
|
|
logger.Debug("Handling StatusUpdate", zap.Any("message", p))
|
|
|
|
err = m.HandleStatusUpdate(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle StatusMessage", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.SyncInstallationContact:
|
2021-09-03 08:26:05 +00:00
|
|
|
logger.Warn("SyncInstallationContact is not supported")
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, msg.ParsedMessage.Interface().(protobuf.SyncInstallationContact))
|
2021-09-03 08:26:05 +00:00
|
|
|
continue
|
|
|
|
|
|
|
|
case protobuf.SyncInstallationContactV2:
|
2020-07-06 08:54:22 +00:00
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
}
|
|
|
|
|
2021-09-03 08:26:05 +00:00
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationContactV2)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling SyncInstallationContact", zap.Any("message", p))
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleSyncInstallationContact(messageState, p)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncInstallationContact", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-25 11:46:43 +00:00
|
|
|
|
2022-03-24 09:35:56 +00:00
|
|
|
case protobuf.SyncProfilePictures:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncProfilePictures)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2022-03-24 09:35:56 +00:00
|
|
|
logger.Debug("Handling SyncProfilePicture", zap.Any("message", p))
|
|
|
|
err = m.HandleSyncProfilePictures(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncProfilePicture", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
case protobuf.SyncBookmark:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncBookmark)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2022-01-17 03:42:11 +00:00
|
|
|
logger.Debug("Handling SyncBookmark", zap.Any("message", p))
|
|
|
|
err = m.handleSyncBookmark(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncBookmark", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-02-10 10:00:59 +00:00
|
|
|
case protobuf.SyncClearHistory:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncClearHistory)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2022-02-10 10:00:59 +00:00
|
|
|
logger.Debug("Handling SyncClearHistory", zap.Any("message", p))
|
|
|
|
err = m.handleSyncClearHistory(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncClearHistory", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
2022-06-01 07:55:48 +00:00
|
|
|
case protobuf.SyncCommunitySettings:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncCommunitySettings)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2022-06-01 07:55:48 +00:00
|
|
|
logger.Debug("Handling SyncCommunitySettings", zap.Any("message", p))
|
|
|
|
err = m.handleSyncCommunitySettings(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncCommunitySettings", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
2022-07-05 19:49:44 +00:00
|
|
|
|
|
|
|
case protobuf.SyncTrustedUser:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncTrustedUser)
|
|
|
|
logger.Debug("Handling SyncTrustedUser", zap.Any("message", p))
|
|
|
|
err = m.handleSyncTrustedUser(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncTrustedUser", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.SyncVerificationRequest:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncVerificationRequest)
|
|
|
|
logger.Debug("Handling SyncVerificationRequest", zap.Any("message", p))
|
|
|
|
err = m.handleSyncVerificationRequest(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncClearHistory", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-10-11 15:39:52 +00:00
|
|
|
case protobuf.Backup:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.Backup)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2021-10-11 15:39:52 +00:00
|
|
|
logger.Debug("Handling Backup", zap.Any("message", p))
|
|
|
|
err = m.HandleBackup(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle Backup", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-15 07:25:09 +00:00
|
|
|
case protobuf.SyncInstallationPublicChat:
|
2020-07-06 08:54:22 +00:00
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
2020-01-15 07:25:09 +00:00
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-07-27 10:13:22 +00:00
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationPublicChat)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2020-01-15 07:25:09 +00:00
|
|
|
logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p))
|
2021-06-07 12:38:13 +00:00
|
|
|
addedChat := m.HandleSyncInstallationPublicChat(messageState, p)
|
2020-07-25 11:46:43 +00:00
|
|
|
|
2021-05-14 10:55:42 +00:00
|
|
|
// We join and re-register as we want to receive mentions from the newly joined public chat
|
|
|
|
if addedChat != nil {
|
2021-09-23 12:56:38 +00:00
|
|
|
_, err = m.createPublicChat(addedChat.ID, messageState.Response)
|
2021-05-14 10:55:42 +00:00
|
|
|
if err != nil {
|
|
|
|
allMessagesProcessed = false
|
|
|
|
logger.Error("error joining chat", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2020-09-03 07:19:46 +00:00
|
|
|
}
|
|
|
|
|
2021-10-05 17:26:02 +00:00
|
|
|
case protobuf.SyncChatRemoved:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncChatRemoved)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2021-10-05 17:26:02 +00:00
|
|
|
logger.Debug("Handling SyncChatRemoved", zap.Any("message", p))
|
|
|
|
err := m.HandleSyncChatRemoved(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle sync removing chat", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-10-12 10:33:32 +00:00
|
|
|
case protobuf.SyncChatMessagesRead:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncChatMessagesRead)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2021-10-12 10:33:32 +00:00
|
|
|
logger.Debug("Handling SyncChatMessagesRead", zap.Any("message", p))
|
|
|
|
err := m.HandleSyncChatMessagesRead(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle sync removing chat", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-08-06 15:40:23 +00:00
|
|
|
case protobuf.SyncCommunity:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
community := msg.ParsedMessage.Interface().(protobuf.SyncCommunity)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, community)
|
2021-08-06 15:40:23 +00:00
|
|
|
logger.Debug("Handling SyncCommunity", zap.Any("message", community))
|
|
|
|
|
|
|
|
err = m.handleSyncCommunity(messageState, community)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncCommunity", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
2021-12-02 14:23:02 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.SyncActivityCenterRead:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
a := msg.ParsedMessage.Interface().(protobuf.SyncActivityCenterRead)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, a)
|
2021-12-02 14:23:02 +00:00
|
|
|
logger.Debug("Handling SyncActivityCenterRead", zap.Any("message", a))
|
|
|
|
|
|
|
|
err = m.handleActivityCenterRead(messageState, a)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncActivityCenterRead", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.SyncActivityCenterAccepted:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
a := msg.ParsedMessage.Interface().(protobuf.SyncActivityCenterAccepted)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, a)
|
2021-12-02 14:23:02 +00:00
|
|
|
logger.Debug("Handling SyncActivityCenterAccepted", zap.Any("message", a))
|
|
|
|
|
|
|
|
err = m.handleActivityCenterAccepted(messageState, a)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncActivityCenterAccepted", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.SyncActivityCenterDismissed:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
a := msg.ParsedMessage.Interface().(protobuf.SyncActivityCenterDismissed)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, a)
|
2021-12-02 14:23:02 +00:00
|
|
|
logger.Debug("Handling SyncActivityCenterDismissed", zap.Any("message", a))
|
|
|
|
|
|
|
|
err = m.handleActivityCenterDismissed(messageState, a)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncActivityCenterDismissed", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
2021-08-06 15:40:23 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-03-23 18:47:00 +00:00
|
|
|
case protobuf.SyncSetting:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
ss := msg.ParsedMessage.Interface().(protobuf.SyncSetting)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, ss)
|
2022-03-23 18:47:00 +00:00
|
|
|
logger.Debug("Handling SyncSetting", zap.Any("message", ss))
|
|
|
|
|
|
|
|
err := m.handleSyncSetting(messageState.Response, &ss)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncSetting", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.RequestAddressForTransaction:
|
2020-07-27 10:13:22 +00:00
|
|
|
command := msg.ParsedMessage.Interface().(protobuf.RequestAddressForTransaction)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling RequestAddressForTransaction", zap.Any("message", command))
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleRequestAddressForTransaction(messageState, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle RequestAddressForTransaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-25 11:46:43 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.SendTransaction:
|
2020-07-27 10:13:22 +00:00
|
|
|
command := msg.ParsedMessage.Interface().(protobuf.SendTransaction)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling SendTransaction", zap.Any("message", command))
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleSendTransaction(messageState, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SendTransaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-25 11:46:43 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.AcceptRequestAddressForTransaction:
|
2020-07-27 10:13:22 +00:00
|
|
|
command := msg.ParsedMessage.Interface().(protobuf.AcceptRequestAddressForTransaction)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling AcceptRequestAddressForTransaction")
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleAcceptRequestAddressForTransaction(messageState, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle AcceptRequestAddressForTransaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
|
|
|
}
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.DeclineRequestAddressForTransaction:
|
2020-07-27 10:13:22 +00:00
|
|
|
command := msg.ParsedMessage.Interface().(protobuf.DeclineRequestAddressForTransaction)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling DeclineRequestAddressForTransaction")
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleDeclineRequestAddressForTransaction(messageState, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle DeclineRequestAddressForTransaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.DeclineRequestTransaction:
|
2020-07-27 10:13:22 +00:00
|
|
|
command := msg.ParsedMessage.Interface().(protobuf.DeclineRequestTransaction)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling DeclineRequestTransaction")
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleDeclineRequestTransaction(messageState, command)
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Warn("failed to handle DeclineRequestTransaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2019-12-02 15:34:05 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
case protobuf.RequestTransaction:
|
2020-07-27 10:13:22 +00:00
|
|
|
command := msg.ParsedMessage.Interface().(protobuf.RequestTransaction)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
logger.Debug("Handling RequestTransaction")
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleRequestTransaction(messageState, command)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle RequestTransaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-25 11:46:43 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
case protobuf.ContactUpdate:
|
2021-09-03 08:26:05 +00:00
|
|
|
if common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-07-27 10:13:22 +00:00
|
|
|
contactUpdate := msg.ParsedMessage.Interface().(protobuf.ContactUpdate)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, contactUpdate)
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleContactUpdate(messageState, contactUpdate)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle ContactUpdate", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-01-10 18:59:01 +00:00
|
|
|
continue
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2022-01-18 16:31:34 +00:00
|
|
|
case protobuf.AcceptContactRequest:
|
|
|
|
logger.Debug("Handling AcceptContactRequest")
|
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.AcceptContactRequest)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
2022-01-18 16:31:34 +00:00
|
|
|
err = m.HandleAcceptContactRequest(messageState, message)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle AcceptContactRequest", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
case protobuf.RetractContactRequest:
|
|
|
|
|
|
|
|
logger.Debug("Handling RetractContactRequest")
|
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.RetractContactRequest)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
2022-01-18 16:31:34 +00:00
|
|
|
err = m.HandleRetractContactRequest(messageState, message)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle RetractContactRequest", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-07-06 08:54:22 +00:00
|
|
|
case protobuf.PushNotificationQuery:
|
|
|
|
logger.Debug("Received PushNotificationQuery")
|
|
|
|
if m.pushNotificationServer == nil {
|
|
|
|
continue
|
|
|
|
}
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.PushNotificationQuery)
|
2020-07-06 08:54:22 +00:00
|
|
|
logger.Debug("Handling PushNotificationQuery")
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
if err := m.pushNotificationServer.HandlePushNotificationQuery(publicKey, msg.ID, message); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-06 08:54:22 +00:00
|
|
|
logger.Warn("failed to handle PushNotificationQuery", zap.Error(err))
|
|
|
|
}
|
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
2020-07-09 16:52:26 +00:00
|
|
|
case protobuf.PushNotificationRegistrationResponse:
|
|
|
|
logger.Debug("Received PushNotificationRegistrationResponse")
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logger.Debug("Handling PushNotificationRegistrationResponse")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.PushNotificationRegistrationResponse)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(publicKey, message); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-09 16:52:26 +00:00
|
|
|
logger.Warn("failed to handle PushNotificationRegistrationResponse", zap.Error(err))
|
|
|
|
}
|
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
2020-07-31 13:46:27 +00:00
|
|
|
case protobuf.ContactCodeAdvertisement:
|
|
|
|
logger.Debug("Received ContactCodeAdvertisement")
|
2020-11-18 12:41:36 +00:00
|
|
|
|
|
|
|
cca := msg.ParsedMessage.Interface().(protobuf.ContactCodeAdvertisement)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, cca)
|
2021-02-17 23:14:48 +00:00
|
|
|
logger.Debug("protobuf.ContactCodeAdvertisement received", zap.Any("cca", cca))
|
2020-11-18 12:41:36 +00:00
|
|
|
if cca.ChatIdentity != nil {
|
|
|
|
|
|
|
|
logger.Debug("Received ContactCodeAdvertisement ChatIdentity")
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleChatIdentity(messageState, *cca.ChatIdentity)
|
2020-11-18 12:41:36 +00:00
|
|
|
if err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-11-18 12:41:36 +00:00
|
|
|
logger.Warn("failed to handle ContactCodeAdvertisement ChatIdentity", zap.Error(err))
|
|
|
|
// No continue as Chat Identity may fail but the rest of the cca may process fine.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 13:46:27 +00:00
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-08-18 15:07:48 +00:00
|
|
|
logger.Debug("Handling ContactCodeAdvertisement")
|
2020-11-18 12:41:36 +00:00
|
|
|
if err := m.pushNotificationClient.HandleContactCodeAdvertisement(publicKey, cca); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-31 13:46:27 +00:00
|
|
|
logger.Warn("failed to handle ContactCodeAdvertisement", zap.Error(err))
|
|
|
|
}
|
2020-11-18 12:41:36 +00:00
|
|
|
|
2020-07-31 13:46:27 +00:00
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
|
|
|
|
2020-07-20 08:06:24 +00:00
|
|
|
case protobuf.PushNotificationResponse:
|
|
|
|
logger.Debug("Received PushNotificationResponse")
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logger.Debug("Handling PushNotificationResponse")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.PushNotificationResponse)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
if err := m.pushNotificationClient.HandlePushNotificationResponse(publicKey, message); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-20 08:06:24 +00:00
|
|
|
logger.Warn("failed to handle PushNotificationResponse", zap.Error(err))
|
|
|
|
}
|
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
2020-07-09 16:52:26 +00:00
|
|
|
|
|
|
|
case protobuf.PushNotificationQueryResponse:
|
|
|
|
logger.Debug("Received PushNotificationQueryResponse")
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logger.Debug("Handling PushNotificationQueryResponse")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.PushNotificationQueryResponse)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(publicKey, message); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-09 16:52:26 +00:00
|
|
|
logger.Warn("failed to handle PushNotificationQueryResponse", zap.Error(err))
|
|
|
|
}
|
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
|
|
|
|
2020-07-06 08:54:22 +00:00
|
|
|
case protobuf.PushNotificationRequest:
|
|
|
|
logger.Debug("Received PushNotificationRequest")
|
|
|
|
if m.pushNotificationServer == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logger.Debug("Handling PushNotificationRequest")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.PushNotificationRequest)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ID, message); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-06 08:54:22 +00:00
|
|
|
logger.Warn("failed to handle PushNotificationRequest", zap.Error(err))
|
|
|
|
}
|
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
2020-07-24 13:47:58 +00:00
|
|
|
case protobuf.EmojiReaction:
|
2020-07-25 11:46:43 +00:00
|
|
|
logger.Debug("Handling EmojiReaction")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.EmojiReaction)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
err = m.HandleEmojiReaction(messageState, message)
|
2020-07-24 13:47:58 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle EmojiReaction", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-24 13:47:58 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-08-07 13:49:37 +00:00
|
|
|
case protobuf.GroupChatInvitation:
|
|
|
|
logger.Debug("Handling GroupChatInvitation")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.GroupChatInvitation)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
err = m.HandleGroupChatInvitation(messageState, message)
|
2020-08-07 13:49:37 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle GroupChatInvitation", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-08-07 13:49:37 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-11-18 12:41:36 +00:00
|
|
|
case protobuf.ChatIdentity:
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.ChatIdentity)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
err = m.HandleChatIdentity(messageState, message)
|
2020-11-18 12:41:36 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle ChatIdentity", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-11-18 12:41:36 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-07-06 08:54:22 +00:00
|
|
|
|
2020-11-18 09:16:51 +00:00
|
|
|
case protobuf.CommunityDescription:
|
|
|
|
logger.Debug("Handling CommunityDescription")
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.CommunityDescription)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
err = m.handleCommunityDescription(messageState, publicKey, message, msg.DecryptedPayload)
|
2020-11-18 09:16:51 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle CommunityDescription", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-11-18 09:16:51 +00:00
|
|
|
continue
|
|
|
|
}
|
2021-04-19 12:09:46 +00:00
|
|
|
|
|
|
|
//if community was among requested ones, send its info and remove filter
|
|
|
|
for communityID := range m.requestedCommunities {
|
|
|
|
if _, ok := messageState.Response.communities[communityID]; ok {
|
|
|
|
m.passStoredCommunityInfoToSignalHandler(communityID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:49:44 +00:00
|
|
|
case protobuf.RequestContactVerification:
|
|
|
|
logger.Debug("Handling RequestContactVerification")
|
|
|
|
err = m.HandleRequestContactVerification(messageState, msg.ParsedMessage.Interface().(protobuf.RequestContactVerification))
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle RequestContactVerification", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.AcceptContactVerification:
|
|
|
|
logger.Debug("Handling AcceptContactVerification")
|
|
|
|
err = m.HandleAcceptContactVerification(messageState, msg.ParsedMessage.Interface().(protobuf.AcceptContactVerification))
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle AcceptContactVerification", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.DeclineContactVerification:
|
|
|
|
logger.Debug("Handling DeclineContactVerification")
|
|
|
|
err = m.HandleDeclineContactVerification(messageState, msg.ParsedMessage.Interface().(protobuf.DeclineContactVerification))
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle DeclineContactVerification", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
case protobuf.ContactVerificationTrusted:
|
|
|
|
logger.Debug("Handling ContactVerificationTrusted")
|
|
|
|
err = m.HandleContactVerificationTrusted(messageState, msg.ParsedMessage.Interface().(protobuf.ContactVerificationTrusted))
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle ContactVerificationTrusted", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-11-18 09:16:51 +00:00
|
|
|
case protobuf.CommunityInvitation:
|
|
|
|
logger.Debug("Handling CommunityInvitation")
|
|
|
|
invitation := msg.ParsedMessage.Interface().(protobuf.CommunityInvitation)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, invitation)
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleCommunityInvitation(messageState, publicKey, invitation, invitation.CommunityDescription)
|
2020-11-18 09:16:51 +00:00
|
|
|
if err != nil {
|
2021-01-11 10:32:51 +00:00
|
|
|
logger.Warn("failed to handle CommunityInvitation", zap.Error(err))
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-11-18 09:16:51 +00:00
|
|
|
continue
|
|
|
|
}
|
2021-01-11 10:32:51 +00:00
|
|
|
case protobuf.CommunityRequestToJoin:
|
|
|
|
logger.Debug("Handling CommunityRequestToJoin")
|
|
|
|
request := msg.ParsedMessage.Interface().(protobuf.CommunityRequestToJoin)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, request)
|
2021-06-07 12:38:13 +00:00
|
|
|
err = m.HandleCommunityRequestToJoin(messageState, publicKey, request)
|
2021-01-11 10:32:51 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle CommunityRequestToJoin", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2022-10-28 08:41:20 +00:00
|
|
|
case protobuf.CommunityCancelRequestToJoin:
|
|
|
|
logger.Debug("Handling CommunityCancelRequestToJoin")
|
|
|
|
request := msg.ParsedMessage.Interface().(protobuf.CommunityCancelRequestToJoin)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, request)
|
|
|
|
err = m.HandleCommunityCancelRequestToJoin(messageState, publicKey, request)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle CommunityCancelRequestToJoin", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2022-07-01 13:54:02 +00:00
|
|
|
case protobuf.CommunityRequestToJoinResponse:
|
|
|
|
logger.Debug("Handling CommunityRequestToJoinResponse")
|
|
|
|
requestToJoinResponse := msg.ParsedMessage.Interface().(protobuf.CommunityRequestToJoinResponse)
|
|
|
|
err = m.HandleCommunityRequestToJoinResponse(messageState, publicKey, requestToJoinResponse)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle CommunityRequestToJoinResponse", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
2022-08-22 10:10:31 +00:00
|
|
|
case protobuf.CommunityRequestToLeave:
|
|
|
|
logger.Debug("Handling CommunityRequestToLeave")
|
|
|
|
request := msg.ParsedMessage.Interface().(protobuf.CommunityRequestToLeave)
|
|
|
|
err = m.HandleCommunityRequestToLeave(messageState, publicKey, request)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle CommunityRequestToLeave", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2021-01-11 10:32:51 +00:00
|
|
|
|
2022-04-22 07:42:22 +00:00
|
|
|
case protobuf.CommunityMessageArchiveMagnetlink:
|
|
|
|
logger.Debug("Handling CommunityMessageArchiveMagnetlink")
|
|
|
|
magnetlinkMessage := msg.ParsedMessage.Interface().(protobuf.CommunityMessageArchiveMagnetlink)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, magnetlinkMessage)
|
2022-04-22 07:42:22 +00:00
|
|
|
err = m.HandleHistoryArchiveMagnetlinkMessage(messageState, publicKey, magnetlinkMessage.MagnetUri, magnetlinkMessage.Clock)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle CommunityMessageArchiveMagnetlink", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2021-09-01 12:02:18 +00:00
|
|
|
case protobuf.AnonymousMetricBatch:
|
|
|
|
logger.Debug("Handling AnonymousMetricBatch")
|
|
|
|
if m.anonMetricsServer == nil {
|
|
|
|
logger.Warn("unable to handle AnonymousMetricBatch, anonMetricsServer is nil")
|
|
|
|
continue
|
|
|
|
}
|
2022-08-24 12:06:48 +00:00
|
|
|
message := msg.ParsedMessage.Interface().(protobuf.AnonymousMetricBatch)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, message)
|
|
|
|
ams, err := m.anonMetricsServer.StoreMetrics(message)
|
2021-09-01 12:02:18 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to store AnonymousMetricBatch", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
messageState.Response.AnonymousMetrics = append(messageState.Response.AnonymousMetrics, ams...)
|
|
|
|
|
2022-05-18 10:42:51 +00:00
|
|
|
case protobuf.SyncWalletAccounts:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncWalletAccounts)
|
2022-08-24 12:06:48 +00:00
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
2022-05-18 10:42:51 +00:00
|
|
|
logger.Debug("Handling SyncWalletAccount", zap.Any("message", p))
|
|
|
|
err = m.HandleSyncWalletAccount(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncWalletAccount", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
2022-08-07 14:25:03 +00:00
|
|
|
case protobuf.SyncContactRequestDecision:
|
|
|
|
logger.Info("SyncContactRequestDecision")
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncContactRequestDecision)
|
|
|
|
err := m.HandleSyncContactRequestDecision(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncContactRequestDecisio", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2022-09-14 10:46:11 +00:00
|
|
|
case protobuf.SyncSavedAddress:
|
|
|
|
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
|
|
|
logger.Warn("not coming from us, ignoring")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
p := msg.ParsedMessage.Interface().(protobuf.SyncSavedAddress)
|
|
|
|
m.outputToCSV(msg.TransportMessage.Timestamp, msg.ID, senderID, filter.Topic, filter.ChatID, msg.Type, p)
|
|
|
|
err = m.handleSyncSavedAddress(messageState, p)
|
|
|
|
if err != nil {
|
|
|
|
logger.Warn("failed to handle SyncSavedAddress", zap.Error(err))
|
|
|
|
allMessagesProcessed = false
|
|
|
|
continue
|
|
|
|
}
|
2019-12-02 15:34:05 +00:00
|
|
|
default:
|
2020-07-07 13:55:24 +00:00
|
|
|
// Check if is an encrypted PushNotificationRegistration
|
|
|
|
if msg.Type == protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION {
|
|
|
|
logger.Debug("Received PushNotificationRegistration")
|
|
|
|
if m.pushNotificationServer == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
logger.Debug("Handling PushNotificationRegistration")
|
2020-07-27 10:13:22 +00:00
|
|
|
if err := m.pushNotificationServer.HandlePushNotificationRegistration(publicKey, msg.ParsedMessage.Interface().([]byte)); err != nil {
|
2021-01-08 15:21:25 +00:00
|
|
|
allMessagesProcessed = false
|
2020-07-07 13:55:24 +00:00
|
|
|
logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err))
|
|
|
|
}
|
|
|
|
// We continue in any case, no changes to messenger
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-07-27 10:13:22 +00:00
|
|
|
logger.Debug("message not handled", zap.Any("messageType", reflect.TypeOf(msg.ParsedMessage.Interface())))
|
2020-01-28 11:16:28 +00:00
|
|
|
|
2019-11-15 08:52:28 +00:00
|
|
|
}
|
2020-08-18 15:07:48 +00:00
|
|
|
} else {
|
|
|
|
logger.Debug("parsed message is nil")
|
2019-11-15 08:52:28 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-08 15:21:25 +00:00
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// Process any community changes
|
|
|
|
for _, changes := range messageState.Response.CommunityChanges {
|
|
|
|
if changes.ShouldMemberJoin {
|
2021-07-22 17:41:49 +00:00
|
|
|
response, err := m.joinCommunity(context.TODO(), changes.Community.ID())
|
2021-01-11 10:32:51 +00:00
|
|
|
if err != nil {
|
|
|
|
logger.Error("cannot join community", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := messageState.Response.Merge(response); err != nil {
|
|
|
|
logger.Error("cannot merge join community response", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if changes.ShouldMemberLeave {
|
|
|
|
response, err := m.leaveCommunity(changes.Community.ID())
|
|
|
|
if err != nil {
|
|
|
|
logger.Error("cannot join community", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := messageState.Response.Merge(response); err != nil {
|
|
|
|
logger.Error("cannot merge join community response", zap.Error(err))
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up as not used by clients currently
|
|
|
|
messageState.Response.CommunityChanges = nil
|
|
|
|
|
2022-01-13 10:06:01 +00:00
|
|
|
// NOTE: for now we confirm messages as processed regardless whether we
|
|
|
|
// actually processed them, this is because we need to differentiate
|
|
|
|
// from messages that we want to retry to process and messages that
|
|
|
|
// are never going to be processed
|
|
|
|
m.transport.MarkP2PMessageAsProcessed(gethcommon.BytesToHash(shhMessage.Hash))
|
|
|
|
|
2021-01-08 15:21:25 +00:00
|
|
|
if allMessagesProcessed {
|
|
|
|
processedMessages = append(processedMessages, types.EncodeHex(shhMessage.Hash))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(processedMessages) != 0 {
|
|
|
|
if err := m.transport.ConfirmMessagesProcessed(processedMessages, m.getTimesource().GetCurrentTime()); err != nil {
|
|
|
|
logger.Warn("failed to confirm processed messages", zap.Error(err))
|
|
|
|
}
|
2019-09-02 09:29:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 11:22:38 +00:00
|
|
|
var contactsToSave []*Contact
|
2021-03-29 15:41:30 +00:00
|
|
|
messageState.ModifiedContacts.Range(func(id string, value bool) (shouldContinue bool) {
|
|
|
|
contact, ok := messageState.AllContacts.Load(id)
|
|
|
|
if ok {
|
2020-04-17 11:22:38 +00:00
|
|
|
contactsToSave = append(contactsToSave, contact)
|
2021-03-24 08:04:03 +00:00
|
|
|
messageState.Response.Contacts = append(messageState.Response.Contacts, contact)
|
2020-04-17 11:22:38 +00:00
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
2019-12-02 15:34:05 +00:00
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
// Hydrate chat alias and identicon
|
|
|
|
for id := range messageState.Response.chats {
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, _ := messageState.AllChats.Load(id)
|
2021-11-05 15:11:10 +00:00
|
|
|
if chat == nil {
|
|
|
|
continue
|
|
|
|
}
|
2020-05-20 12:16:12 +00:00
|
|
|
if chat.OneToOne() {
|
2021-03-29 15:41:30 +00:00
|
|
|
contact, ok := m.allContacts.Load(chat.ID)
|
2020-05-20 12:16:12 +00:00
|
|
|
if ok {
|
|
|
|
chat.Alias = contact.Alias
|
|
|
|
chat.Identicon = contact.Identicon
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
messageState.Response.AddChat(chat)
|
2020-05-20 12:16:12 +00:00
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
messageState.ModifiedInstallations.Range(func(id string, value bool) (shouldContinue bool) {
|
|
|
|
installation, _ := messageState.AllInstallations.Load(id)
|
2020-01-10 18:59:01 +00:00
|
|
|
messageState.Response.Installations = append(messageState.Response.Installations, installation)
|
|
|
|
if installation.InstallationMetadata != nil {
|
2021-03-29 15:41:30 +00:00
|
|
|
err = m.setInstallationMetadata(id, installation.InstallationMetadata)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
2021-03-29 15:41:30 +00:00
|
|
|
return false
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2019-09-26 07:01:17 +00:00
|
|
|
}
|
|
|
|
|
2021-01-11 10:32:51 +00:00
|
|
|
if len(messageState.Response.chats) > 0 {
|
|
|
|
err = m.saveChats(messageState.Response.Chats())
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2021-01-11 10:32:51 +00:00
|
|
|
|
2021-05-27 17:24:04 +00:00
|
|
|
if len(messageState.Response.pinMessages) > 0 {
|
|
|
|
err = m.SavePinMessages(messageState.Response.PinMessages())
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
Move to protobuf for Message type (#1706)
* Use a single Message type `v1/message.go` and `message.go` are the same now, and they embed `protobuf.ChatMessage`
* Use `SendChatMessage` for sending chat messages, this is basically the old `Send` but a bit more flexible so we can send different message types (stickers,commands), and not just text.
* Remove dedup from services/shhext. Because now we process in status-protocol, dedup makes less sense, as those messages are going to be processed anyway, so removing for now, we can re-evaluate if bringing it to status-go or not.
* Change the various retrieveX method to a single one:
`RetrieveAll` will be processing those messages that it can process (Currently only `Message`), and return the rest in `RawMessages` (still transit). The format for the response is:
`Chats`: -> The chats updated by receiving the message
`Messages`: -> The messages retrieved (already matched to a chat)
`Contacts`: -> The contacts updated by the messages
`RawMessages` -> Anything else that can't be parsed, eventually as we move everything to status-protocol-go this will go away.
2019-12-05 16:25:34 +00:00
|
|
|
}
|
2021-05-27 17:24:04 +00:00
|
|
|
|
2021-06-03 13:11:55 +00:00
|
|
|
messagesToSave := messageState.Response.Messages()
|
2022-09-29 11:50:23 +00:00
|
|
|
messagesCount := len(messagesToSave)
|
|
|
|
if messagesCount > 0 {
|
|
|
|
if messagesCount <= maxChunkSizeMessages {
|
|
|
|
err = m.SaveMessages(messagesToSave)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
messageChunks := chunkSlice(messagesToSave, maxChunkSizeMessages)
|
|
|
|
chunksCount := len(messageChunks)
|
|
|
|
for i, msgs := range messageChunks {
|
|
|
|
err := m.SaveMessages(msgs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// We slow down the saving of message chunks to keep the database responsive
|
|
|
|
// this is important when messages from history archives are handled,
|
|
|
|
// which could result in handling several thousand messages per archive
|
|
|
|
if i < chunksCount-1 {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
|
|
|
}
|
2021-05-14 21:22:50 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-26 07:01:17 +00:00
|
|
|
|
2020-07-27 12:27:48 +00:00
|
|
|
for _, emojiReaction := range messageState.EmojiReactions {
|
|
|
|
messageState.Response.EmojiReactions = append(messageState.Response.EmojiReactions, emojiReaction)
|
|
|
|
}
|
|
|
|
|
2020-08-07 13:49:37 +00:00
|
|
|
for _, groupChatInvitation := range messageState.GroupChatInvitations {
|
|
|
|
messageState.Response.Invitations = append(messageState.Response.Invitations, groupChatInvitation)
|
|
|
|
}
|
|
|
|
|
2020-04-17 11:22:38 +00:00
|
|
|
if len(contactsToSave) > 0 {
|
|
|
|
err = m.persistence.SaveContacts(contactsToSave)
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2019-09-26 07:01:17 +00:00
|
|
|
|
2020-11-10 15:08:32 +00:00
|
|
|
newMessagesIds := map[string]struct{}{}
|
2021-06-03 13:11:55 +00:00
|
|
|
for _, message := range messagesToSave {
|
2021-06-25 08:30:18 +00:00
|
|
|
if message.New {
|
|
|
|
newMessagesIds[message.ID] = struct{}{}
|
|
|
|
}
|
2020-11-10 15:08:32 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 13:11:55 +00:00
|
|
|
messagesWithResponses, err := m.pullMessagesAndResponsesFromDB(messagesToSave)
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-22 16:12:59 +00:00
|
|
|
messagesByID := map[string]*common.Message{}
|
|
|
|
for _, message := range messagesWithResponses {
|
|
|
|
messagesByID[message.ID] = message
|
|
|
|
}
|
2021-06-03 13:11:55 +00:00
|
|
|
messageState.Response.SetMessages(messagesWithResponses)
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
|
2021-03-31 16:23:45 +00:00
|
|
|
notificationsEnabled, err := m.settings.GetNotificationsEnabled()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-07 04:42:27 +00:00
|
|
|
|
|
|
|
profilePicturesVisibility, err := m.settings.GetProfilePicturesVisibility()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-02-10 17:19:34 +00:00
|
|
|
m.prepareMessages(messageState.Response.messages)
|
|
|
|
|
2021-06-03 13:11:55 +00:00
|
|
|
for _, message := range messageState.Response.messages {
|
2020-11-10 15:08:32 +00:00
|
|
|
if _, ok := newMessagesIds[message.ID]; ok {
|
|
|
|
message.New = true
|
2021-02-23 07:37:08 +00:00
|
|
|
|
2021-03-31 16:23:45 +00:00
|
|
|
if notificationsEnabled {
|
|
|
|
// Create notification body to be eventually passed to `localnotifications.SendMessageNotifications()`
|
2021-10-07 04:42:27 +00:00
|
|
|
if err = messageState.addNewMessageNotification(m.identity.PublicKey, message, messagesByID[message.ResponseTo], profilePicturesVisibility); err != nil {
|
2021-03-31 16:23:45 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-23 07:37:08 +00:00
|
|
|
}
|
2021-05-29 17:05:25 +00:00
|
|
|
|
|
|
|
// Create activity center notification body to be eventually passed to `activitycenter.SendActivityCenterNotifications()`
|
|
|
|
if err = messageState.addNewActivityCenterNotification(m.identity.PublicKey, m, message, messagesByID[message.ResponseTo]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-11-10 15:08:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
// Reset installations
|
2021-03-29 15:41:30 +00:00
|
|
|
m.modifiedInstallations = new(stringBoolMap)
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
if len(messageState.AllBookmarks) > 0 {
|
|
|
|
bookmarks, err := m.storeSyncBookmarks(messageState.AllBookmarks)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
messageState.Response.AddBookmarks(bookmarks)
|
|
|
|
}
|
|
|
|
|
2022-07-05 19:49:44 +00:00
|
|
|
if len(messageState.AllVerificationRequests) > 0 {
|
|
|
|
for _, vr := range messageState.AllVerificationRequests {
|
|
|
|
messageState.Response.AddVerificationRequest(vr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(messageState.AllTrustStatus) > 0 {
|
|
|
|
messageState.Response.AddTrustStatuses(messageState.AllTrustStatus)
|
|
|
|
}
|
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
return messageState.Response, nil
|
2019-09-26 07:01:17 +00:00
|
|
|
}
|
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
func (m *Messenger) storeSyncBookmarks(bookmarkMap map[string]*browsers.Bookmark) ([]*browsers.Bookmark, error) {
|
|
|
|
var bookmarks []*browsers.Bookmark
|
|
|
|
for _, bookmark := range bookmarkMap {
|
|
|
|
bookmarks = append(bookmarks, bookmark)
|
|
|
|
}
|
|
|
|
return m.browserDatabase.StoreSyncBookmarks(bookmarks)
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
func (m *Messenger) MessageByID(id string) (*common.Message, error) {
|
2019-08-06 21:50:13 +00:00
|
|
|
return m.persistence.MessageByID(id)
|
|
|
|
}
|
|
|
|
|
2019-08-20 11:20:25 +00:00
|
|
|
func (m *Messenger) MessagesExist(ids []string) (map[string]bool, error) {
|
|
|
|
return m.persistence.MessagesExist(ids)
|
2019-08-06 21:50:13 +00:00
|
|
|
}
|
|
|
|
|
2022-06-21 16:31:15 +00:00
|
|
|
func (m *Messenger) latestIncomingMessageClock(chatID string) (uint64, error) {
|
|
|
|
return m.persistence.latestIncomingMessageClock(chatID)
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
func (m *Messenger) MessageByChatID(chatID, cursor string, limit int) ([]*common.Message, string, error) {
|
2020-11-03 10:16:05 +00:00
|
|
|
chat, err := m.persistence.Chat(chatID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
|
2021-10-27 10:59:43 +00:00
|
|
|
if chat == nil {
|
|
|
|
return nil, "", ErrChatNotFound
|
|
|
|
}
|
|
|
|
|
2022-02-10 17:19:34 +00:00
|
|
|
var msgs []*common.Message
|
|
|
|
var nextCursor string
|
|
|
|
|
2020-11-03 10:16:05 +00:00
|
|
|
if chat.Timeline() {
|
|
|
|
var chatIDs = []string{"@" + contactIDFromPublicKey(&m.identity.PublicKey)}
|
2021-03-24 08:04:03 +00:00
|
|
|
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
2021-10-01 14:50:16 +00:00
|
|
|
if contact.Added {
|
2020-11-03 10:16:05 +00:00
|
|
|
chatIDs = append(chatIDs, "@"+contact.ID)
|
|
|
|
}
|
2021-03-24 08:04:03 +00:00
|
|
|
return true
|
|
|
|
})
|
2022-02-10 17:19:34 +00:00
|
|
|
msgs, nextCursor, err = m.persistence.MessageByChatIDs(chatIDs, cursor, limit)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
msgs, nextCursor, err = m.persistence.MessageByChatID(chatID, cursor, limit)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-05-09 13:07:57 +00:00
|
|
|
if m.httpServer != nil {
|
|
|
|
for idx := range msgs {
|
2022-05-10 09:34:53 +00:00
|
|
|
m.prepareMessage(msgs[idx], m.httpServer)
|
2022-05-09 13:07:57 +00:00
|
|
|
}
|
2022-02-10 17:19:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return msgs, nextCursor, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) prepareMessages(messages map[string]*common.Message) {
|
2022-05-09 13:07:57 +00:00
|
|
|
if m.httpServer != nil {
|
|
|
|
for idx := range messages {
|
2022-05-10 09:34:53 +00:00
|
|
|
m.prepareMessage(messages[idx], m.httpServer)
|
2022-05-09 13:07:57 +00:00
|
|
|
}
|
2020-11-03 10:16:05 +00:00
|
|
|
}
|
2019-08-06 21:50:13 +00:00
|
|
|
}
|
|
|
|
|
2022-05-10 09:34:53 +00:00
|
|
|
func (m *Messenger) prepareMessage(msg *common.Message, s *server.MediaServer) {
|
|
|
|
if msg.QuotedMessage != nil && msg.QuotedMessage.ContentType == int64(protobuf.ChatMessage_IMAGE) {
|
|
|
|
msg.QuotedMessage.ImageLocalURL = s.MakeImageURL(msg.QuotedMessage.ID)
|
|
|
|
}
|
2022-10-05 17:54:47 +00:00
|
|
|
if msg.QuotedMessage != nil && msg.QuotedMessage.ContentType == int64(protobuf.ChatMessage_AUDIO) {
|
|
|
|
msg.QuotedMessage.AudioLocalURL = s.MakeAudioURL(msg.QuotedMessage.ID)
|
|
|
|
}
|
|
|
|
if msg.QuotedMessage != nil && msg.QuotedMessage.ContentType == int64(protobuf.ChatMessage_STICKER) {
|
|
|
|
msg.QuotedMessage.HasSticker = true
|
|
|
|
}
|
2022-05-10 09:34:53 +00:00
|
|
|
if msg.ContentType == protobuf.ChatMessage_IMAGE {
|
|
|
|
msg.ImageLocalURL = s.MakeImageURL(msg.ID)
|
|
|
|
}
|
2022-09-20 09:55:59 +00:00
|
|
|
if msg.ContentType == protobuf.ChatMessage_DISCORD_MESSAGE {
|
|
|
|
|
|
|
|
dm := msg.GetDiscordMessage()
|
|
|
|
exists, err := m.persistence.HasDiscordMessageAuthorImagePayload(dm.Author.Id)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if exists {
|
|
|
|
dm.Author.LocalUrl = s.MakeDiscordAuthorAvatarURL(dm.Author.Id)
|
|
|
|
}
|
|
|
|
|
|
|
|
for idx, attachment := range dm.Attachments {
|
|
|
|
if strings.Contains(attachment.ContentType, "image") {
|
2022-10-05 10:39:55 +00:00
|
|
|
hasPayload, err := m.persistence.HasDiscordMessageAttachmentPayload(attachment.Id, dm.Id)
|
2022-09-20 09:55:59 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to check if message attachment exist", zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if hasPayload {
|
|
|
|
localURL := s.MakeDiscordAttachmentURL(dm.Id, attachment.Id)
|
|
|
|
dm.Attachments[idx].LocalUrl = localURL
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
msg.Payload = &protobuf.ChatMessage_DiscordMessage{
|
|
|
|
DiscordMessage: dm,
|
|
|
|
}
|
|
|
|
}
|
2022-05-10 09:34:53 +00:00
|
|
|
if msg.ContentType == protobuf.ChatMessage_AUDIO {
|
|
|
|
msg.AudioLocalURL = s.MakeAudioURL(msg.ID)
|
|
|
|
}
|
|
|
|
if msg.ContentType == protobuf.ChatMessage_STICKER {
|
|
|
|
msg.StickerLocalURL = s.MakeStickerURL(msg.GetSticker().Hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-04 19:31:44 +00:00
|
|
|
func (m *Messenger) AllMessageByChatIDWhichMatchTerm(chatID string, searchTerm string, caseSensitive bool) ([]*common.Message, error) {
|
|
|
|
_, err := m.persistence.Chat(chatID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.persistence.AllMessageByChatIDWhichMatchTerm(chatID, searchTerm, caseSensitive)
|
|
|
|
}
|
|
|
|
|
2021-08-19 19:47:03 +00:00
|
|
|
func (m *Messenger) AllMessagesFromChatsAndCommunitiesWhichMatchTerm(communityIds []string, chatIds []string, searchTerm string, caseSensitive bool) ([]*common.Message, error) {
|
|
|
|
return m.persistence.AllMessagesFromChatsAndCommunitiesWhichMatchTerm(communityIds, chatIds, searchTerm, caseSensitive)
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
func (m *Messenger) SaveMessages(messages []*common.Message) error {
|
2020-05-20 12:16:12 +00:00
|
|
|
return m.persistence.SaveMessages(messages)
|
2019-08-06 21:50:13 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 11:20:25 +00:00
|
|
|
func (m *Messenger) DeleteMessage(id string) error {
|
|
|
|
return m.persistence.DeleteMessage(id)
|
2019-08-06 21:50:13 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 11:20:25 +00:00
|
|
|
func (m *Messenger) DeleteMessagesByChatID(id string) error {
|
|
|
|
return m.persistence.DeleteMessagesByChatID(id)
|
2019-08-06 21:50:13 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 12:08:53 +00:00
|
|
|
// MarkMessagesSeen marks messages with `ids` as seen in the chat `chatID`.
|
|
|
|
// It returns the number of affected messages or error. If there is an error,
|
|
|
|
// the number of affected messages is always zero.
|
2021-08-31 08:49:39 +00:00
|
|
|
func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, uint64, error) {
|
|
|
|
count, countWithMentions, err := m.persistence.MarkMessagesSeen(chatID, ids)
|
2019-12-02 15:34:05 +00:00
|
|
|
if err != nil {
|
2021-08-31 08:49:39 +00:00
|
|
|
return 0, 0, err
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
|
|
|
chat, err := m.persistence.Chat(chatID)
|
|
|
|
if err != nil {
|
2021-08-31 08:49:39 +00:00
|
|
|
return 0, 0, err
|
2019-12-02 15:34:05 +00:00
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allChats.Store(chatID, chat)
|
2021-08-31 08:49:39 +00:00
|
|
|
return count, countWithMentions, nil
|
2019-08-06 21:50:13 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 10:33:32 +00:00
|
|
|
func (m *Messenger) syncChatMessagesRead(ctx context.Context, chatID string, clock uint64) error {
|
|
|
|
if !m.hasPairedDevices() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
_, chat := m.getLastClockWithRelatedChat()
|
|
|
|
|
|
|
|
syncMessage := &protobuf.SyncChatMessagesRead{
|
|
|
|
Clock: clock,
|
|
|
|
Id: chatID,
|
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(syncMessage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_MESSAGES_READ,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) markAllRead(chatID string, clock uint64, shouldBeSynced bool) error {
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-02-26 12:31:48 +00:00
|
|
|
if !ok {
|
|
|
|
return errors.New("chat not found")
|
|
|
|
}
|
|
|
|
|
2021-10-12 10:33:32 +00:00
|
|
|
seen, mentioned, err := m.persistence.MarkAllRead(chatID, clock)
|
2020-02-26 12:31:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-10-12 10:33:32 +00:00
|
|
|
if shouldBeSynced {
|
|
|
|
err := m.syncChatMessagesRead(context.Background(), chatID, clock)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.ReadMessagesAtClockValue = clock
|
2021-10-21 17:04:56 +00:00
|
|
|
chat.Highlight = false
|
2021-10-12 10:33:32 +00:00
|
|
|
|
|
|
|
if chat.UnviewedMessagesCount >= uint(seen) {
|
|
|
|
chat.UnviewedMessagesCount -= uint(seen)
|
|
|
|
} else {
|
|
|
|
chat.UnviewedMessagesCount = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if chat.UnviewedMentionsCount >= uint(mentioned) {
|
|
|
|
chat.UnviewedMentionsCount -= uint(mentioned)
|
|
|
|
} else {
|
|
|
|
chat.UnviewedMentionsCount = 0
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
|
|
m.allChats.Store(chat.ID, chat)
|
2022-01-31 12:10:28 +00:00
|
|
|
return m.persistence.SaveChats([]*Chat{chat})
|
2020-02-26 12:31:48 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 10:33:32 +00:00
|
|
|
func (m *Messenger) MarkAllRead(chatID string) error {
|
2022-04-06 17:01:24 +00:00
|
|
|
err := m.persistence.DismissAllActivityCenterNotificationsFromChatID(chatID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-06-21 16:31:15 +00:00
|
|
|
clock, _ := m.latestIncomingMessageClock(chatID)
|
|
|
|
|
|
|
|
if clock == 0 {
|
|
|
|
chat, ok := m.allChats.Load(chatID)
|
|
|
|
if !ok {
|
|
|
|
return errors.New("chat not found")
|
|
|
|
}
|
|
|
|
clock, _ = chat.NextClockAndTimestamp(m.getTimesource())
|
2021-10-12 10:33:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return m.markAllRead(chatID, clock, true)
|
|
|
|
}
|
|
|
|
|
2021-09-20 06:33:36 +00:00
|
|
|
func (m *Messenger) MarkAllReadInCommunity(communityID string) ([]string, error) {
|
2022-04-04 01:02:40 +00:00
|
|
|
err := m.persistence.DismissAllActivityCenterNotificationsFromCommunity(communityID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-20 06:33:36 +00:00
|
|
|
chatIDs, err := m.persistence.AllChatIDsByCommunity(communityID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.persistence.MarkAllReadMultiple(chatIDs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, chatID := range chatIDs {
|
|
|
|
chat, ok := m.allChats.Load(chatID)
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
chat.UnviewedMessagesCount = 0
|
|
|
|
chat.UnviewedMentionsCount = 0
|
|
|
|
m.allChats.Store(chat.ID, chat)
|
|
|
|
} else {
|
|
|
|
err = errors.New(fmt.Sprintf("chat with chatID %s not found", chatID))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return chatIDs, err
|
|
|
|
}
|
|
|
|
|
2020-06-26 07:46:14 +00:00
|
|
|
// MuteChat signals to the messenger that we don't want to be notified
|
|
|
|
// on new messages from this chat
|
|
|
|
func (m *Messenger) MuteChat(chatID string) error {
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-06-26 07:46:14 +00:00
|
|
|
if !ok {
|
2021-06-04 07:18:32 +00:00
|
|
|
// Only one to one chan be muted when it's not in the database
|
|
|
|
publicKey, err := common.HexToPubkey(chatID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a one to one chat and set active to false
|
|
|
|
chat = CreateOneToOneChat(chatID, publicKey, m.getTimesource())
|
|
|
|
chat.Active = false
|
|
|
|
err = m.initChatSyncFields(chat)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = m.saveChat(chat)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-06-26 07:46:14 +00:00
|
|
|
}
|
|
|
|
|
2021-09-03 08:26:05 +00:00
|
|
|
var contact *Contact
|
|
|
|
if chat.OneToOne() {
|
|
|
|
contact, _ = m.allContacts.Load(chatID)
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.muteChat(chat, contact)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) muteChat(chat *Chat, contact *Contact) error {
|
|
|
|
err := m.persistence.MuteChat(chat.ID)
|
2020-06-26 07:46:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.Muted = true
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
|
|
m.allChats.Store(chat.ID, chat)
|
2020-08-18 15:07:48 +00:00
|
|
|
|
2021-09-03 08:26:05 +00:00
|
|
|
if contact != nil {
|
|
|
|
err := m.syncContact(context.Background(), contact)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 15:07:48 +00:00
|
|
|
return m.reregisterForPushNotifications()
|
2020-06-26 07:46:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UnmuteChat signals to the messenger that we want to be notified
|
|
|
|
// on new messages from this chat
|
|
|
|
func (m *Messenger) UnmuteChat(chatID string) error {
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-06-26 07:46:14 +00:00
|
|
|
if !ok {
|
|
|
|
return errors.New("chat not found")
|
|
|
|
}
|
|
|
|
|
2021-09-03 08:26:05 +00:00
|
|
|
var contact *Contact
|
|
|
|
if chat.OneToOne() {
|
|
|
|
contact, _ = m.allContacts.Load(chatID)
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.unmuteChat(chat, contact)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) unmuteChat(chat *Chat, contact *Contact) error {
|
|
|
|
err := m.persistence.UnmuteChat(chat.ID)
|
2020-06-26 07:46:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.Muted = false
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
|
|
m.allChats.Store(chat.ID, chat)
|
2021-09-03 08:26:05 +00:00
|
|
|
|
|
|
|
if contact != nil {
|
|
|
|
err := m.syncContact(context.Background(), contact)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-08-18 15:07:48 +00:00
|
|
|
return m.reregisterForPushNotifications()
|
2020-06-26 07:46:14 +00:00
|
|
|
}
|
|
|
|
|
2019-08-06 21:50:13 +00:00
|
|
|
func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
|
|
|
|
return m.persistence.UpdateMessageOutgoingStatus(id, newOutgoingStatus)
|
|
|
|
}
|
2019-09-26 07:01:17 +00:00
|
|
|
|
|
|
|
// Identicon returns an identicon based on the input string
|
|
|
|
func Identicon(id string) (string, error) {
|
|
|
|
return identicon.GenerateBase64(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenerateAlias name returns the generated name given a public key hex encoded prefixed with 0x
|
|
|
|
func GenerateAlias(id string) (string, error) {
|
|
|
|
return alias.GenerateFromPublicKeyString(id)
|
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contract, address string) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message := &common.Message{}
|
2020-01-20 16:44:32 +00:00
|
|
|
err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-25 11:46:43 +00:00
|
|
|
message.MessageType = protobuf.MessageType_ONE_TO_ONE
|
2020-01-10 18:59:01 +00:00
|
|
|
message.ContentType = protobuf.ChatMessage_TRANSACTION_COMMAND
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Text = "Request transaction"
|
|
|
|
|
|
|
|
request := &protobuf.RequestTransaction{
|
|
|
|
Clock: message.Clock,
|
|
|
|
Address: address,
|
|
|
|
Value: value,
|
|
|
|
Contract: contract,
|
2021-07-07 11:18:18 +00:00
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters = &common.CommandParameters{
|
2021-01-22 13:59:45 +00:00
|
|
|
ID: rawMessage.ID,
|
2020-01-10 18:59:01 +00:00
|
|
|
Value: value,
|
|
|
|
Address: address,
|
|
|
|
Contract: contract,
|
2020-09-01 13:27:01 +00:00
|
|
|
CommandState: common.CommandStateRequestTransaction,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-22 13:59:45 +00:00
|
|
|
messageID := rawMessage.ID
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
message.ID = messageID
|
|
|
|
message.CommandParameters.ID = messageID
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, from, value, contract string) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message := &common.Message{}
|
2020-01-20 16:44:32 +00:00
|
|
|
err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-25 11:46:43 +00:00
|
|
|
message.MessageType = protobuf.MessageType_ONE_TO_ONE
|
2020-01-10 18:59:01 +00:00
|
|
|
message.ContentType = protobuf.ChatMessage_TRANSACTION_COMMAND
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Text = "Request address for transaction"
|
|
|
|
|
|
|
|
request := &protobuf.RequestAddressForTransaction{
|
|
|
|
Clock: message.Clock,
|
|
|
|
Value: value,
|
|
|
|
Contract: contract,
|
2021-07-07 11:18:18 +00:00
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters = &common.CommandParameters{
|
2021-01-22 13:59:45 +00:00
|
|
|
ID: rawMessage.ID,
|
2020-01-10 18:59:01 +00:00
|
|
|
From: from,
|
|
|
|
Value: value,
|
|
|
|
Contract: contract,
|
2020-09-01 13:27:01 +00:00
|
|
|
CommandState: common.CommandStateRequestAddressForTransaction,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-22 13:59:45 +00:00
|
|
|
messageID := rawMessage.ID
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
message.ID = messageID
|
|
|
|
message.CommandParameters.ID = messageID
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, messageID, address string) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
message, err := m.MessageByID(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if message == nil {
|
|
|
|
return nil, errors.New("message not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
chatID := message.LocalChatID
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Clock = clock
|
|
|
|
message.WhisperTimestamp = timestamp
|
|
|
|
message.Timestamp = timestamp
|
|
|
|
message.Text = "Request address for transaction accepted"
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-09-01 13:27:01 +00:00
|
|
|
message.OutgoingStatus = common.OutgoingStatusSending
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
// Hide previous message
|
2020-01-17 12:39:09 +00:00
|
|
|
previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if previousMessage == nil {
|
|
|
|
return nil, errors.New("No previous message found")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.persistence.HideMessage(previousMessage.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
message.Replace = previousMessage.ID
|
|
|
|
|
|
|
|
request := &protobuf.AcceptRequestAddressForTransaction{
|
|
|
|
Clock: message.Clock,
|
|
|
|
Id: messageID,
|
|
|
|
Address: address,
|
2021-07-07 11:18:18 +00:00
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
message.ID = rawMessage.ID
|
2020-01-10 18:59:01 +00:00
|
|
|
message.CommandParameters.Address = address
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionAccepted
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID string) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
message, err := m.MessageByID(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if message == nil {
|
|
|
|
return nil, errors.New("message not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
chatID := message.LocalChatID
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Clock = clock
|
|
|
|
message.WhisperTimestamp = timestamp
|
|
|
|
message.Timestamp = timestamp
|
|
|
|
message.Text = "Transaction request declined"
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-09-01 13:27:01 +00:00
|
|
|
message.OutgoingStatus = common.OutgoingStatusSending
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Replace = messageID
|
|
|
|
|
|
|
|
err = m.persistence.HideMessage(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
request := &protobuf.DeclineRequestTransaction{
|
2021-07-07 11:18:18 +00:00
|
|
|
Clock: message.Clock,
|
|
|
|
Id: messageID,
|
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
message.ID = rawMessage.ID
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters.CommandState = common.CommandStateRequestTransactionDeclined
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, messageID string) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
message, err := m.MessageByID(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if message == nil {
|
|
|
|
return nil, errors.New("message not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
chatID := message.LocalChatID
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Clock = clock
|
|
|
|
message.WhisperTimestamp = timestamp
|
|
|
|
message.Timestamp = timestamp
|
|
|
|
message.Text = "Request address for transaction declined"
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-09-01 13:27:01 +00:00
|
|
|
message.OutgoingStatus = common.OutgoingStatusSending
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Replace = messageID
|
|
|
|
|
|
|
|
err = m.persistence.HideMessage(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
request := &protobuf.DeclineRequestAddressForTransaction{
|
2021-07-07 11:18:18 +00:00
|
|
|
Clock: message.Clock,
|
|
|
|
Id: messageID,
|
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
message.ID = rawMessage.ID
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionDeclined
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHash, messageID string, signature []byte) (*MessengerResponse, error) {
|
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
message, err := m.MessageByID(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if message == nil {
|
|
|
|
return nil, errors.New("message not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
chatID := message.LocalChatID
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Clock = clock
|
|
|
|
message.WhisperTimestamp = timestamp
|
|
|
|
message.Timestamp = timestamp
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-02-10 11:22:37 +00:00
|
|
|
message.Text = transactionSentTxt
|
2020-09-01 13:27:01 +00:00
|
|
|
message.OutgoingStatus = common.OutgoingStatusSending
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
// Hide previous message
|
2020-01-17 12:39:09 +00:00
|
|
|
previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID)
|
2020-10-08 10:46:03 +00:00
|
|
|
if err != nil && err != common.ErrRecordNotFound {
|
2020-01-10 18:59:01 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if previousMessage != nil {
|
|
|
|
err = m.persistence.HideMessage(previousMessage.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
message.Replace = previousMessage.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
err = m.persistence.HideMessage(messageID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
request := &protobuf.SendTransaction{
|
|
|
|
Clock: message.Clock,
|
|
|
|
Id: messageID,
|
|
|
|
TransactionHash: transactionHash,
|
|
|
|
Signature: signature,
|
2021-07-07 11:18:18 +00:00
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
message.ID = rawMessage.ID
|
2020-01-10 18:59:01 +00:00
|
|
|
message.CommandParameters.TransactionHash = transactionHash
|
|
|
|
message.CommandParameters.Signature = signature
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters.CommandState = common.CommandStateTransactionSent
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 07:25:09 +00:00
|
|
|
func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract, transactionHash string, signature []byte) (*MessengerResponse, error) {
|
2020-01-10 18:59:01 +00:00
|
|
|
var response MessengerResponse
|
|
|
|
|
|
|
|
// A valid added chat is required.
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("Chat not found")
|
|
|
|
}
|
|
|
|
if chat.ChatType != ChatTypeOneToOne {
|
|
|
|
return nil, errors.New("Need to be a one-to-one chat")
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
message := &common.Message{}
|
2020-01-20 16:44:32 +00:00
|
|
|
err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-25 11:46:43 +00:00
|
|
|
message.MessageType = protobuf.MessageType_ONE_TO_ONE
|
2020-01-10 18:59:01 +00:00
|
|
|
message.ContentType = protobuf.ChatMessage_TRANSACTION_COMMAND
|
|
|
|
message.LocalChatID = chatID
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Clock = clock
|
|
|
|
message.WhisperTimestamp = timestamp
|
2020-12-07 15:13:39 +00:00
|
|
|
message.Seen = true
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Timestamp = timestamp
|
2020-02-10 11:22:37 +00:00
|
|
|
message.Text = transactionSentTxt
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
request := &protobuf.SendTransaction{
|
|
|
|
Clock: message.Clock,
|
|
|
|
TransactionHash: transactionHash,
|
|
|
|
Signature: signature,
|
2021-07-07 11:18:18 +00:00
|
|
|
ChatId: chatID,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
encodedMessage, err := proto.Marshal(request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
rawMessage, err := m.dispatchMessage(ctx, common.RawMessage{
|
2020-01-10 18:59:01 +00:00
|
|
|
LocalChatID: chat.ID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
|
|
|
ResendAutomatically: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-22 13:59:45 +00:00
|
|
|
message.ID = rawMessage.ID
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters = &common.CommandParameters{
|
2020-01-10 18:59:01 +00:00
|
|
|
TransactionHash: transactionHash,
|
2020-01-15 07:25:09 +00:00
|
|
|
Value: value,
|
|
|
|
Contract: contract,
|
2020-01-10 18:59:01 +00:00
|
|
|
Signature: signature,
|
2020-09-01 13:27:01 +00:00
|
|
|
CommandState: common.CommandStateTransactionSent,
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-09-01 13:27:01 +00:00
|
|
|
err = m.persistence.SaveMessages([]*common.Message{message})
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 08:30:18 +00:00
|
|
|
return m.addMessagesAndChat(chat, []*common.Message{message}, &response)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.Address) (*MessengerResponse, error) {
|
|
|
|
if m.verifyTransactionClient == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
logger := m.logger.With(zap.String("site", "ValidateTransactions"))
|
|
|
|
logger.Debug("Validating transactions")
|
|
|
|
txs, err := m.persistence.TransactionsToValidate()
|
|
|
|
if err != nil {
|
|
|
|
logger.Error("Error pulling", zap.Error(err))
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
logger.Debug("Txs", zap.Int("count", len(txs)), zap.Any("txs", txs))
|
|
|
|
var response MessengerResponse
|
|
|
|
validator := NewTransactionValidator(addresses, m.persistence, m.verifyTransactionClient, m.logger)
|
|
|
|
responses, err := validator.ValidateTransactions(ctx)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error("Error validating", zap.Error(err))
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, validationResult := range responses {
|
2020-09-01 13:27:01 +00:00
|
|
|
var message *common.Message
|
2020-01-15 07:25:09 +00:00
|
|
|
chatID := contactIDFromPublicKey(validationResult.Transaction.From)
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-01-10 18:59:01 +00:00
|
|
|
if !ok {
|
2020-01-20 16:44:32 +00:00
|
|
|
chat = OneToOneFromPublicKey(validationResult.Transaction.From, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
if validationResult.Message != nil {
|
|
|
|
message = validationResult.Message
|
|
|
|
} else {
|
2020-09-01 13:27:01 +00:00
|
|
|
message = &common.Message{}
|
2020-01-20 16:44:32 +00:00
|
|
|
err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-25 11:46:43 +00:00
|
|
|
message.MessageType = protobuf.MessageType_ONE_TO_ONE
|
2020-01-10 18:59:01 +00:00
|
|
|
message.ContentType = protobuf.ChatMessage_TRANSACTION_COMMAND
|
|
|
|
message.LocalChatID = chatID
|
|
|
|
message.OutgoingStatus = ""
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
message.Clock = clock
|
|
|
|
message.Timestamp = timestamp
|
|
|
|
message.WhisperTimestamp = timestamp
|
|
|
|
message.Text = "Transaction received"
|
2020-04-06 12:08:53 +00:00
|
|
|
message.Seen = false
|
2020-01-10 18:59:01 +00:00
|
|
|
|
|
|
|
message.ID = validationResult.Transaction.MessageID
|
|
|
|
if message.CommandParameters == nil {
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters = &common.CommandParameters{}
|
2020-01-10 18:59:01 +00:00
|
|
|
} else {
|
|
|
|
message.CommandParameters = validationResult.Message.CommandParameters
|
|
|
|
}
|
|
|
|
|
|
|
|
message.CommandParameters.Value = validationResult.Value
|
|
|
|
message.CommandParameters.Contract = validationResult.Contract
|
|
|
|
message.CommandParameters.Address = validationResult.Address
|
2020-09-01 13:27:01 +00:00
|
|
|
message.CommandParameters.CommandState = common.CommandStateTransactionSent
|
2020-01-10 18:59:01 +00:00
|
|
|
message.CommandParameters.TransactionHash = validationResult.Transaction.TransactionHash
|
|
|
|
|
2021-05-25 09:40:02 +00:00
|
|
|
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:44:32 +00:00
|
|
|
err = chat.UpdateFromMessage(message, m.transport)
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-17 12:39:09 +00:00
|
|
|
if len(message.CommandParameters.ID) != 0 {
|
|
|
|
// Hide previous message
|
|
|
|
previousMessage, err := m.persistence.MessageByCommandID(chatID, message.CommandParameters.ID)
|
2020-10-08 10:46:03 +00:00
|
|
|
if err != nil && err != common.ErrRecordNotFound {
|
2020-01-10 18:59:01 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-17 12:39:09 +00:00
|
|
|
|
|
|
|
if previousMessage != nil {
|
|
|
|
err = m.persistence.HideMessage(previousMessage.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
message.Replace = previousMessage.ID
|
|
|
|
}
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 13:11:55 +00:00
|
|
|
response.AddMessage(message)
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allChats.Store(chat.ID, chat)
|
2021-01-11 10:32:51 +00:00
|
|
|
response.AddChat(chat)
|
2020-01-10 18:59:01 +00:00
|
|
|
|
2021-02-23 07:37:08 +00:00
|
|
|
contact, err := m.getOrBuildContactFromMessage(message)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-03-31 16:23:45 +00:00
|
|
|
|
|
|
|
notificationsEnabled, err := m.settings.GetNotificationsEnabled()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-10-07 04:42:27 +00:00
|
|
|
|
|
|
|
profilePicturesVisibility, err := m.settings.GetProfilePicturesVisibility()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-03-31 16:23:45 +00:00
|
|
|
if notificationsEnabled {
|
2021-10-07 04:42:27 +00:00
|
|
|
notification, err := NewMessageNotification(message.ID, message, chat, contact, m.allContacts, profilePicturesVisibility)
|
2021-03-31 16:23:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
response.AddNotification(notification)
|
|
|
|
}
|
2021-02-23 07:37:08 +00:00
|
|
|
|
2020-01-10 18:59:01 +00:00
|
|
|
}
|
|
|
|
|
2021-06-03 13:11:55 +00:00
|
|
|
if len(response.messages) > 0 {
|
|
|
|
err = m.SaveMessages(response.Messages())
|
2020-01-10 18:59:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &response, nil
|
|
|
|
}
|
2020-01-20 16:44:32 +00:00
|
|
|
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
// pullMessagesAndResponsesFromDB pulls all the messages and the one that have
|
|
|
|
// been replied to from the database
|
2020-09-01 13:27:01 +00:00
|
|
|
func (m *Messenger) pullMessagesAndResponsesFromDB(messages []*common.Message) ([]*common.Message, error) {
|
Add replies to messages
Currently replies to messages are handled in status-react.
This causes some issues with the fact that sometimes replies might come
out of order, they might be offloaded to the database etc.
This commit changes the behavior so that status-go always returns the
replies, and in case a reply comes out of order (first the reply, later
the message being replied to), it will include in the messages the
updated message.
It also adds some fields (RTL,Replace,LineCount) to the database which
were not previously saved, resulting in some potential bugs.
The method that we use to pull replies is currently a bit naive, we just
pull all the message again from the database, but has the advantage of
being simple. It will go through performance testing to make sure
performnace are acceptable, if so I think it's reasonable to avoid some
complexity.
2020-04-08 13:42:02 +00:00
|
|
|
var messageIDs []string
|
|
|
|
for _, message := range messages {
|
|
|
|
messageIDs = append(messageIDs, message.ID)
|
|
|
|
if len(message.ResponseTo) != 0 {
|
|
|
|
messageIDs = append(messageIDs, message.ResponseTo)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
// We pull from the database all the messages & replies involved,
|
|
|
|
// so we let the db build the correct messages
|
|
|
|
return m.persistence.MessagesByIDs(messageIDs)
|
|
|
|
}
|
|
|
|
|
2020-03-20 08:32:13 +00:00
|
|
|
func (m *Messenger) SignMessage(message string) ([]byte, error) {
|
|
|
|
hash := crypto.TextHash([]byte(message))
|
|
|
|
return crypto.Sign(hash, m.identity)
|
|
|
|
}
|
|
|
|
|
2020-11-18 09:16:51 +00:00
|
|
|
func (m *Messenger) getTimesource() common.TimeSource {
|
2020-01-20 16:44:32 +00:00
|
|
|
return m.transport
|
|
|
|
}
|
2020-02-07 11:30:26 +00:00
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// AddPushNotificationsServer adds a push notification server
|
2020-08-20 09:05:39 +00:00
|
|
|
func (m *Messenger) AddPushNotificationsServer(ctx context.Context, publicKey *ecdsa.PublicKey, serverType pushnotificationclient.ServerType) error {
|
2020-07-07 09:00:04 +00:00
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("push notification client not enabled")
|
|
|
|
}
|
2020-08-20 09:05:39 +00:00
|
|
|
return m.pushNotificationClient.AddPushNotificationsServer(publicKey, serverType)
|
2020-07-07 09:00:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-17 11:41:49 +00:00
|
|
|
// RemovePushNotificationServer removes a push notification server
|
|
|
|
func (m *Messenger) RemovePushNotificationServer(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("push notification client not enabled")
|
|
|
|
}
|
|
|
|
return m.pushNotificationClient.RemovePushNotificationServer(publicKey)
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// UnregisterFromPushNotifications unregister from any server
|
2020-07-15 12:43:15 +00:00
|
|
|
func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error {
|
2020-07-16 07:45:42 +00:00
|
|
|
return m.pushNotificationClient.Unregister()
|
2020-07-15 12:43:15 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// DisableSendingPushNotifications signals the client not to send any push notification
|
2020-07-15 12:43:15 +00:00
|
|
|
func (m *Messenger) DisableSendingPushNotifications() error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("push notification client not enabled")
|
|
|
|
}
|
|
|
|
m.pushNotificationClient.DisableSending()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// EnableSendingPushNotifications signals the client to send push notifications
|
2020-07-15 12:43:15 +00:00
|
|
|
func (m *Messenger) EnableSendingPushNotifications() error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("push notification client not enabled")
|
|
|
|
}
|
|
|
|
m.pushNotificationClient.EnableSending()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
func (m *Messenger) pushNotificationOptions() *pushnotificationclient.RegistrationOptions {
|
2020-07-07 09:00:04 +00:00
|
|
|
var contactIDs []*ecdsa.PublicKey
|
|
|
|
var mutedChatIDs []string
|
2020-09-03 07:19:46 +00:00
|
|
|
var publicChatIDs []string
|
2020-07-07 09:00:04 +00:00
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
2021-10-01 14:50:16 +00:00
|
|
|
if contact.Added && !contact.Blocked {
|
2020-07-07 09:00:04 +00:00
|
|
|
pk, err := contact.PublicKey()
|
|
|
|
if err != nil {
|
|
|
|
m.logger.Warn("could not parse contact public key")
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
2020-07-07 09:00:04 +00:00
|
|
|
}
|
|
|
|
contactIDs = append(contactIDs, pk)
|
2021-10-01 14:50:16 +00:00
|
|
|
} else if contact.Blocked {
|
2020-07-07 09:00:04 +00:00
|
|
|
mutedChatIDs = append(mutedChatIDs, contact.ID)
|
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
|
2020-07-07 09:00:04 +00:00
|
|
|
if chat.Muted {
|
|
|
|
mutedChatIDs = append(mutedChatIDs, chat.ID)
|
|
|
|
}
|
2021-09-01 09:03:45 +00:00
|
|
|
if chat.Active && (chat.Public() || chat.CommunityChat()) {
|
2020-09-03 07:19:46 +00:00
|
|
|
publicChatIDs = append(publicChatIDs, chat.ID)
|
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
2021-08-31 08:49:39 +00:00
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
return &pushnotificationclient.RegistrationOptions{
|
2020-09-03 07:19:46 +00:00
|
|
|
ContactIDs: contactIDs,
|
|
|
|
MutedChatIDs: mutedChatIDs,
|
|
|
|
PublicChatIDs: publicChatIDs,
|
2020-09-02 14:11:16 +00:00
|
|
|
}
|
2020-07-17 12:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterForPushNotification register deviceToken with any push notification server enabled
|
2020-07-30 08:24:30 +00:00
|
|
|
func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType) error {
|
2020-07-17 12:29:51 +00:00
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("push notification client not enabled")
|
|
|
|
}
|
2020-07-20 13:58:54 +00:00
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
2020-07-17 12:29:51 +00:00
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, m.pushNotificationOptions())
|
2020-08-18 15:07:48 +00:00
|
|
|
if err != nil {
|
|
|
|
m.logger.Error("failed to register for push notifications", zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2020-07-07 09:00:04 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// RegisteredForPushNotifications returns whether we successfully registered with all the servers
|
2020-07-16 08:36:17 +00:00
|
|
|
func (m *Messenger) RegisteredForPushNotifications() (bool, error) {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return false, errors.New("no push notification client")
|
|
|
|
}
|
|
|
|
return m.pushNotificationClient.Registered()
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// EnablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications only from contacts
|
2020-07-17 11:41:49 +00:00
|
|
|
func (m *Messenger) EnablePushNotificationsFromContactsOnly() error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("no push notification client")
|
|
|
|
}
|
2020-07-20 13:58:54 +00:00
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
2020-07-17 11:41:49 +00:00
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(m.pushNotificationOptions())
|
2020-07-17 11:41:49 +00:00
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone
|
2020-07-17 11:41:49 +00:00
|
|
|
func (m *Messenger) DisablePushNotificationsFromContactsOnly() error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("no push notification client")
|
|
|
|
}
|
2020-07-20 13:58:54 +00:00
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
2020-07-17 11:41:49 +00:00
|
|
|
|
2020-09-02 14:11:16 +00:00
|
|
|
return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(m.pushNotificationOptions())
|
2020-07-17 11:41:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-03 07:30:03 +00:00
|
|
|
// EnablePushNotificationsBlockMentions is used to indicate that we dont want to received push notifications for mentions
|
|
|
|
func (m *Messenger) EnablePushNotificationsBlockMentions() error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("no push notification client")
|
|
|
|
}
|
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
|
|
|
|
|
|
|
return m.pushNotificationClient.EnablePushNotificationsBlockMentions(m.pushNotificationOptions())
|
|
|
|
}
|
|
|
|
|
|
|
|
// DisablePushNotificationsBlockMentions is used to indicate that we want to received push notifications for mentions
|
|
|
|
func (m *Messenger) DisablePushNotificationsBlockMentions() error {
|
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return errors.New("no push notification client")
|
|
|
|
}
|
|
|
|
m.mutex.Lock()
|
|
|
|
defer m.mutex.Unlock()
|
|
|
|
|
|
|
|
return m.pushNotificationClient.DisablePushNotificationsBlockMentions(m.pushNotificationOptions())
|
|
|
|
}
|
|
|
|
|
2020-08-20 09:05:39 +00:00
|
|
|
// GetPushNotificationsServers returns the servers used for push notifications
|
|
|
|
func (m *Messenger) GetPushNotificationsServers() ([]*pushnotificationclient.PushNotificationServer, error) {
|
2020-07-16 08:36:17 +00:00
|
|
|
if m.pushNotificationClient == nil {
|
|
|
|
return nil, errors.New("no push notification client")
|
|
|
|
}
|
|
|
|
return m.pushNotificationClient.GetServers()
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// StartPushNotificationsServer initialize and start a push notification server, using the current messenger identity key
|
|
|
|
func (m *Messenger) StartPushNotificationsServer() error {
|
2020-07-14 14:07:19 +00:00
|
|
|
if m.pushNotificationServer == nil {
|
2020-07-22 07:41:40 +00:00
|
|
|
pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(m.database)
|
|
|
|
config := &pushnotificationserver.Config{
|
2020-08-20 07:26:00 +00:00
|
|
|
Enabled: true,
|
2020-07-14 14:07:19 +00:00
|
|
|
Logger: m.logger,
|
|
|
|
Identity: m.identity,
|
|
|
|
}
|
2021-06-23 14:13:48 +00:00
|
|
|
m.pushNotificationServer = pushnotificationserver.New(config, pushNotificationServerPersistence, m.sender)
|
2020-07-14 14:07:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return m.pushNotificationServer.Start()
|
|
|
|
}
|
|
|
|
|
2020-07-22 07:41:40 +00:00
|
|
|
// StopPushNotificationServer stops the push notification server if running
|
|
|
|
func (m *Messenger) StopPushNotificationsServer() error {
|
2020-07-14 14:07:19 +00:00
|
|
|
m.pushNotificationServer = nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-20 12:16:12 +00:00
|
|
|
func generateAliasAndIdenticon(pk string) (string, string, error) {
|
|
|
|
identicon, err := identicon.GenerateBase64(pk)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
name, err := alias.GenerateFromPublicKeyString(pk)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
return name, identicon, nil
|
|
|
|
|
|
|
|
}
|
2020-07-20 17:06:38 +00:00
|
|
|
|
2020-07-27 12:27:48 +00:00
|
|
|
func (m *Messenger) SendEmojiReaction(ctx context.Context, chatID, messageID string, emojiID protobuf.EmojiReaction_Type) (*MessengerResponse, error) {
|
2020-07-20 17:06:38 +00:00
|
|
|
var response MessengerResponse
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(chatID)
|
2020-07-20 17:06:38 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, ErrChatNotFound
|
|
|
|
}
|
|
|
|
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
|
|
|
|
2020-07-26 22:06:58 +00:00
|
|
|
emojiR := &EmojiReaction{
|
|
|
|
EmojiReaction: protobuf.EmojiReaction{
|
|
|
|
Clock: clock,
|
|
|
|
MessageId: messageID,
|
|
|
|
ChatId: chatID,
|
2020-07-28 08:02:51 +00:00
|
|
|
Type: emojiID,
|
2020-07-26 22:06:58 +00:00
|
|
|
},
|
2020-07-28 07:53:32 +00:00
|
|
|
LocalChatID: chatID,
|
|
|
|
From: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)),
|
2020-07-20 17:06:38 +00:00
|
|
|
}
|
2020-07-26 22:06:58 +00:00
|
|
|
encodedMessage, err := m.encodeChatEntity(chat, emojiR)
|
2020-07-20 17:06:38 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-28 13:26:34 +00:00
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
2021-01-15 17:47:30 +00:00
|
|
|
LocalChatID: chatID,
|
|
|
|
Payload: encodedMessage,
|
|
|
|
SkipGroupMessageWrap: true,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
|
2020-07-27 12:27:48 +00:00
|
|
|
// Don't resend using datasync, that would create quite a lot
|
|
|
|
// of traffic if clicking too eagelry
|
|
|
|
ResendAutomatically: false,
|
2020-07-20 17:06:38 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
response.EmojiReactions = []*EmojiReaction{emojiR}
|
2021-01-11 10:32:51 +00:00
|
|
|
response.AddChat(chat)
|
2020-07-20 17:06:38 +00:00
|
|
|
|
2020-07-21 23:42:55 +00:00
|
|
|
err = m.persistence.SaveEmojiReaction(emojiR)
|
|
|
|
if err != nil {
|
2020-12-15 14:43:41 +00:00
|
|
|
return nil, errors.Wrap(err, "Can't save emoji reaction in db")
|
2020-07-21 23:42:55 +00:00
|
|
|
}
|
2020-07-20 17:06:38 +00:00
|
|
|
|
|
|
|
return &response, nil
|
|
|
|
}
|
|
|
|
|
2020-07-27 15:57:01 +00:00
|
|
|
func (m *Messenger) EmojiReactionsByChatID(chatID string, cursor string, limit int) ([]*EmojiReaction, error) {
|
2020-11-16 11:54:39 +00:00
|
|
|
chat, err := m.persistence.Chat(chatID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if chat.Timeline() {
|
|
|
|
var chatIDs = []string{"@" + contactIDFromPublicKey(&m.identity.PublicKey)}
|
2021-03-29 15:41:30 +00:00
|
|
|
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
2021-10-01 14:50:16 +00:00
|
|
|
if contact.Added {
|
2020-11-16 11:54:39 +00:00
|
|
|
chatIDs = append(chatIDs, "@"+contact.ID)
|
|
|
|
}
|
2021-03-29 15:41:30 +00:00
|
|
|
return true
|
|
|
|
})
|
2020-11-16 11:54:39 +00:00
|
|
|
return m.persistence.EmojiReactionsByChatIDs(chatIDs, cursor, limit)
|
|
|
|
}
|
2020-07-27 15:57:01 +00:00
|
|
|
return m.persistence.EmojiReactionsByChatID(chatID, cursor, limit)
|
|
|
|
}
|
|
|
|
|
2021-11-05 13:48:20 +00:00
|
|
|
func (m *Messenger) EmojiReactionsByChatIDMessageID(chatID string, messageID string) ([]*EmojiReaction, error) {
|
|
|
|
_, err := m.persistence.Chat(chatID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m.persistence.EmojiReactionsByChatIDMessageID(chatID, messageID)
|
|
|
|
}
|
|
|
|
|
2020-07-22 00:38:01 +00:00
|
|
|
func (m *Messenger) SendEmojiReactionRetraction(ctx context.Context, emojiReactionID string) (*MessengerResponse, error) {
|
2020-07-27 12:27:48 +00:00
|
|
|
emojiR, err := m.persistence.EmojiReactionByID(emojiReactionID)
|
2020-07-22 00:38:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-22 00:57:44 +00:00
|
|
|
// Check that the sender is the key owner
|
|
|
|
pk := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))
|
2020-07-27 12:27:48 +00:00
|
|
|
if emojiR.From != pk {
|
2020-07-22 14:54:24 +00:00
|
|
|
return nil, errors.Errorf("identity mismatch, "+
|
|
|
|
"emoji reactions can only be retracted by the reaction sender, "+
|
2020-07-22 00:57:44 +00:00
|
|
|
"emoji reaction sent by '%s', current identity '%s'",
|
2020-07-27 12:27:48 +00:00
|
|
|
emojiR.From, pk,
|
2020-07-22 00:57:44 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get chat and clock
|
2021-03-29 15:41:30 +00:00
|
|
|
chat, ok := m.allChats.Load(emojiR.GetChatId())
|
2020-07-22 00:38:01 +00:00
|
|
|
if !ok {
|
|
|
|
return nil, ErrChatNotFound
|
|
|
|
}
|
|
|
|
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
|
|
|
|
2020-07-27 12:27:48 +00:00
|
|
|
// Update the relevant fields
|
|
|
|
emojiR.Clock = clock
|
|
|
|
emojiR.Retracted = true
|
|
|
|
|
|
|
|
encodedMessage, err := m.encodeChatEntity(chat, emojiR)
|
2020-07-22 00:38:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the marshalled EmojiReactionRetraction protobuf
|
2020-07-28 13:26:34 +00:00
|
|
|
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
2021-01-15 17:47:30 +00:00
|
|
|
LocalChatID: emojiR.GetChatId(),
|
|
|
|
Payload: encodedMessage,
|
|
|
|
SkipGroupMessageWrap: true,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
|
2020-07-27 12:27:48 +00:00
|
|
|
// Don't resend using datasync, that would create quite a lot
|
|
|
|
// of traffic if clicking too eagelry
|
|
|
|
ResendAutomatically: false,
|
2020-07-22 00:38:01 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-07-22 00:57:44 +00:00
|
|
|
// Update MessengerResponse
|
2020-07-22 00:38:01 +00:00
|
|
|
response := MessengerResponse{}
|
2020-07-27 12:27:48 +00:00
|
|
|
emojiR.Retracted = true
|
|
|
|
response.EmojiReactions = []*EmojiReaction{emojiR}
|
2021-01-11 10:32:51 +00:00
|
|
|
response.AddChat(chat)
|
2020-07-22 00:38:01 +00:00
|
|
|
|
2020-07-22 00:57:44 +00:00
|
|
|
// Persist retraction state for emoji reaction
|
2020-07-28 07:53:32 +00:00
|
|
|
err = m.persistence.SaveEmojiReaction(emojiR)
|
2020-07-22 00:43:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-22 00:38:01 +00:00
|
|
|
|
|
|
|
return &response, nil
|
2020-07-20 17:06:38 +00:00
|
|
|
}
|
2020-07-25 16:13:08 +00:00
|
|
|
|
2020-07-27 10:13:22 +00:00
|
|
|
func (m *Messenger) encodeChatEntity(chat *Chat, message common.ChatEntity) ([]byte, error) {
|
2020-07-25 16:13:08 +00:00
|
|
|
var encodedMessage []byte
|
|
|
|
var err error
|
2020-07-26 22:10:08 +00:00
|
|
|
l := m.logger.With(zap.String("site", "Send"), zap.String("chatID", chat.ID))
|
2020-07-25 16:13:08 +00:00
|
|
|
|
|
|
|
switch chat.ChatType {
|
|
|
|
case ChatTypeOneToOne:
|
2020-07-26 22:10:08 +00:00
|
|
|
l.Debug("sending private message")
|
2020-07-25 16:13:08 +00:00
|
|
|
message.SetMessageType(protobuf.MessageType_ONE_TO_ONE)
|
2020-07-26 22:06:58 +00:00
|
|
|
encodedMessage, err = proto.Marshal(message.GetProtobuf())
|
2020-07-25 16:13:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-01 12:02:18 +00:00
|
|
|
|
2020-10-20 15:10:28 +00:00
|
|
|
case ChatTypePublic, ChatTypeProfile:
|
2020-07-26 22:10:08 +00:00
|
|
|
l.Debug("sending public message", zap.String("chatName", chat.Name))
|
2020-07-25 16:13:08 +00:00
|
|
|
message.SetMessageType(protobuf.MessageType_PUBLIC_GROUP)
|
2020-07-26 22:06:58 +00:00
|
|
|
encodedMessage, err = proto.Marshal(message.GetProtobuf())
|
2020-07-25 16:13:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-01 12:02:18 +00:00
|
|
|
|
2020-11-18 09:16:51 +00:00
|
|
|
case ChatTypeCommunityChat:
|
|
|
|
l.Debug("sending community chat message", zap.String("chatName", chat.Name))
|
|
|
|
// TODO: add grant
|
|
|
|
message.SetMessageType(protobuf.MessageType_COMMUNITY_CHAT)
|
|
|
|
encodedMessage, err = proto.Marshal(message.GetProtobuf())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-01 12:02:18 +00:00
|
|
|
|
2020-07-25 16:13:08 +00:00
|
|
|
case ChatTypePrivateGroupChat:
|
|
|
|
message.SetMessageType(protobuf.MessageType_PRIVATE_GROUP)
|
2020-07-26 22:10:08 +00:00
|
|
|
l.Debug("sending group message", zap.String("chatName", chat.Name))
|
2021-01-15 17:47:30 +00:00
|
|
|
if !message.WrapGroupMessage() {
|
|
|
|
encodedMessage, err = proto.Marshal(message.GetProtobuf())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
2020-07-25 16:13:08 +00:00
|
|
|
|
2021-01-15 17:47:30 +00:00
|
|
|
group, err := newProtocolGroupFromChat(chat)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-25 16:13:08 +00:00
|
|
|
|
2022-11-18 10:04:38 +00:00
|
|
|
// NOTE(cammellos): Disabling for now since the optimiziation is not
|
|
|
|
// applicable anymore after we changed group rules to allow
|
|
|
|
// anyone to change group details
|
|
|
|
encodedMessage, err = m.sender.EncodeMembershipUpdate(group, message)
|
2021-01-15 17:47:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-25 16:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return nil, errors.New("chat type not supported")
|
|
|
|
}
|
|
|
|
|
|
|
|
return encodedMessage, nil
|
|
|
|
}
|
2021-02-23 07:37:08 +00:00
|
|
|
|
|
|
|
func (m *Messenger) getOrBuildContactFromMessage(msg *common.Message) (*Contact, error) {
|
2021-03-29 15:41:30 +00:00
|
|
|
if c, ok := m.allContacts.Load(msg.From); ok {
|
2021-02-23 07:37:08 +00:00
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
senderPubKey, err := msg.GetSenderPubKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
senderID := contactIDFromPublicKey(senderPubKey)
|
|
|
|
c, err := buildContact(senderID, senderPubKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-03-29 15:41:30 +00:00
|
|
|
// TODO(samyoul) remove storing of an updated reference pointer?
|
|
|
|
m.allContacts.Store(msg.From, c)
|
2021-02-23 07:37:08 +00:00
|
|
|
return c, nil
|
|
|
|
}
|
2021-08-26 20:25:43 +00:00
|
|
|
|
|
|
|
func (m *Messenger) BloomFilter() []byte {
|
|
|
|
return m.transport.BloomFilter()
|
|
|
|
}
|
2021-02-09 13:58:21 +00:00
|
|
|
|
2022-03-23 18:47:00 +00:00
|
|
|
func (m *Messenger) getSettings() (settings.Settings, error) {
|
|
|
|
sDB, err := accounts.NewDB(m.database)
|
|
|
|
if err != nil {
|
|
|
|
return settings.Settings{}, err
|
|
|
|
}
|
2021-02-09 13:58:21 +00:00
|
|
|
return sDB.GetSettings()
|
|
|
|
}
|
2022-02-10 10:00:59 +00:00
|
|
|
|
2022-01-17 03:42:11 +00:00
|
|
|
func (m *Messenger) handleSyncBookmark(state *ReceivedMessageState, message protobuf.SyncBookmark) error {
|
|
|
|
bookmark := &browsers.Bookmark{
|
|
|
|
URL: message.Url,
|
|
|
|
Name: message.Name,
|
|
|
|
ImageURL: message.ImageUrl,
|
|
|
|
Removed: message.Removed,
|
|
|
|
Clock: message.Clock,
|
|
|
|
}
|
|
|
|
state.AllBookmarks[message.Url] = bookmark
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-02-10 10:00:59 +00:00
|
|
|
func (m *Messenger) handleSyncClearHistory(state *ReceivedMessageState, message protobuf.SyncClearHistory) error {
|
|
|
|
chatID := message.ChatId
|
|
|
|
existingChat, ok := state.AllChats.Load(chatID)
|
|
|
|
if !ok {
|
|
|
|
return ErrChatNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
if existingChat.DeletedAtClockValue >= message.ClearedAt {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err := m.persistence.ClearHistoryFromSyncMessage(existingChat, message.ClearedAt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if existingChat.Public() {
|
|
|
|
err = m.transport.ClearProcessedMessageIDsCache()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
state.AllChats.Store(chatID, existingChat)
|
|
|
|
state.Response.AddChat(existingChat)
|
|
|
|
state.Response.AddClearedHistory(&ClearedHistory{
|
|
|
|
ClearedAt: message.ClearedAt,
|
|
|
|
ChatID: chatID,
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
2022-07-05 19:49:44 +00:00
|
|
|
|
|
|
|
func (m *Messenger) handleSyncTrustedUser(state *ReceivedMessageState, message protobuf.SyncTrustedUser) error {
|
|
|
|
updated, err := m.verificationDatabase.UpsertTrustStatus(message.Id, verification.TrustStatus(message.Status), message.Clock)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if updated {
|
|
|
|
state.AllTrustStatus[message.Id] = verification.TrustStatus(message.Status)
|
|
|
|
|
|
|
|
contact, ok := m.allContacts.Load(message.Id)
|
|
|
|
if !ok {
|
|
|
|
m.logger.Info("contact not found")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
contact.TrustStatus = verification.TrustStatus(message.Status)
|
|
|
|
m.allContacts.Store(contact.ID, contact)
|
|
|
|
state.ModifiedContacts.Store(contact.ID, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ToVerificationRequest(message protobuf.SyncVerificationRequest) *verification.Request {
|
|
|
|
return &verification.Request{
|
|
|
|
From: message.From,
|
|
|
|
To: message.To,
|
|
|
|
Challenge: message.Challenge,
|
|
|
|
Response: message.Response,
|
|
|
|
RequestedAt: message.RequestedAt,
|
|
|
|
RepliedAt: message.RepliedAt,
|
|
|
|
RequestStatus: verification.RequestStatus(message.VerificationStatus),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Messenger) handleSyncVerificationRequest(state *ReceivedMessageState, message protobuf.SyncVerificationRequest) error {
|
|
|
|
verificationRequest := ToVerificationRequest(message)
|
|
|
|
|
2022-08-31 14:41:58 +00:00
|
|
|
err := m.verificationDatabase.SaveVerificationRequest(verificationRequest)
|
2022-07-05 19:49:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
myPubKey := hexutil.Encode(crypto.FromECDSAPub(&m.identity.PublicKey))
|
|
|
|
|
|
|
|
state.AllVerificationRequests = append(state.AllVerificationRequests, verificationRequest)
|
|
|
|
|
|
|
|
if message.From == myPubKey { // Verification requests we sent
|
|
|
|
contact, ok := m.allContacts.Load(message.To)
|
|
|
|
if !ok {
|
|
|
|
m.logger.Info("contact not found")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
contact.VerificationStatus = VerificationStatus(message.VerificationStatus)
|
|
|
|
if err := m.persistence.SaveContact(contact, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
m.allContacts.Store(contact.ID, contact)
|
|
|
|
state.ModifiedContacts.Store(contact.ID, true)
|
|
|
|
|
|
|
|
// TODO: create activity center notif
|
|
|
|
|
|
|
|
}
|
|
|
|
// else { // Verification requests we received
|
|
|
|
// // TODO: activity center notif
|
|
|
|
//}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-09-29 11:50:23 +00:00
|
|
|
|
|
|
|
func chunkSlice[T comparable](slice []T, chunkSize int) [][]T {
|
|
|
|
var chunks [][]T
|
|
|
|
for i := 0; i < len(slice); i += chunkSize {
|
|
|
|
end := i + chunkSize
|
|
|
|
|
|
|
|
// necessary check to avoid slicing beyond
|
|
|
|
// slice capacity
|
|
|
|
if end > len(slice) {
|
|
|
|
end = len(slice)
|
|
|
|
}
|
|
|
|
|
|
|
|
chunks = append(chunks, slice[i:end])
|
|
|
|
}
|
|
|
|
|
|
|
|
return chunks
|
|
|
|
}
|
|
|
|
|
|
|
|
func chunkAttachmentsByByteSize(slice []*protobuf.DiscordMessageAttachment, maxFileSizeBytes uint64) [][]*protobuf.DiscordMessageAttachment {
|
|
|
|
var chunks [][]*protobuf.DiscordMessageAttachment
|
|
|
|
|
|
|
|
currentChunkSize := uint64(0)
|
|
|
|
currentChunk := make([]*protobuf.DiscordMessageAttachment, 0)
|
|
|
|
|
|
|
|
for i, attachment := range slice {
|
|
|
|
payloadBytes := attachment.GetFileSizeBytes()
|
|
|
|
if currentChunkSize+payloadBytes > maxFileSizeBytes && len(currentChunk) > 0 {
|
|
|
|
chunks = append(chunks, currentChunk)
|
|
|
|
currentChunk = make([]*protobuf.DiscordMessageAttachment, 0)
|
|
|
|
currentChunkSize = uint64(0)
|
|
|
|
}
|
|
|
|
currentChunk = append(currentChunk, attachment)
|
|
|
|
currentChunkSize = currentChunkSize + payloadBytes
|
|
|
|
if i == len(slice)-1 {
|
|
|
|
chunks = append(chunks, currentChunk)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return chunks
|
|
|
|
}
|