From 7d5d413222951840e3c98b06434491a04ed78b73 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 1 Sep 2020 12:34:28 +0200 Subject: [PATCH 1/9] Add parsing and storing of mentions --- protocol/message.go | 29 +++++++++++ protocol/message_persistence.go | 21 ++++++++ protocol/message_test.go | 20 +++++++ protocol/migrations/migrations.go | 52 ++++++++++++++++++- .../sqlite/1598955122_add_mentions.down.sql | 0 .../sqlite/1598955122_add_mentions.up.sql | 1 + protocol/persistence_test.go | 32 ++++++++++++ 7 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 protocol/migrations/sqlite/1598955122_add_mentions.down.sql create mode 100644 protocol/migrations/sqlite/1598955122_add_mentions.up.sql diff --git a/protocol/message.go b/protocol/message.go index 677bc8418..68ab1afd2 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -12,6 +12,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/status-im/markdown" + "github.com/status-im/markdown/ast" "github.com/status-im/status-go/protocol/protobuf" ) @@ -117,6 +118,9 @@ type Message struct { // that has been updated Replace string `json:"replace,omitempty"` SigPubKey *ecdsa.PublicKey `json:"-"` + + // Mentions is an array of mentions for a given message + Mentions []string } func (m *Message) MarshalJSON() ([]byte, error) { @@ -151,6 +155,7 @@ func (m *Message) MarshalJSON() ([]byte, error) { Timestamp uint64 `json:"timestamp"` ContentType protobuf.ChatMessage_ContentType `json:"contentType"` MessageType protobuf.MessageType `json:"messageType"` + Mentions []string `json:"mentions,omitempty"` }{ ID: m.ID, WhisperTimestamp: m.WhisperTimestamp, @@ -174,6 +179,7 @@ func (m *Message) MarshalJSON() ([]byte, error) { Audio: m.Base64Audio, Timestamp: m.Timestamp, ContentType: m.ContentType, + Mentions: m.Mentions, MessageType: m.MessageType, CommandParameters: m.CommandParameters, } @@ -295,10 +301,33 @@ func (m *Message) parseAudio() error { return nil } +type MentionNodeVisitor struct { + mentions []string +} + +func (v *MentionNodeVisitor) Visit(node ast.Node, entering bool) ast.WalkStatus { + // only on entering we fetch, otherwise we go on + if !entering { + return ast.GoToNext + } + switch n := node.(type) { + case *ast.Mention: + v.mentions = append(v.mentions, string(n.Literal)) + } + return ast.GoToNext +} + +func extractMentions(parsedText ast.Node) []string { + visitor := &MentionNodeVisitor{} + ast.Walk(parsedText, visitor) + return visitor.mentions +} + // PrepareContent return the parsed content of the message, the line-count and whether // is a right-to-left message func (m *Message) PrepareContent() error { parsedText := markdown.Parse([]byte(m.Text), nil) + m.Mentions = extractMentions(parsedText) jsonParsedText, err := json.Marshal(parsedText) if err != nil { return err diff --git a/protocol/message_persistence.go b/protocol/message_persistence.go index dc0bdc8fb..639de482e 100644 --- a/protocol/message_persistence.go +++ b/protocol/message_persistence.go @@ -40,6 +40,7 @@ func (db sqlitePersistence) tableUserMessagesAllFields() string { audio_type, audio_duration_ms, audio_base64, + mentions, command_id, command_value, command_from, @@ -74,6 +75,7 @@ func (db sqlitePersistence) tableUserMessagesAllFieldsJoin() string { m1.image_base64, COALESCE(m1.audio_duration_ms,0), m1.audio_base64, + m1.mentions, m1.command_id, m1.command_value, m1.command_from, @@ -111,6 +113,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message var quotedImage sql.NullString var quotedAudio sql.NullString var quotedAudioDuration sql.NullInt64 + var serializedMentions []byte var alias sql.NullString var identicon sql.NullString @@ -138,6 +141,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message &message.Base64Image, &audio.DurationMs, &message.Base64Audio, + &serializedMentions, &command.ID, &command.Value, &command.From, @@ -177,6 +181,13 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message message.Alias = alias.String message.Identicon = identicon.String + if serializedMentions != nil { + err := json.Unmarshal(serializedMentions, &message.Mentions) + if err != nil { + return err + } + } + switch message.ContentType { case protobuf.ChatMessage_STICKER: message.Payload = &protobuf.ChatMessage_Sticker{Sticker: sticker} @@ -211,6 +222,15 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte if command == nil { command = &CommandParameters{} } + + var serializedMentions []byte + var err error + if len(message.Mentions) != 0 { + serializedMentions, err = json.Marshal(message.Mentions) + if err != nil { + return nil, err + } + } return []interface{}{ message.ID, message.WhisperTimestamp, @@ -235,6 +255,7 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte audio.Type, audio.DurationMs, message.Base64Audio, + serializedMentions, command.ID, command.Value, command.From, diff --git a/protocol/message_test.go b/protocol/message_test.go index b8dbe80fd..ad8537801 100644 --- a/protocol/message_test.go +++ b/protocol/message_test.go @@ -7,6 +7,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/protobuf" ) @@ -94,3 +96,21 @@ func TestGetAudioMessageMIME(t *testing.T) { _, err = getImageMessageMIME(unknown) require.Error(t, err) } + +func TestPrepareContentMentions(t *testing.T) { + message := &Message{} + pk1, err := crypto.GenerateKey() + require.NoError(t, err) + pk1String := types.EncodeHex(crypto.FromECDSAPub(&pk1.PublicKey)) + + pk2, err := crypto.GenerateKey() + require.NoError(t, err) + pk2String := types.EncodeHex(crypto.FromECDSAPub(&pk2.PublicKey)) + + message.Text = "hey @" + pk1String + " @" + pk2String + + require.NoError(t, message.PrepareContent()) + require.Len(t, message.Mentions, 2) + require.Equal(t, message.Mentions[0], pk1String) + require.Equal(t, message.Mentions[1], pk2String) +} diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index 59950bcf2..66102594f 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -22,6 +22,8 @@ // 1596805115_create_group_chat_invitations_table.up.sql (231B) // 1597322655_add_invitation_admin_chat_field.up.sql (54B) // 1597757544_add_nickname.up.sql (52B) +// 1598955122_add_mentions.down.sql (0) +// 1598955122_add_mentions.up.sql (52B) // 1599641390_add_emoji_reactions_index.down.sql (67B) // 1599641390_add_emoji_reactions_index.up.sql (126B) // doc.go (850B) @@ -533,6 +535,46 @@ func _1597757544_add_nicknameUpSql() (*asset, error) { return a, nil } +var __1598955122_add_mentionsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00") + +func _1598955122_add_mentionsDownSqlBytes() ([]byte, error) { + return bindataRead( + __1598955122_add_mentionsDownSql, + "1598955122_add_mentions.down.sql", + ) +} + +func _1598955122_add_mentionsDownSql() (*asset, error) { + bytes, err := _1598955122_add_mentionsDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1598955122_add_mentions.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1599679300, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} + return a, nil +} + +var __1598955122_add_mentionsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x28\x2d\x4e\x2d\x8a\xcf\x4d\x2d\x2e\x4e\x4c\x4f\x2d\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\xc8\x4d\xcd\x2b\xc9\xcc\xcf\x2b\x56\x70\xf2\xf1\x77\xb2\xe6\x02\x04\x00\x00\xff\xff\xca\xf8\x74\x41\x34\x00\x00\x00") + +func _1598955122_add_mentionsUpSqlBytes() ([]byte, error) { + return bindataRead( + __1598955122_add_mentionsUpSql, + "1598955122_add_mentions.up.sql", + ) +} + +func _1598955122_add_mentionsUpSql() (*asset, error) { + bytes, err := _1598955122_add_mentionsUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1598955122_add_mentions.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1599679300, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8d, 0x22, 0x17, 0x92, 0xd2, 0x11, 0x4e, 0x7, 0x93, 0x9a, 0x55, 0xfd, 0xb, 0x97, 0xc4, 0x63, 0x6a, 0x81, 0x97, 0xcd, 0xb2, 0xf8, 0x4b, 0x5f, 0x3c, 0xfa, 0x3a, 0x38, 0x53, 0x10, 0xed, 0x9d}} + return a, nil +} + var __1599641390_add_emoji_reactions_indexDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\xf0\xf4\x73\x71\x8d\x50\x48\xcd\xcd\xcf\xca\x8c\x2f\x4a\x4d\x4c\x2e\xc9\xcc\xcf\x2b\x8e\xcf\x4d\x2d\x2e\x4e\x4c\x4f\x8d\xcf\x4c\x89\xcf\xc9\x4f\x4e\xcc\x89\x4f\xce\x48\x2c\x01\xf1\x8a\x52\x4b\x8a\x12\x93\x4b\x52\x53\xe2\x33\x53\x2a\xac\xb9\x00\x01\x00\x00\xff\xff\xb2\x85\x84\xa0\x43\x00\x00\x00") func _1599641390_add_emoji_reactions_indexDownSqlBytes() ([]byte, error) { @@ -548,7 +590,7 @@ func _1599641390_add_emoji_reactions_indexDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.down.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1599641440, 0)} + info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.down.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1599679300, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x89, 0x39, 0x0, 0x51, 0x5b, 0x48, 0xc3, 0xf3, 0x6a, 0x96, 0xf1, 0xd2, 0xa6, 0x60, 0xa8, 0x68, 0x21, 0xb5, 0xa0, 0x11, 0x11, 0x99, 0xde, 0xad, 0xa6, 0xa7, 0x56, 0xc1, 0xb2, 0xa6, 0x63, 0xe4}} return a, nil } @@ -568,7 +610,7 @@ func _1599641390_add_emoji_reactions_indexUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.up.sql", size: 126, mode: os.FileMode(0644), modTime: time.Unix(1599641420, 0)} + info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.up.sql", size: 126, mode: os.FileMode(0644), modTime: time.Unix(1599679300, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf9, 0xd8, 0xdc, 0xa7, 0xb, 0x92, 0x7a, 0x61, 0x37, 0x24, 0x1c, 0x77, 0x5e, 0xe, 0x7e, 0xfc, 0x9f, 0x98, 0x7b, 0x65, 0xe7, 0xf9, 0x71, 0x57, 0x89, 0x2d, 0x90, 0x1b, 0xf6, 0x5e, 0x37, 0xe8}} return a, nil } @@ -728,6 +770,10 @@ var _bindata = map[string]func() (*asset, error){ "1597757544_add_nickname.up.sql": _1597757544_add_nicknameUpSql, + "1598955122_add_mentions.down.sql": _1598955122_add_mentionsDownSql, + + "1598955122_add_mentions.up.sql": _1598955122_add_mentionsUpSql, + "1599641390_add_emoji_reactions_index.down.sql": _1599641390_add_emoji_reactions_indexDownSql, "1599641390_add_emoji_reactions_index.up.sql": _1599641390_add_emoji_reactions_indexUpSql, @@ -798,6 +844,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "1596805115_create_group_chat_invitations_table.up.sql": &bintree{_1596805115_create_group_chat_invitations_tableUpSql, map[string]*bintree{}}, "1597322655_add_invitation_admin_chat_field.up.sql": &bintree{_1597322655_add_invitation_admin_chat_fieldUpSql, map[string]*bintree{}}, "1597757544_add_nickname.up.sql": &bintree{_1597757544_add_nicknameUpSql, map[string]*bintree{}}, + "1598955122_add_mentions.down.sql": &bintree{_1598955122_add_mentionsDownSql, map[string]*bintree{}}, + "1598955122_add_mentions.up.sql": &bintree{_1598955122_add_mentionsUpSql, map[string]*bintree{}}, "1599641390_add_emoji_reactions_index.down.sql": &bintree{_1599641390_add_emoji_reactions_indexDownSql, map[string]*bintree{}}, "1599641390_add_emoji_reactions_index.up.sql": &bintree{_1599641390_add_emoji_reactions_indexUpSql, map[string]*bintree{}}, "doc.go": &bintree{docGo, map[string]*bintree{}}, diff --git a/protocol/migrations/sqlite/1598955122_add_mentions.down.sql b/protocol/migrations/sqlite/1598955122_add_mentions.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/protocol/migrations/sqlite/1598955122_add_mentions.up.sql b/protocol/migrations/sqlite/1598955122_add_mentions.up.sql new file mode 100644 index 000000000..0c776601a --- /dev/null +++ b/protocol/migrations/sqlite/1598955122_add_mentions.up.sql @@ -0,0 +1 @@ +ALTER TABLE user_messages ADD COLUMN mentions BLOB; diff --git a/protocol/persistence_test.go b/protocol/persistence_test.go index 7d4daacf7..76402d183 100644 --- a/protocol/persistence_test.go +++ b/protocol/persistence_test.go @@ -11,6 +11,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/sqlite" ) @@ -529,3 +531,33 @@ func TestSaveChat(t *testing.T) { require.NoError(t, err) require.Equal(t, &chat, retrievedChat) } + +func TestSaveMentions(t *testing.T) { + chatID := "chat-id" + db, err := openTestDB() + require.NoError(t, err) + p := sqlitePersistence{db: db} + + key, err := crypto.GenerateKey() + require.NoError(t, err) + + pkString := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)) + + message := Message{ + ID: "1", + LocalChatID: chatID, + ChatMessage: protobuf.ChatMessage{Text: "some-text"}, + From: "me", + Mentions: []string{pkString}, + } + + err = p.SaveMessages([]*Message{&message}) + require.NoError(t, err) + + retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10) + require.NoError(t, err) + require.Len(t, retrievedMessages, 1) + require.Len(t, retrievedMessages[0].Mentions, 1) + require.Equal(t, retrievedMessages[0].Mentions, message.Mentions) + +} From 00c5b60d7f0bc40f9fc4a2d63addc72ec19fc6e5 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 1 Sep 2020 15:27:01 +0200 Subject: [PATCH 2/9] Move message to common namespace --- protocol/chat.go | 7 +- protocol/chat_test.go | 14 +- protocol/{ => common}/message.go | 3 +- protocol/{ => common}/message_test.go | 6 +- protocol/group_chat_system_messages.go | 9 +- protocol/message_builder.go | 5 +- protocol/message_handler.go | 32 ++-- protocol/message_persistence.go | 35 ++--- protocol/messenger.go | 86 +++++------ protocol/messenger_test.go | 95 ++++++------ protocol/persistence.go | 4 +- protocol/persistence_test.go | 53 +++---- protocol/protobuf/push_notifications.pb.go | 170 ++++++++++++--------- protocol/protobuf/push_notifications.proto | 2 + protocol/pushnotificationclient/client.go | 4 + protocol/transaction_validator.go | 13 +- protocol/transaction_validator_test.go | 33 ++-- services/ext/api.go | 7 +- services/shhext/api_nimbus.go | 7 +- 19 files changed, 311 insertions(+), 274 deletions(-) rename protocol/{ => common}/message.go (99%) rename protocol/{ => common}/message_test.go (97%) diff --git a/protocol/chat.go b/protocol/chat.go index c470f14be..a0b902f44 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -8,6 +8,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" ) @@ -50,8 +51,8 @@ type Chat struct { DeletedAtClockValue uint64 `json:"deletedAtClockValue"` // Denormalized fields - UnviewedMessagesCount uint `json:"unviewedMessagesCount"` - LastMessage *Message `json:"lastMessage"` + UnviewedMessagesCount uint `json:"unviewedMessagesCount"` + LastMessage *common.Message `json:"lastMessage"` // Group chat fields // Members are the members who have been invited to the group chat @@ -172,7 +173,7 @@ func (c *Chat) NextClockAndTimestamp(timesource TimeSource) (uint64, uint64) { return clock, timestamp } -func (c *Chat) UpdateFromMessage(message *Message, timesource TimeSource) error { +func (c *Chat) UpdateFromMessage(message *common.Message, timesource TimeSource) error { c.Timestamp = int64(timesource.GetCurrentTime()) // If the clock of the last message is lower, we set the message diff --git a/protocol/chat_test.go b/protocol/chat_test.go index d8e41e11b..1261e8cde 100644 --- a/protocol/chat_test.go +++ b/protocol/chat_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/stretchr/testify/suite" + + "github.com/status-im/status-go/protocol/common" ) type ChatTestSuite struct { @@ -75,7 +77,7 @@ func (s *ChatTestSuite) TestValidateChat() { func (s *ChatTestSuite) TestUpdateFromMessage() { // Base case, clock is higher - message := &Message{} + message := &common.Message{} chat := &Chat{} message.Clock = 1 @@ -84,7 +86,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() { s.Require().Equal(uint64(1), chat.LastClockValue) // Clock is lower and lastMessage is not nil - message = &Message{} + message = &common.Message{} lastMessage := message chat = &Chat{LastClockValue: 2, LastMessage: lastMessage} @@ -94,7 +96,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() { s.Require().Equal(uint64(2), chat.LastClockValue) // Clock is lower and lastMessage is nil - message = &Message{} + message = &common.Message{} chat = &Chat{LastClockValue: 2} message.Clock = 1 @@ -103,7 +105,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() { s.Require().Equal(uint64(2), chat.LastClockValue) // Clock is higher but lastMessage has lower clock message then the receiving one - message = &Message{} + message = &common.Message{} chat = &Chat{LastClockValue: 2} message.Clock = 1 @@ -112,7 +114,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() { s.Require().Equal(uint64(2), chat.LastClockValue) chat.LastClockValue = 4 - message = &Message{} + message = &common.Message{} message.Clock = 3 s.Require().NoError(chat.UpdateFromMessage(message, &testTimeSource{})) s.Require().Equal(chat.LastMessage, message) @@ -122,7 +124,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() { func (s *ChatTestSuite) TestSerializeJSON() { - message := &Message{} + message := &common.Message{} chat := &Chat{} message.Clock = 1 diff --git a/protocol/message.go b/protocol/common/message.go similarity index 99% rename from protocol/message.go rename to protocol/common/message.go index 68ab1afd2..51a031f77 100644 --- a/protocol/message.go +++ b/protocol/common/message.go @@ -1,4 +1,4 @@ -package protocol +package common import ( "crypto/ecdsa" @@ -11,6 +11,7 @@ import ( "unicode/utf8" "github.com/golang/protobuf/proto" + "github.com/status-im/markdown" "github.com/status-im/markdown/ast" diff --git a/protocol/message_test.go b/protocol/common/message_test.go similarity index 97% rename from protocol/message_test.go rename to protocol/common/message_test.go index ad8537801..0fa39e2d9 100644 --- a/protocol/message_test.go +++ b/protocol/common/message_test.go @@ -1,4 +1,4 @@ -package protocol +package common import ( "io/ioutil" @@ -16,7 +16,7 @@ const expectedJPEG = "data:image/jpeg;base64,/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQ const expectedAAC = "data:audio/aac;base64,//FQgBw//NoATGF2YzUyLjcwLjAAQniptokphEFCg5qs1v9fn48+qz1rfWNhwvz+CqB5dipmq3T2PlT1Ld6sPj+19fUt1C3NKV0KowiqohZVCrdf19WMatvV3YbIvAuy/q2RafA8UiZPmZY7DdmHZtP9ri25kedWSiMKQRt79ttlod55LkuX7/f7/f7/f7/YGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYHNqo8g5qs1v9fn48+qz1rfWNhwvz+CqAAAAAAAAAAAAAAAAAAAAAAABw//FQgCNf/CFXbUZfDKFRgsYlKDegtXJH9eLkT54uRM1ckDYDcXRzZGF6Kz5Yps5fTeLY6w7gclwly+0PJL3udY3PyekTFI65bdniF3OjvHeafzZfWTs0qRMSkdll1sbb4SNT5e8vX98ytot6jEZ0NhJi2pBVP/tKV2JMyo36n9uxR2tKR+FoLCsP4SVi49kmvaSCWm5bQD96OmVQA9Q40bqnOa7rT8j9N0TlK991XdcenGTLbyS6eUnN2U1ckf14uRPni5EzVyQAAAAAAAAAAx6Q1flBp+KH2LhgH2Xx+14QB2/jcizm6ngck4vB9DoH9/Vcb7E8Dy+D/1ii1pSPwsUUUXCSsXHsk17SBfKwn2uHr6QAAAAAAAHA//FQgBt//CF3VO1KFCFWcd/r04m+O0758/tXHUlvaqEK9lvhUZXEZMXKMV/LQ6B3/mOl/Mrfs6jpD2b7f+n4yt+tm2x5ZmnpD++dZo/V9VgblI3OW/s1b8qt0h1RBiIRIIYIYQIBeCM8yy7etkwt1JAajRSoZGwwNZ07TTFTyMR1mTUVVUTW97vaDaHU5DV1snBf0mN4fraa+rf/vpdZ8FxqatGjNxPh35UuVfpNqc48W4nZ6rOO/16cTfHad8+f2rjqS3tVAAAAAAAAAAAAAAAAAAAAAAAAAAAO//FQgBm//CEXVPU+GiFsPr7x6+N6v+m+q511I4SgtYVyoyWjcMWMxkaxxDGSx1qVcarjDESt8zLQehx/lkil/GrHBy/NfJcHek0XtfanZJLHNXO2rUnFklPAlQSBS4l0pIoXIfORcXx0UYj1nTsSe1/0wXDkkFCfxWHtqRayOmWm3oS6JGdnZdtjesjByefiS8dLW1tVVVC58ijoxN3gmGFYj07+YJ6eth9fePXxvV/031XOupHCUAAAAAAAAAAAAAAAAAAAAAAAAAAA4P/xUIAcf/whN1T9NsMOEK5rxxxxXnid+f0/Ia195vi6oGH1ZVr6kjqScdSF9lt3qXH+Lxf0fo/Oe53r99IUPzybv/YWGZ7Vgk31MGw+DMp05+3y9fPERUTHlt1c9sUyoqCaD5bdXVz2wkG0hnpDmFy8r0fr3VBn/C7Rmg+L0/45EWfdocGq3HQ1uRro0GJK+vsvo837NR82s01l/n97rsWn7RYNBM3WRcDY3cJKosqMJhgdHtj9yflthd65rxxxxXnid+f0/Ia195vi6oAAAAAAAAAAAAAAAAAAAAAAAAAAAABw" func TestPrepareContentImage(t *testing.T) { - file, err := os.Open("../_assets/tests/test.jpg") + file, err := os.Open("../../_assets/tests/test.jpg") require.NoError(t, err) defer file.Close() @@ -62,7 +62,7 @@ func TestGetImageMessageMIME(t *testing.T) { } func TestPrepareContentAudio(t *testing.T) { - file, err := os.Open("../_assets/tests/test.aac") + file, err := os.Open("../../_assets/tests/test.aac") require.NoError(t, err) defer file.Close() diff --git a/protocol/group_chat_system_messages.go b/protocol/group_chat_system_messages.go index 772cbe4ee..59f518757 100644 --- a/protocol/group_chat_system_messages.go +++ b/protocol/group_chat_system_messages.go @@ -6,6 +6,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" ) @@ -27,7 +28,7 @@ func tsprintf(format string, params map[string]string) string { return format } -func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) *Message { +func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) *common.Message { var text string switch e.Type { case protobuf.MembershipUpdateEvent_CHAT_CREATED: @@ -56,7 +57,7 @@ func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[p } timestamp := v1protocol.TimestampInMsFromTime(time.Now()) - message := &Message{ + message := &common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: e.ChatID, Text: text, @@ -75,8 +76,8 @@ func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[p return message } -func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*Message { - var messages []*Message +func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*common.Message { + var messages []*common.Message for _, e := range events { messages = append(messages, eventToSystemMessage(e, translations)) diff --git a/protocol/message_builder.go b/protocol/message_builder.go index e76018dce..16f2a3cb4 100644 --- a/protocol/message_builder.go +++ b/protocol/message_builder.go @@ -5,11 +5,12 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/identity/alias" "github.com/status-im/status-go/protocol/identity/identicon" ) -func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, timesource TimeSource) error { +func extendMessageFromChat(message *common.Message, chat *Chat, key *ecdsa.PublicKey, timesource TimeSource) error { clock, timestamp := chat.NextClockAndTimestamp(timesource) message.LocalChatID = chat.ID @@ -19,7 +20,7 @@ func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, t message.SigPubKey = key message.WhisperTimestamp = timestamp message.Seen = true - message.OutgoingStatus = OutgoingStatusSending + message.OutgoingStatus = common.OutgoingStatusSending identicon, err := identicon.GenerateBase64(message.From) if err != nil { diff --git a/protocol/message_handler.go b/protocol/message_handler.go index 682798788..67ec97c0f 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -145,7 +145,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta return nil } -func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, message *Message) error { +func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, message *common.Message) error { message.ID = state.CurrentMessageState.MessageID message.From = state.CurrentMessageState.Contact.ID message.Alias = state.CurrentMessageState.Contact.Alias @@ -182,7 +182,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa message.OutgoingStatus = "" } else { // Our own message, mark as sent - message.OutgoingStatus = OutgoingStatusSent + message.OutgoingStatus = common.OutgoingStatusSent } err = chat.UpdateFromMessage(message, state.Timesource) @@ -330,7 +330,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error { logger.Warn("failed to validate message", zap.Error(err)) return err } - receivedMessage := &Message{ + receivedMessage := &common.Message{ ID: state.CurrentMessageState.MessageID, ChatMessage: state.CurrentMessageState.Message, From: state.CurrentMessageState.Contact.ID, @@ -369,7 +369,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error { chat.UnviewedMessagesCount++ } else { // Our own message, mark as sent - receivedMessage.OutgoingStatus = OutgoingStatusSent + receivedMessage.OutgoingStatus = common.OutgoingStatusSent } err = chat.UpdateFromMessage(receivedMessage, state.Timesource) @@ -401,7 +401,7 @@ func (m *MessageHandler) HandleRequestAddressForTransaction(messageState *Receiv if err != nil { return err } - message := &Message{ + message := &common.Message{ ChatMessage: protobuf.ChatMessage{ Clock: command.Clock, Timestamp: messageState.CurrentMessageState.WhisperTimestamp, @@ -410,11 +410,11 @@ func (m *MessageHandler) HandleRequestAddressForTransaction(messageState *Receiv MessageType: protobuf.MessageType_ONE_TO_ONE, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, }, - CommandParameters: &CommandParameters{ + CommandParameters: &common.CommandParameters{ ID: messageState.CurrentMessageState.MessageID, Value: command.Value, Contract: command.Contract, - CommandState: CommandStateRequestAddressForTransaction, + CommandState: common.CommandStateRequestAddressForTransaction, }, } return m.handleCommandMessage(messageState, message) @@ -425,7 +425,7 @@ func (m *MessageHandler) HandleRequestTransaction(messageState *ReceivedMessageS if err != nil { return err } - message := &Message{ + message := &common.Message{ ChatMessage: protobuf.ChatMessage{ Clock: command.Clock, Timestamp: messageState.CurrentMessageState.WhisperTimestamp, @@ -434,11 +434,11 @@ func (m *MessageHandler) HandleRequestTransaction(messageState *ReceivedMessageS MessageType: protobuf.MessageType_ONE_TO_ONE, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, }, - CommandParameters: &CommandParameters{ + CommandParameters: &common.CommandParameters{ ID: messageState.CurrentMessageState.MessageID, Value: command.Value, Contract: command.Contract, - CommandState: CommandStateRequestTransaction, + CommandState: common.CommandStateRequestTransaction, Address: command.Address, }, } @@ -466,7 +466,7 @@ func (m *MessageHandler) HandleAcceptRequestAddressForTransaction(messageState * return errors.New("Initial message must originate from us") } - if initialMessage.CommandParameters.CommandState != CommandStateRequestAddressForTransaction { + if initialMessage.CommandParameters.CommandState != common.CommandStateRequestAddressForTransaction { return errors.New("Wrong state for command") } @@ -475,7 +475,7 @@ func (m *MessageHandler) HandleAcceptRequestAddressForTransaction(messageState * initialMessage.Text = requestAddressForTransactionAcceptedMessage initialMessage.CommandParameters.Address = command.Address initialMessage.Seen = false - initialMessage.CommandParameters.CommandState = CommandStateRequestAddressForTransactionAccepted + initialMessage.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionAccepted // Hide previous message previousMessage, err := m.persistence.MessageByCommandID(messageState.CurrentMessageState.Contact.ID, command.Id) @@ -536,7 +536,7 @@ func (m *MessageHandler) HandleDeclineRequestAddressForTransaction(messageState return errors.New("Initial message must originate from us") } - if oldMessage.CommandParameters.CommandState != CommandStateRequestAddressForTransaction { + if oldMessage.CommandParameters.CommandState != common.CommandStateRequestAddressForTransaction { return errors.New("Wrong state for command") } @@ -544,7 +544,7 @@ func (m *MessageHandler) HandleDeclineRequestAddressForTransaction(messageState oldMessage.Timestamp = messageState.CurrentMessageState.WhisperTimestamp oldMessage.Text = requestAddressForTransactionDeclinedMessage oldMessage.Seen = false - oldMessage.CommandParameters.CommandState = CommandStateRequestAddressForTransactionDeclined + oldMessage.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionDeclined // Hide previous message err = m.persistence.HideMessage(command.Id) @@ -577,7 +577,7 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM return errors.New("Initial message must originate from us") } - if oldMessage.CommandParameters.CommandState != CommandStateRequestTransaction { + if oldMessage.CommandParameters.CommandState != common.CommandStateRequestTransaction { return errors.New("Wrong state for command") } @@ -585,7 +585,7 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM oldMessage.Timestamp = messageState.CurrentMessageState.WhisperTimestamp oldMessage.Text = transactionRequestDeclinedMessage oldMessage.Seen = false - oldMessage.CommandParameters.CommandState = CommandStateRequestTransactionDeclined + oldMessage.CommandParameters.CommandState = common.CommandStateRequestTransactionDeclined // Hide previous message err = m.persistence.HideMessage(command.Id) diff --git a/protocol/message_persistence.go b/protocol/message_persistence.go index 639de482e..04eaa5178 100644 --- a/protocol/message_persistence.go +++ b/protocol/message_persistence.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/pkg/errors" @@ -106,7 +107,7 @@ type scanner interface { Scan(dest ...interface{}) error } -func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message *Message, others ...interface{}) error { +func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message *common.Message, others ...interface{}) error { var quotedText sql.NullString var quotedParsedText []byte var quotedFrom sql.NullString @@ -118,7 +119,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message var identicon sql.NullString sticker := &protobuf.StickerMessage{} - command := &CommandParameters{} + command := &common.CommandParameters{} audio := &protobuf.AudioMessage{} args := []interface{}{ @@ -169,7 +170,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message } if quotedText.Valid { - message.QuotedMessage = &QuotedMessage{ + message.QuotedMessage = &common.QuotedMessage{ From: quotedFrom.String, Text: quotedText.String, ParsedText: quotedParsedText, @@ -202,7 +203,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message return nil } -func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]interface{}, error) { +func (db sqlitePersistence) tableUserMessagesAllValues(message *common.Message) ([]interface{}, error) { sticker := message.GetSticker() if sticker == nil { sticker = &protobuf.StickerMessage{} @@ -220,7 +221,7 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte command := message.CommandParameters if command == nil { - command = &CommandParameters{} + command = &common.CommandParameters{} } var serializedMentions []byte @@ -271,7 +272,7 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte }, nil } -func (db sqlitePersistence) messageByID(tx *sql.Tx, id string) (*Message, error) { +func (db sqlitePersistence) messageByID(tx *sql.Tx, id string) (*common.Message, error) { var err error if tx == nil { tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) @@ -288,7 +289,7 @@ func (db sqlitePersistence) messageByID(tx *sql.Tx, id string) (*Message, error) }() } - var message Message + var message common.Message allFields := db.tableUserMessagesAllFieldsJoin() row := tx.QueryRow( @@ -322,9 +323,9 @@ func (db sqlitePersistence) messageByID(tx *sql.Tx, id string) (*Message, error) } } -func (db sqlitePersistence) MessageByCommandID(chatID, id string) (*Message, error) { +func (db sqlitePersistence) MessageByCommandID(chatID, id string) (*common.Message, error) { - var message Message + var message common.Message allFields := db.tableUserMessagesAllFieldsJoin() row := db.db.QueryRow( @@ -363,7 +364,7 @@ func (db sqlitePersistence) MessageByCommandID(chatID, id string) (*Message, err } } -func (db sqlitePersistence) MessageByID(id string) (*Message, error) { +func (db sqlitePersistence) MessageByID(id string) (*common.Message, error) { return db.messageByID(nil, id) } @@ -398,7 +399,7 @@ func (db sqlitePersistence) MessagesExist(ids []string) (map[string]bool, error) return result, nil } -func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*Message, error) { +func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*common.Message, error) { if len(ids) == 0 { return nil, nil } @@ -433,9 +434,9 @@ func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*Message, error) { } defer rows.Close() - var result []*Message + var result []*common.Message for rows.Next() { - var message Message + var message common.Message if err := db.tableUserMessagesScanAllFields(rows, &message); err != nil { return nil, err } @@ -448,7 +449,7 @@ func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*Message, error) { // MessageByChatID returns all messages for a given chatID in descending order. // Ordering is accomplished using two concatenated values: ClockValue and ID. // These two values are also used to compose a cursor which is returned to the result. -func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, limit int) ([]*Message, string, error) { +func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, limit int) ([]*common.Message, string, error) { cursorWhere := "" if currCursor != "" { cursorWhere = "AND cursor <= ?" @@ -491,12 +492,12 @@ func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, li defer rows.Close() var ( - result []*Message + result []*common.Message cursors []string ) for rows.Next() { var ( - message Message + message common.Message cursor string ) if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil { @@ -587,7 +588,7 @@ func (db sqlitePersistence) EmojiReactionsByChatID(chatID string, currCursor str return result, nil } -func (db sqlitePersistence) SaveMessages(messages []*Message) (err error) { +func (db sqlitePersistence) SaveMessages(messages []*common.Message) (err error) { tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{}) if err != nil { return diff --git a/protocol/messenger.go b/protocol/messenger.go index 3732846bf..25628ee50 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -91,7 +91,7 @@ type RawResponse struct { type MessengerResponse struct { Chats []*Chat `json:"chats,omitempty"` - Messages []*Message `json:"messages,omitempty"` + Messages []*common.Message `json:"messages,omitempty"` Contacts []*Contact `json:"contacts,omitempty"` Installations []*multidevice.Installation `json:"installations,omitempty"` EmojiReactions []*EmojiReaction `json:"emojiReactions,omitempty"` @@ -1689,7 +1689,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage) } // SendChatMessage takes a minimal message and sends it based on the corresponding chat -func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*MessengerResponse, error) { +func (m *Messenger) SendChatMessage(ctx context.Context, message *common.Message) (*MessengerResponse, error) { m.mutex.Lock() defer m.mutex.Unlock() @@ -1778,12 +1778,12 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } - response.Messages, err = m.pullMessagesAndResponsesFromDB([]*Message{message}) + response.Messages, err = m.pullMessagesAndResponsesFromDB([]*common.Message{message}) if err != nil { return nil, err } @@ -2540,7 +2540,7 @@ func (m *Messenger) ConfirmMessagesProcessed(messageIDs [][]byte) error { return nil } -func (m *Messenger) MessageByID(id string) (*Message, error) { +func (m *Messenger) MessageByID(id string) (*common.Message, error) { return m.persistence.MessageByID(id) } @@ -2548,11 +2548,11 @@ func (m *Messenger) MessagesExist(ids []string) (map[string]bool, error) { return m.persistence.MessagesExist(ids) } -func (m *Messenger) MessageByChatID(chatID, cursor string, limit int) ([]*Message, string, error) { +func (m *Messenger) MessageByChatID(chatID, cursor string, limit int) ([]*common.Message, string, error) { return m.persistence.MessageByChatID(chatID, cursor, limit) } -func (m *Messenger) SaveMessages(messages []*Message) error { +func (m *Messenger) SaveMessages(messages []*common.Message) error { return m.persistence.SaveMessages(messages) } @@ -2733,7 +2733,7 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr return nil, errors.New("Need to be a one-to-one chat") } - message := &Message{} + message := &common.Message{} err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport) if err != nil { return nil, err @@ -2760,12 +2760,12 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr ResendAutomatically: true, }) - message.CommandParameters = &CommandParameters{ + message.CommandParameters = &common.CommandParameters{ ID: types.EncodeHex(id), Value: value, Address: address, Contract: contract, - CommandState: CommandStateRequestTransaction, + CommandState: common.CommandStateRequestTransaction, } if err != nil { @@ -2785,13 +2785,13 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -2810,7 +2810,7 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr return nil, errors.New("Need to be a one-to-one chat") } - message := &Message{} + message := &common.Message{} err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport) if err != nil { return nil, err @@ -2836,12 +2836,12 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr ResendAutomatically: true, }) - message.CommandParameters = &CommandParameters{ + message.CommandParameters = &common.CommandParameters{ ID: types.EncodeHex(id), From: from, Value: value, Contract: contract, - CommandState: CommandStateRequestAddressForTransaction, + CommandState: common.CommandStateRequestAddressForTransaction, } if err != nil { @@ -2861,13 +2861,13 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -2902,7 +2902,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess message.WhisperTimestamp = timestamp message.Timestamp = timestamp message.Text = "Request address for transaction accepted" - message.OutgoingStatus = OutgoingStatusSending + message.OutgoingStatus = common.OutgoingStatusSending // Hide previous message previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID) @@ -2944,7 +2944,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess message.ID = types.EncodeHex(newMessageID) message.CommandParameters.Address = address - message.CommandParameters.CommandState = CommandStateRequestAddressForTransactionAccepted + message.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionAccepted err = message.PrepareContent() if err != nil { @@ -2956,13 +2956,13 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -2997,7 +2997,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str message.WhisperTimestamp = timestamp message.Timestamp = timestamp message.Text = "Transaction request declined" - message.OutgoingStatus = OutgoingStatusSending + message.OutgoingStatus = common.OutgoingStatusSending message.Replace = messageID err = m.persistence.HideMessage(messageID) @@ -3026,7 +3026,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str } message.ID = types.EncodeHex(newMessageID) - message.CommandParameters.CommandState = CommandStateRequestTransactionDeclined + message.CommandParameters.CommandState = common.CommandStateRequestTransactionDeclined err = message.PrepareContent() if err != nil { @@ -3038,13 +3038,13 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -3079,7 +3079,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes message.WhisperTimestamp = timestamp message.Timestamp = timestamp message.Text = "Request address for transaction declined" - message.OutgoingStatus = OutgoingStatusSending + message.OutgoingStatus = common.OutgoingStatusSending message.Replace = messageID err = m.persistence.HideMessage(messageID) @@ -3108,7 +3108,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes } message.ID = types.EncodeHex(newMessageID) - message.CommandParameters.CommandState = CommandStateRequestAddressForTransactionDeclined + message.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionDeclined err = message.PrepareContent() if err != nil { @@ -3120,13 +3120,13 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -3161,7 +3161,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas message.WhisperTimestamp = timestamp message.Timestamp = timestamp message.Text = transactionSentTxt - message.OutgoingStatus = OutgoingStatusSending + message.OutgoingStatus = common.OutgoingStatusSending // Hide previous message previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID) @@ -3207,7 +3207,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas message.ID = types.EncodeHex(newMessageID) message.CommandParameters.TransactionHash = transactionHash message.CommandParameters.Signature = signature - message.CommandParameters.CommandState = CommandStateTransactionSent + message.CommandParameters.CommandState = common.CommandStateTransactionSent err = message.PrepareContent() if err != nil { @@ -3219,13 +3219,13 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -3244,7 +3244,7 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract return nil, errors.New("Need to be a one-to-one chat") } - message := &Message{} + message := &common.Message{} err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport) if err != nil { return nil, err @@ -3282,12 +3282,12 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract } message.ID = types.EncodeHex(newMessageID) - message.CommandParameters = &CommandParameters{ + message.CommandParameters = &common.CommandParameters{ TransactionHash: transactionHash, Value: value, Contract: contract, Signature: signature, - CommandState: CommandStateTransactionSent, + CommandState: common.CommandStateTransactionSent, } err = message.PrepareContent() @@ -3300,13 +3300,13 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract return nil, err } - err = m.persistence.SaveMessages([]*Message{message}) + err = m.persistence.SaveMessages([]*common.Message{message}) if err != nil { return nil, err } response.Chats = []*Chat{chat} - response.Messages = []*Message{message} + response.Messages = []*common.Message{message} return &response, m.saveChat(chat) } @@ -3335,7 +3335,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. return nil, err } for _, validationResult := range responses { - var message *Message + var message *common.Message chatID := contactIDFromPublicKey(validationResult.Transaction.From) chat, ok := m.allChats[chatID] if !ok { @@ -3344,7 +3344,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. if validationResult.Message != nil { message = validationResult.Message } else { - message = &Message{} + message = &common.Message{} err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport) if err != nil { return nil, err @@ -3365,7 +3365,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. message.ID = validationResult.Transaction.MessageID if message.CommandParameters == nil { - message.CommandParameters = &CommandParameters{} + message.CommandParameters = &common.CommandParameters{} } else { message.CommandParameters = validationResult.Message.CommandParameters } @@ -3373,7 +3373,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. message.CommandParameters.Value = validationResult.Value message.CommandParameters.Contract = validationResult.Contract message.CommandParameters.Address = validationResult.Address - message.CommandParameters.CommandState = CommandStateTransactionSent + message.CommandParameters.CommandState = common.CommandStateTransactionSent message.CommandParameters.TransactionHash = validationResult.Transaction.TransactionHash err = message.PrepareContent() @@ -3422,7 +3422,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types. // pullMessagesAndResponsesFromDB pulls all the messages and the one that have // been replied to from the database -func (m *Messenger) pullMessagesAndResponsesFromDB(messages []*Message) ([]*Message, error) { +func (m *Messenger) pullMessagesAndResponsesFromDB(messages []*common.Message) ([]*common.Message, error) { var messageIDs []string for _, message := range messages { messageIDs = append(messageIDs, message.ID) diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index 17703ff31..c3b9dfaac 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -24,6 +24,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" enstypes "github.com/status-im/status-go/eth-node/types/ens" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/tt" v1protocol "github.com/status-im/status-go/protocol/v1" @@ -288,9 +289,9 @@ func (s *MessengerSuite) TestInit() { } } -func buildTestMessage(chat Chat) *Message { +func buildTestMessage(chat Chat) *common.Message { clock, timestamp := chat.NextClockAndTimestamp(&testTimeSource{}) - message := &Message{} + message := &common.Message{} message.Text = "text-input-message" message.ChatId = chat.ID message.Clock = clock @@ -322,7 +323,7 @@ func (s *MessengerSuite) TestMarkMessagesSeen() { inputMessage2.ID = "2" inputMessage2.Seen = false - err = s.m.SaveMessages([]*Message{inputMessage1, inputMessage2}) + err = s.m.SaveMessages([]*common.Message{inputMessage1, inputMessage2}) s.Require().NoError(err) count, err := s.m.MarkMessagesSeen(chat.ID, []string{inputMessage1.ID}) @@ -346,7 +347,7 @@ func (s *MessengerSuite) TestMarkAllRead() { inputMessage2.ID = "2" inputMessage2.Seen = false - err = s.m.SaveMessages([]*Message{inputMessage1, inputMessage2}) + err = s.m.SaveMessages([]*common.Message{inputMessage1, inputMessage2}) s.Require().NoError(err) err = s.m.MarkAllRead(chat.ID) @@ -374,7 +375,7 @@ func (s *MessengerSuite) TestSendPublic() { s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") - s.Require().Equal(outputMessage.OutgoingStatus, OutgoingStatusSending, "it marks the message as sending") + s.Require().Equal(outputMessage.OutgoingStatus, common.OutgoingStatusSending, "it marks the message as sending") s.Require().NotEmpty(outputMessage.ID, "it sets the ID field") s.Require().Equal(protobuf.MessageType_PUBLIC_GROUP, outputMessage.MessageType) @@ -389,7 +390,7 @@ func (s *MessengerSuite) TestSendPrivateOneToOne() { pkString := hex.EncodeToString(crypto.FromECDSAPub(&recipientKey.PublicKey)) chat := CreateOneToOneChat(pkString, &recipientKey.PublicKey, s.m.transport) - inputMessage := &Message{} + inputMessage := &common.Message{} inputMessage.ChatId = chat.ID chat.LastClockValue = uint64(100000000000000) err = s.m.SaveChat(&chat) @@ -405,7 +406,7 @@ func (s *MessengerSuite) TestSendPrivateOneToOne() { s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") - s.Require().Equal(outputMessage.OutgoingStatus, OutgoingStatusSending, "it marks the message as sending") + s.Require().Equal(outputMessage.OutgoingStatus, common.OutgoingStatusSending, "it marks the message as sending") s.Require().NotEmpty(outputMessage.ID, "it sets the ID field") s.Require().Equal(protobuf.MessageType_ONE_TO_ONE, outputMessage.MessageType) } @@ -422,13 +423,13 @@ func (s *MessengerSuite) TestSendPrivateGroup() { _, err = s.m.AddMembersToGroupChat(context.Background(), chat.ID, members) s.NoError(err) - inputMessage := &Message{} + inputMessage := &common.Message{} inputMessage.ChatId = chat.ID chat.LastClockValue = uint64(100000000000000) err = s.m.SaveChat(chat) s.NoError(err) response, err = s.m.SendChatMessage(context.Background(), inputMessage) - s.NoError(err) + s.Require().NoError(err) s.Require().Equal(1, len(response.Messages), "it returns the message") outputMessage := response.Messages[0] @@ -438,7 +439,7 @@ func (s *MessengerSuite) TestSendPrivateGroup() { s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") - s.Require().Equal(outputMessage.OutgoingStatus, OutgoingStatusSending, "it marks the message as sending") + s.Require().Equal(outputMessage.OutgoingStatus, common.OutgoingStatusSending, "it marks the message as sending") s.Require().NotEmpty(outputMessage.ID, "it sets the ID field") s.Require().Equal(protobuf.MessageType_PRIVATE_GROUP, outputMessage.MessageType) } @@ -450,7 +451,7 @@ func (s *MessengerSuite) TestSendPrivateEmptyGroup() { chat := response.Chats[0] - inputMessage := &Message{} + inputMessage := &common.Message{} inputMessage.ChatId = chat.ID chat.LastClockValue = uint64(100000000000000) err = s.m.SaveChat(chat) @@ -466,7 +467,7 @@ func (s *MessengerSuite) TestSendPrivateEmptyGroup() { s.Require().NotEqual(uint64(0), chat.Timestamp, "it sets the timestamp") s.Require().Equal("0x"+hex.EncodeToString(crypto.FromECDSAPub(&s.privateKey.PublicKey)), outputMessage.From, "it sets the From field") s.Require().True(outputMessage.Seen, "it marks the message as seen") - s.Require().Equal(outputMessage.OutgoingStatus, OutgoingStatusSending, "it marks the message as sending") + s.Require().Equal(outputMessage.OutgoingStatus, common.OutgoingStatusSending, "it marks the message as sending") s.Require().NotEmpty(outputMessage.ID, "it sets the ID field") s.Require().Equal(protobuf.MessageType_PRIVATE_GROUP, outputMessage.MessageType) } @@ -962,7 +963,7 @@ func (s *MessengerSuite) TestChatPersistencePublic() { LastClockValue: 20, DeletedAtClockValue: 30, UnviewedMessagesCount: 40, - LastMessage: &Message{}, + LastMessage: &common.Message{}, } s.Require().NoError(s.m.SaveChat(&chat)) @@ -987,7 +988,7 @@ func (s *MessengerSuite) TestDeleteChat() { LastClockValue: 20, DeletedAtClockValue: 30, UnviewedMessagesCount: 40, - LastMessage: &Message{}, + LastMessage: &common.Message{}, } s.Require().NoError(s.m.SaveChat(&chat)) @@ -1010,7 +1011,7 @@ func (s *MessengerSuite) TestChatPersistenceUpdate() { LastClockValue: 20, DeletedAtClockValue: 30, UnviewedMessagesCount: 40, - LastMessage: &Message{}, + LastMessage: &common.Message{}, } s.Require().NoError(s.m.SaveChat(&chat)) @@ -1044,7 +1045,7 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() { LastClockValue: 20, DeletedAtClockValue: 30, UnviewedMessagesCount: 40, - LastMessage: &Message{}, + LastMessage: &common.Message{}, } contact := Contact{ ID: testPK, @@ -1133,7 +1134,7 @@ func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() { LastClockValue: 20, DeletedAtClockValue: 30, UnviewedMessagesCount: 40, - LastMessage: &Message{}, + LastMessage: &common.Message{}, } s.Require().NoError(s.m.SaveChat(&chat)) savedChats := s.m.Chats() @@ -1211,7 +1212,7 @@ func (s *MessengerSuite) TestBlockContact() { contact.Name = "blocked" - messages := []*Message{ + messages := []*common.Message{ { ID: "test-1", LocalChatID: chat2.ID, @@ -1479,7 +1480,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() { s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestAddressForTransaction, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransaction, senderMessage.CommandParameters.CommandState) // Wait for the message to reach its destination response, err = WaitOnMessengerResponse( @@ -1500,7 +1501,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() { s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestAddressForTransaction, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransaction, receiverMessage.CommandParameters.CommandState) // We decline the request response, err = theirMessenger.DeclineRequestAddressForTransaction(context.Background(), receiverMessage.ID) @@ -1514,7 +1515,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() { s.Require().NotNil(senderMessage.CommandParameters) s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(contract, senderMessage.CommandParameters.Contract) - s.Require().Equal(CommandStateRequestAddressForTransactionDeclined, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransactionDeclined, senderMessage.CommandParameters.CommandState) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) s.Require().Equal(receiverMessage.ID, senderMessage.Replace) @@ -1535,7 +1536,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() { s.Require().NotNil(receiverMessage.CommandParameters) s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) - s.Require().Equal(CommandStateRequestAddressForTransactionDeclined, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransactionDeclined, receiverMessage.CommandParameters.CommandState) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal(initialCommandID, receiverMessage.Replace) s.Require().NoError(theirMessenger.Shutdown()) @@ -1574,7 +1575,7 @@ func (s *MessengerSuite) TestSendEthTransaction() { s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(signature, senderMessage.CommandParameters.Signature) - s.Require().Equal(CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) s.Require().NotEmpty(senderMessage.ID) s.Require().Equal("", senderMessage.Replace) @@ -1638,7 +1639,7 @@ func (s *MessengerSuite) TestSendEthTransaction() { s.Require().Equal(transactionHash, receiverMessage.CommandParameters.TransactionHash) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal("", receiverMessage.CommandParameters.ID) - s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState) s.Require().Equal(senderMessage.ID, receiverMessage.ID) s.Require().Equal("", receiverMessage.Replace) s.Require().NoError(theirMessenger.Shutdown()) @@ -1677,7 +1678,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() { s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(signature, senderMessage.CommandParameters.Signature) - s.Require().Equal(CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) s.Require().NotEmpty(senderMessage.ID) var transactions []*TransactionToValidate @@ -1741,7 +1742,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() { s.Require().Equal(transactionHash, receiverMessage.CommandParameters.TransactionHash) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal("", receiverMessage.CommandParameters.ID) - s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState) s.Require().Equal(senderMessage.ID, receiverMessage.ID) s.Require().Equal(senderMessage.Replace, senderMessage.Replace) s.Require().NoError(theirMessenger.Shutdown()) @@ -1775,7 +1776,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() { s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestAddressForTransaction, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransaction, senderMessage.CommandParameters.CommandState) // Wait for the message to reach its destination response, err = WaitOnMessengerResponse( @@ -1796,7 +1797,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() { s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestAddressForTransaction, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransaction, receiverMessage.CommandParameters.CommandState) // We accept the request response, err = theirMessenger.AcceptRequestAddressForTransaction(context.Background(), receiverMessage.ID, "some-address") @@ -1810,7 +1811,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() { s.Require().NotNil(senderMessage.CommandParameters) s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(contract, senderMessage.CommandParameters.Contract) - s.Require().Equal(CommandStateRequestAddressForTransactionAccepted, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransactionAccepted, senderMessage.CommandParameters.CommandState) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) s.Require().Equal("some-address", senderMessage.CommandParameters.Address) s.Require().Equal(receiverMessage.ID, senderMessage.Replace) @@ -1832,7 +1833,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() { s.Require().NotNil(receiverMessage.CommandParameters) s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) - s.Require().Equal(CommandStateRequestAddressForTransactionAccepted, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestAddressForTransactionAccepted, receiverMessage.CommandParameters.CommandState) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal("some-address", receiverMessage.CommandParameters.Address) s.Require().Equal(initialCommandID, receiverMessage.Replace) @@ -1868,7 +1869,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() { s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(receiverAddressString, senderMessage.CommandParameters.Address) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestTransaction, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestTransaction, senderMessage.CommandParameters.CommandState) // Wait for the message to reach its destination response, err = WaitOnMessengerResponse( @@ -1890,7 +1891,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() { s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestTransaction, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestTransaction, receiverMessage.CommandParameters.CommandState) response, err = theirMessenger.DeclineRequestTransaction(context.Background(), initialCommandID) s.Require().NoError(err) @@ -1904,7 +1905,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() { s.Require().Equal("Transaction request declined", senderMessage.Text) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) s.Require().Equal(receiverMessage.ID, senderMessage.Replace) - s.Require().Equal(CommandStateRequestTransactionDeclined, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestTransactionDeclined, senderMessage.CommandParameters.CommandState) // Wait for the message to reach its destination response, err = WaitOnMessengerResponse( @@ -1924,7 +1925,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() { s.Require().Equal("Transaction request declined", receiverMessage.Text) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal(initialCommandID, receiverMessage.Replace) - s.Require().Equal(CommandStateRequestTransactionDeclined, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestTransactionDeclined, receiverMessage.CommandParameters.CommandState) s.Require().NoError(theirMessenger.Shutdown()) } @@ -1957,7 +1958,7 @@ func (s *MessengerSuite) TestRequestTransaction() { s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(receiverAddressString, senderMessage.CommandParameters.Address) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestTransaction, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestTransaction, senderMessage.CommandParameters.CommandState) // Wait for the message to reach its destination response, err = WaitOnMessengerResponse( @@ -1979,7 +1980,7 @@ func (s *MessengerSuite) TestRequestTransaction() { s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) - s.Require().Equal(CommandStateRequestTransaction, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateRequestTransaction, receiverMessage.CommandParameters.CommandState) transactionHash := "0x412a851ac2ae51cad34a56c8a9cfee55d577ac5e1ac71cf488a2f2093a373799" signature, err := buildSignature(theirMessenger.identity, &theirMessenger.identity.PublicKey, transactionHash) @@ -2003,7 +2004,7 @@ func (s *MessengerSuite) TestRequestTransaction() { s.Require().Equal(signature, senderMessage.CommandParameters.Signature) s.Require().NotEmpty(senderMessage.ID) s.Require().Equal(receiverMessage.ID, senderMessage.Replace) - s.Require().Equal(CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateTransactionSent, senderMessage.CommandParameters.CommandState) var transactions []*TransactionToValidate // Wait for the message to reach its destination @@ -2068,7 +2069,7 @@ func (s *MessengerSuite) TestRequestTransaction() { s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal(signature, receiverMessage.CommandParameters.Signature) - s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState) + s.Require().Equal(common.CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState) s.Require().Equal(senderMessage.ID, receiverMessage.ID) s.Require().Equal(senderMessage.Replace, senderMessage.Replace) s.Require().NoError(theirMessenger.Shutdown()) @@ -2102,7 +2103,7 @@ func (m *mockSendMessagesRequest) SendMessagesRequest(peerID []byte, request typ } func (s *MessengerSuite) TestMessageJSON() { - message := &Message{ + message := &common.Message{ ID: "test-1", LocalChatID: "local-chat-id", Alias: "alias", @@ -2121,7 +2122,7 @@ func (s *MessengerSuite) TestMessageJSON() { s.Require().NoError(err) s.Require().Equal(expectedJSON, string(messageJSON)) - decodedMessage := &Message{} + decodedMessage := &common.Message{} err = json.Unmarshal([]byte(expectedJSON), decodedMessage) s.Require().NoError(err) s.Require().Equal(message, decodedMessage) @@ -2186,14 +2187,14 @@ func (s *MessageHandlerSuite) TestRun() { Name string Error bool Chat Chat // Chat to create - Message Message + Message common.Message SigPubKey *ecdsa.PublicKey ExpectedChatID string }{ { Name: "Public chat", Chat: CreatePublicChat("test-chat", &testTimeSource{}), - Message: Message{ + Message: common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: "test-chat", MessageType: protobuf.MessageType_PUBLIC_GROUP, @@ -2205,7 +2206,7 @@ func (s *MessageHandlerSuite) TestRun() { { Name: "Private message from myself with existing chat", Chat: CreateOneToOneChat("test-private-chat", &key1.PublicKey, &testTimeSource{}), - Message: Message{ + Message: common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: "test-chat", MessageType: protobuf.MessageType_ONE_TO_ONE, @@ -2217,7 +2218,7 @@ func (s *MessageHandlerSuite) TestRun() { { Name: "Private message from other with existing chat", Chat: CreateOneToOneChat("test-private-chat", &key2.PublicKey, &testTimeSource{}), - Message: Message{ + Message: common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: "test-chat", MessageType: protobuf.MessageType_ONE_TO_ONE, @@ -2229,7 +2230,7 @@ func (s *MessageHandlerSuite) TestRun() { }, { Name: "Private message from myself without chat", - Message: Message{ + Message: common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: "test-chat", MessageType: protobuf.MessageType_ONE_TO_ONE, @@ -2241,7 +2242,7 @@ func (s *MessageHandlerSuite) TestRun() { }, { Name: "Private message from other without chat", - Message: Message{ + Message: common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: "test-chat", MessageType: protobuf.MessageType_ONE_TO_ONE, @@ -2258,7 +2259,7 @@ func (s *MessageHandlerSuite) TestRun() { }, { Name: "Private group message", - Message: Message{ + Message: common.Message{ ChatMessage: protobuf.ChatMessage{ ChatId: "non-existing-chat", MessageType: protobuf.MessageType_PRIVATE_GROUP, diff --git a/protocol/persistence.go b/protocol/persistence.go index 788d0793d..5f7af6101 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -266,7 +266,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { // Restore last message if lastMessageBytes != nil { - message := &Message{} + message := &common.Message{} if err = json.Unmarshal(lastMessageBytes, message); err != nil { return } @@ -346,7 +346,7 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) { // Restore last message if lastMessageBytes != nil { - message := &Message{} + message := &common.Message{} if err = json.Unmarshal(lastMessageBytes, message); err != nil { return nil, err } diff --git a/protocol/persistence_test.go b/protocol/persistence_test.go index 76402d183..5e69a6aa6 100644 --- a/protocol/persistence_test.go +++ b/protocol/persistence_test.go @@ -13,6 +13,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/sqlite" ) @@ -103,9 +104,9 @@ func TestMessageByChatID(t *testing.T) { count := 1000 pageSize := 50 - var messages []*Message + var messages []*common.Message for i := 0; i < count; i++ { - messages = append(messages, &Message{ + messages = append(messages, &common.Message{ ID: strconv.Itoa(i), LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{ @@ -116,7 +117,7 @@ func TestMessageByChatID(t *testing.T) { // Add some other chats. if count%5 == 0 { - messages = append(messages, &Message{ + messages = append(messages, &common.Message{ ID: strconv.Itoa(count + i), LocalChatID: "other-chat", ChatMessage: protobuf.ChatMessage{ @@ -132,7 +133,7 @@ func TestMessageByChatID(t *testing.T) { outOfOrderCount := pageSize + 1 allCount := count + outOfOrderCount for i := 0; i < pageSize+1; i++ { - messages = append(messages, &Message{ + messages = append(messages, &common.Message{ ID: strconv.Itoa(count*2 + i), LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{ @@ -147,13 +148,13 @@ func TestMessageByChatID(t *testing.T) { require.NoError(t, err) var ( - result []*Message + result []*common.Message cursor string iter int ) for { var ( - items []*Message + items []*common.Message err error ) @@ -183,7 +184,7 @@ func TestMessageReplies(t *testing.T) { require.NoError(t, err) p := sqlitePersistence{db: db} chatID := testPublicChatID - message1 := &Message{ + message1 := &common.Message{ ID: "id-1", LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{ @@ -192,7 +193,7 @@ func TestMessageReplies(t *testing.T) { }, From: "1", } - message2 := &Message{ + message2 := &common.Message{ ID: "id-2", LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{ @@ -204,7 +205,7 @@ func TestMessageReplies(t *testing.T) { From: "2", } - message3 := &Message{ + message3 := &common.Message{ ID: "id-3", LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{ @@ -215,7 +216,7 @@ func TestMessageReplies(t *testing.T) { From: "3", } - messages := []*Message{message1, message2, message3} + messages := []*common.Message{message1, message2, message3} err = p.SaveMessages(messages) require.NoError(t, err) @@ -227,7 +228,7 @@ func TestMessageReplies(t *testing.T) { require.Nil(t, retrievedMessages[0].QuotedMessage) require.Equal(t, "id-1", retrievedMessages[1].ResponseTo) - require.Equal(t, &QuotedMessage{From: "1", Text: "content-1"}, retrievedMessages[1].QuotedMessage) + require.Equal(t, &common.QuotedMessage{From: "1", Text: "content-1"}, retrievedMessages[1].QuotedMessage) require.Equal(t, "", retrievedMessages[2].ResponseTo) require.Nil(t, retrievedMessages[2].QuotedMessage) @@ -242,10 +243,10 @@ func TestMessageByChatIDWithTheSameClocks(t *testing.T) { count := len(clockValues) pageSize := 2 - var messages []*Message + var messages []*common.Message for i, clock := range clockValues { - messages = append(messages, &Message{ + messages = append(messages, &common.Message{ ID: strconv.Itoa(i), LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{ @@ -259,13 +260,13 @@ func TestMessageByChatIDWithTheSameClocks(t *testing.T) { require.NoError(t, err) var ( - result []*Message + result []*common.Message cursor string iter int ) for { var ( - items []*Message + items []*common.Message err error ) @@ -324,14 +325,14 @@ func TestDeleteMessagesByChatID(t *testing.T) { err = insertMinimalMessage(p, "2") require.NoError(t, err) - m, _, err := p.MessageByChatID("chat-id", "", 10) + m, _, err := p.MessageByChatID(testPublicChatID, "", 10) require.NoError(t, err) require.Equal(t, 2, len(m)) - err = p.DeleteMessagesByChatID("chat-id") + err = p.DeleteMessagesByChatID(testPublicChatID) require.NoError(t, err) - m, _, err = p.MessageByChatID("chat-id", "", 10) + m, _, err = p.MessageByChatID(testPublicChatID, "", 10) require.NoError(t, err) require.Equal(t, 0, len(m)) @@ -390,7 +391,7 @@ func TestPersistenceEmojiReactions(t *testing.T) { from2 := "from-2" from3 := "from-3" - chatID := "chat-id" + chatID := testPublicChatID err = insertMinimalMessage(p, id1) require.NoError(t, err) @@ -487,9 +488,9 @@ func openTestDB() (*sql.DB, error) { } func insertMinimalMessage(p sqlitePersistence, id string) error { - return p.SaveMessages([]*Message{{ + return p.SaveMessages([]*common.Message{{ ID: id, - LocalChatID: "chat-id", + LocalChatID: testPublicChatID, ChatMessage: protobuf.ChatMessage{Text: "some-text"}, From: "me", }}) @@ -512,7 +513,7 @@ func TestMessagesAudioDurationMsNull(t *testing.T) { require.NoError(t, err) require.Len(t, m, 1) - m, _, err = p.MessageByChatID("chat-id", "", 10) + m, _, err = p.MessageByChatID(testPublicChatID, "", 10) require.NoError(t, err) require.Len(t, m, 1) } @@ -523,7 +524,7 @@ func TestSaveChat(t *testing.T) { p := sqlitePersistence{db: db} chat := CreatePublicChat("test-chat", &testTimeSource{}) - chat.LastMessage = &Message{} + chat.LastMessage = &common.Message{} err = p.SaveChat(chat) require.NoError(t, err) @@ -533,7 +534,7 @@ func TestSaveChat(t *testing.T) { } func TestSaveMentions(t *testing.T) { - chatID := "chat-id" + chatID := testPublicChatID db, err := openTestDB() require.NoError(t, err) p := sqlitePersistence{db: db} @@ -543,7 +544,7 @@ func TestSaveMentions(t *testing.T) { pkString := types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)) - message := Message{ + message := common.Message{ ID: "1", LocalChatID: chatID, ChatMessage: protobuf.ChatMessage{Text: "some-text"}, @@ -551,7 +552,7 @@ func TestSaveMentions(t *testing.T) { Mentions: []string{pkString}, } - err = p.SaveMessages([]*Message{&message}) + err = p.SaveMessages([]*common.Message{&message}) require.NoError(t, err) retrievedMessages, _, err := p.MessageByChatID(chatID, "", 10) diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 485cc3e2d..bd636ac53 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -142,21 +142,23 @@ func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) { } type PushNotificationRegistration struct { - TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` - DeviceToken string `protobuf:"bytes,2,opt,name=device_token,json=deviceToken,proto3" json:"device_token,omitempty"` - InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` - AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` - Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` - AllowedKeyList [][]byte `protobuf:"bytes,7,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"` - BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` - Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` - Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"` - AllowFromContactsOnly bool `protobuf:"varint,11,opt,name=allow_from_contacts_only,json=allowFromContactsOnly,proto3" json:"allow_from_contacts_only,omitempty"` - ApnTopic string `protobuf:"bytes,12,opt,name=apn_topic,json=apnTopic,proto3" json:"apn_topic,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` + DeviceToken string `protobuf:"bytes,2,opt,name=device_token,json=deviceToken,proto3" json:"device_token,omitempty"` + InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` + Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` + AllowedKeyList [][]byte `protobuf:"bytes,7,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"` + BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` + Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` + Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"` + AllowFromContactsOnly bool `protobuf:"varint,11,opt,name=allow_from_contacts_only,json=allowFromContactsOnly,proto3" json:"allow_from_contacts_only,omitempty"` + ApnTopic string `protobuf:"bytes,12,opt,name=apn_topic,json=apnTopic,proto3" json:"apn_topic,omitempty"` + BlockMentions bool `protobuf:"varint,13,opt,name=block_mentions,json=blockMentions,proto3" json:"block_mentions,omitempty"` + AllowedMentionsChatList [][]byte `protobuf:"bytes,14,rep,name=allowed_mentions_chat_list,json=allowedMentionsChatList,proto3" json:"allowed_mentions_chat_list,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} } @@ -268,6 +270,20 @@ func (m *PushNotificationRegistration) GetApnTopic() string { return "" } +func (m *PushNotificationRegistration) GetBlockMentions() bool { + if m != nil { + return m.BlockMentions + } + return false +} + +func (m *PushNotificationRegistration) GetAllowedMentionsChatList() [][]byte { + if m != nil { + return m.AllowedMentionsChatList + } + return nil +} + type PushNotificationRegistrationResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"` @@ -799,65 +815,67 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 952 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x51, 0x6f, 0xe3, 0x44, - 0x10, 0xc6, 0x4e, 0xda, 0x24, 0x93, 0x90, 0xba, 0xab, 0xb6, 0x67, 0x0e, 0x7a, 0x04, 0x83, 0x44, - 0xd4, 0x87, 0x80, 0x8a, 0xc4, 0x9d, 0x78, 0x22, 0xa4, 0x4e, 0xcf, 0x6a, 0x63, 0x87, 0x8d, 0xcb, - 0xe9, 0x24, 0x24, 0xcb, 0xb1, 0x37, 0xad, 0x55, 0xd7, 0x6b, 0xbc, 0x9b, 0xa2, 0xbc, 0xf1, 0x03, - 0x78, 0xe1, 0x95, 0x9f, 0xc1, 0x33, 0xff, 0x80, 0x3f, 0x85, 0xbc, 0xb6, 0xd3, 0xb4, 0x71, 0xd3, - 0x22, 0xf1, 0x94, 0xcc, 0x37, 0x3b, 0x33, 0xbb, 0xf3, 0xcd, 0x37, 0x06, 0x35, 0x9e, 0xb3, 0x2b, - 0x27, 0xa2, 0x3c, 0x98, 0x05, 0x9e, 0xcb, 0x03, 0x1a, 0xb1, 0x5e, 0x9c, 0x50, 0x4e, 0x51, 0x5d, - 0xfc, 0x4c, 0xe7, 0x33, 0xed, 0xef, 0x2a, 0x7c, 0x32, 0x9e, 0xb3, 0x2b, 0x73, 0xe5, 0x14, 0x26, - 0x97, 0x01, 0xe3, 0x89, 0xf8, 0x8f, 0x2c, 0x00, 0x4e, 0xaf, 0x49, 0xe4, 0xf0, 0x45, 0x4c, 0x54, - 0xa9, 0x23, 0x75, 0xdb, 0xc7, 0x5f, 0xf7, 0x8a, 0xf8, 0xde, 0xa6, 0xd8, 0x9e, 0x9d, 0x06, 0xda, - 0x8b, 0x98, 0xe0, 0x06, 0x2f, 0xfe, 0xa2, 0xcf, 0xa0, 0xe5, 0x93, 0xdb, 0xc0, 0x23, 0x8e, 0xc0, - 0x54, 0xb9, 0x23, 0x75, 0x1b, 0xb8, 0x99, 0x61, 0x22, 0x02, 0x7d, 0x09, 0x3b, 0x41, 0xc4, 0xb8, - 0x1b, 0x86, 0x22, 0x8f, 0x13, 0xf8, 0x6a, 0x45, 0x9c, 0x6a, 0xaf, 0xc2, 0x86, 0x9f, 0xe6, 0x72, - 0x3d, 0x8f, 0x30, 0x96, 0xe7, 0xaa, 0x66, 0xb9, 0x32, 0x2c, 0xcb, 0xa5, 0x42, 0x8d, 0x44, 0xee, - 0x34, 0x24, 0xbe, 0xba, 0xd5, 0x91, 0xba, 0x75, 0x5c, 0x98, 0xa9, 0xe7, 0x96, 0x24, 0x2c, 0xa0, - 0x91, 0xba, 0xdd, 0x91, 0xba, 0x55, 0x5c, 0x98, 0xa8, 0x0b, 0x8a, 0x1b, 0x86, 0xf4, 0x57, 0xe2, - 0x3b, 0xd7, 0x64, 0xe1, 0x84, 0x01, 0xe3, 0x6a, 0xad, 0x53, 0xe9, 0xb6, 0x70, 0x3b, 0xc7, 0xcf, - 0xc8, 0xe2, 0x3c, 0x60, 0x1c, 0x1d, 0xc1, 0xee, 0x34, 0xa4, 0xde, 0x35, 0xf1, 0x1d, 0xef, 0xca, - 0xe5, 0xd9, 0xd1, 0xba, 0x38, 0xba, 0x93, 0x3b, 0x06, 0x57, 0x2e, 0x17, 0x67, 0x5f, 0x01, 0xcc, - 0xa3, 0x44, 0xf4, 0x87, 0x24, 0x6a, 0x43, 0x5c, 0x66, 0x05, 0x41, 0x7b, 0xb0, 0x75, 0x99, 0xb8, - 0x11, 0x57, 0xa1, 0x23, 0x75, 0x5b, 0x38, 0x33, 0xd0, 0x6b, 0x50, 0x45, 0x4d, 0x67, 0x96, 0xd0, - 0x1b, 0xc7, 0xa3, 0x11, 0x77, 0x3d, 0xce, 0x1c, 0x1a, 0x85, 0x0b, 0xb5, 0x29, 0x72, 0xec, 0x0b, - 0xff, 0x30, 0xa1, 0x37, 0x83, 0xdc, 0x6b, 0x45, 0xe1, 0x02, 0x7d, 0x0c, 0x0d, 0x37, 0x8e, 0x1c, - 0x4e, 0xe3, 0xc0, 0x53, 0x5b, 0xa2, 0x31, 0x75, 0x37, 0x8e, 0xec, 0xd4, 0xd6, 0x86, 0xd0, 0x58, - 0x92, 0x83, 0x0e, 0x00, 0x5d, 0x98, 0x67, 0xa6, 0xf5, 0xce, 0x74, 0x6c, 0xeb, 0x4c, 0x37, 0x1d, - 0xfb, 0xfd, 0x58, 0x57, 0x3e, 0x40, 0x1f, 0x42, 0xa3, 0x3f, 0xce, 0x31, 0x45, 0x42, 0x08, 0xda, - 0x43, 0x03, 0xeb, 0x3f, 0xf4, 0x27, 0x7a, 0x8e, 0xc9, 0xda, 0x5f, 0x32, 0x7c, 0xb1, 0x69, 0x04, - 0x30, 0x61, 0x31, 0x8d, 0x18, 0x49, 0x9b, 0xcd, 0xe6, 0x82, 0x16, 0x31, 0x43, 0x75, 0x5c, 0x98, - 0xc8, 0x84, 0x2d, 0x92, 0x24, 0x34, 0x11, 0x83, 0xd0, 0x3e, 0x7e, 0xf3, 0xbc, 0xd9, 0x2a, 0x12, - 0xf7, 0xf4, 0x34, 0x56, 0xcc, 0x58, 0x96, 0x06, 0x1d, 0x02, 0x24, 0xe4, 0x97, 0x39, 0x61, 0xbc, - 0x98, 0x9b, 0x16, 0x6e, 0xe4, 0x88, 0xe1, 0x6b, 0xbf, 0x49, 0xd0, 0x58, 0xc6, 0xac, 0x3e, 0x5d, - 0xc7, 0xd8, 0xc2, 0xc5, 0xd3, 0xf7, 0x61, 0x77, 0xd4, 0x3f, 0x1f, 0x5a, 0x78, 0xa4, 0x9f, 0x38, - 0x23, 0x7d, 0x32, 0xe9, 0x9f, 0xea, 0x8a, 0x84, 0xf6, 0x40, 0xf9, 0x49, 0xc7, 0x13, 0xc3, 0x32, - 0x9d, 0x91, 0x31, 0x19, 0xf5, 0xed, 0xc1, 0x5b, 0x45, 0x46, 0x2f, 0xe1, 0xe0, 0xc2, 0x9c, 0x5c, - 0x8c, 0xc7, 0x16, 0xb6, 0xf5, 0x93, 0xd5, 0x1e, 0x56, 0xd2, 0xa6, 0x19, 0xa6, 0xad, 0x63, 0xb3, - 0x7f, 0x9e, 0x55, 0x50, 0xaa, 0xda, 0x1c, 0xd4, 0x9c, 0xa9, 0x01, 0xf5, 0x49, 0xdf, 0xbf, 0x25, - 0x09, 0x0f, 0x18, 0xb9, 0x21, 0x11, 0x47, 0xef, 0xe1, 0x60, 0x4d, 0xb5, 0x4e, 0x10, 0xcd, 0xa8, - 0x2a, 0x75, 0x2a, 0xdd, 0xe6, 0xf1, 0xe7, 0x8f, 0xb7, 0xe7, 0xc7, 0x39, 0x49, 0x16, 0x46, 0x34, - 0xa3, 0x78, 0x2f, 0x7e, 0xe0, 0x4a, 0x51, 0xed, 0x0d, 0xec, 0x97, 0x86, 0xa0, 0x4f, 0xa1, 0x19, - 0xcf, 0xa7, 0x61, 0xe0, 0xa5, 0xd3, 0xce, 0x44, 0xa1, 0x16, 0x86, 0x0c, 0x3a, 0x23, 0x0b, 0xa6, - 0xfd, 0x2e, 0xc3, 0x47, 0x8f, 0x56, 0x5b, 0x13, 0xa1, 0xb4, 0x2e, 0xc2, 0x12, 0x41, 0xcb, 0xa5, - 0x82, 0x3e, 0x04, 0xb8, 0xbb, 0x4a, 0x41, 0xde, 0xf2, 0x26, 0xa5, 0xc2, 0xac, 0x96, 0x0a, 0x73, - 0x29, 0xa6, 0xad, 0x55, 0x31, 0x3d, 0x2e, 0xf9, 0x23, 0xd8, 0x65, 0x24, 0xb9, 0x25, 0x89, 0xb3, - 0x52, 0xbf, 0x26, 0x62, 0x77, 0x32, 0xc7, 0xb8, 0xb8, 0x85, 0xf6, 0x87, 0x04, 0x87, 0xa5, 0xed, - 0x58, 0x4e, 0xfb, 0x6b, 0xa8, 0xfe, 0x57, 0xce, 0x44, 0x40, 0xfa, 0xfe, 0x1b, 0xc2, 0x98, 0x7b, - 0x49, 0x8a, 0x1e, 0xb5, 0x70, 0x23, 0x47, 0x0c, 0x7f, 0x55, 0x45, 0x95, 0x7b, 0x2a, 0xd2, 0xfe, - 0x91, 0x41, 0x79, 0x98, 0xfc, 0x39, 0xcc, 0xbc, 0x80, 0x9a, 0x58, 0x5c, 0xcb, 0x6a, 0xdb, 0xa9, - 0xf9, 0x34, 0x13, 0x25, 0x8c, 0x56, 0x4b, 0x19, 0x55, 0xa1, 0x96, 0xdf, 0x3f, 0xa7, 0xa2, 0x30, - 0xd1, 0x00, 0xaa, 0xe2, 0x9b, 0xb2, 0x2d, 0x74, 0xff, 0xd5, 0xe3, 0x4d, 0x5a, 0x03, 0x84, 0xdc, - 0x45, 0xb0, 0x66, 0xc3, 0x5e, 0x99, 0x17, 0x69, 0xf0, 0xaa, 0x10, 0xf6, 0xf8, 0x62, 0xf2, 0xd6, - 0x31, 0x2d, 0xdb, 0x18, 0x1a, 0x83, 0xbe, 0x9d, 0x6a, 0x37, 0x17, 0x79, 0x13, 0x6a, 0x77, 0xd2, - 0x16, 0x86, 0x99, 0xba, 0x15, 0x59, 0x8b, 0xe1, 0xc5, 0xfa, 0xf2, 0x11, 0x1b, 0x04, 0x7d, 0x0b, - 0xf5, 0x7c, 0x99, 0xb0, 0x9c, 0xde, 0x97, 0x1b, 0x36, 0xd6, 0xf2, 0xec, 0x13, 0xcc, 0x6a, 0x7f, - 0xca, 0x70, 0xb0, 0x5e, 0x32, 0xa6, 0x09, 0xdf, 0xb0, 0x3a, 0xbf, 0xbf, 0xbf, 0x3a, 0x8f, 0x36, - 0xad, 0xce, 0x34, 0x55, 0xe9, 0xb2, 0xfc, 0x3f, 0x58, 0xd6, 0x7e, 0x7e, 0xce, 0x52, 0xdd, 0x81, - 0xe6, 0x3b, 0x6c, 0x99, 0xa7, 0xab, 0x5f, 0x94, 0x07, 0xcb, 0x51, 0x4e, 0x31, 0xd3, 0xb2, 0x1d, - 0xac, 0x9f, 0x1a, 0x13, 0x5b, 0xc7, 0xfa, 0x89, 0x52, 0x49, 0x17, 0xe6, 0xfa, 0x83, 0x72, 0xa9, - 0xdd, 0xef, 0xab, 0xf4, 0x50, 0x31, 0xdf, 0x41, 0x2d, 0x11, 0x6f, 0x67, 0xaa, 0x2c, 0xd8, 0xea, - 0x3c, 0xd5, 0x24, 0x5c, 0x04, 0x4c, 0xb7, 0xc5, 0xc9, 0x6f, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, - 0xff, 0x42, 0x7f, 0xee, 0x48, 0x09, 0x00, 0x00, + // 990 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x6f, 0xe3, 0x44, + 0x18, 0xc6, 0x4e, 0xda, 0x24, 0x6f, 0xd2, 0xd4, 0x1d, 0xf5, 0xc3, 0x14, 0xba, 0x04, 0x03, 0x22, + 0xea, 0x21, 0xa0, 0x22, 0xb1, 0x2b, 0xb8, 0x10, 0x52, 0xa7, 0x6b, 0xb5, 0xb1, 0xc3, 0xc4, 0x65, + 0xb5, 0x12, 0xd2, 0xc8, 0xb1, 0x27, 0xad, 0x55, 0xd7, 0x36, 0x1e, 0xa7, 0x28, 0x37, 0x7e, 0x00, + 0x17, 0xae, 0x9c, 0xf8, 0x0d, 0xfc, 0x0d, 0xfe, 0x14, 0xf2, 0xd8, 0xce, 0xba, 0x8d, 0x9b, 0x16, + 0x69, 0x4f, 0xc9, 0x3c, 0xef, 0xc7, 0xcc, 0xfb, 0xf1, 0x3c, 0x06, 0x39, 0x9c, 0xb3, 0x6b, 0xe2, + 0x07, 0xb1, 0x3b, 0x73, 0x6d, 0x2b, 0x76, 0x03, 0x9f, 0xf5, 0xc2, 0x28, 0x88, 0x03, 0x54, 0xe7, + 0x3f, 0xd3, 0xf9, 0x4c, 0xf9, 0x7b, 0x03, 0x3e, 0x1e, 0xcf, 0xd9, 0xb5, 0x5e, 0xf0, 0xc2, 0xf4, + 0xca, 0x65, 0x71, 0xc4, 0xff, 0x23, 0x03, 0x20, 0x0e, 0x6e, 0xa8, 0x4f, 0xe2, 0x45, 0x48, 0x65, + 0xa1, 0x23, 0x74, 0xdb, 0x27, 0x5f, 0xf7, 0xf2, 0xf8, 0xde, 0xba, 0xd8, 0x9e, 0x99, 0x04, 0x9a, + 0x8b, 0x90, 0xe2, 0x46, 0x9c, 0xff, 0x45, 0x9f, 0x42, 0xcb, 0xa1, 0x77, 0xae, 0x4d, 0x09, 0xc7, + 0x64, 0xb1, 0x23, 0x74, 0x1b, 0xb8, 0x99, 0x62, 0x3c, 0x02, 0x7d, 0x09, 0xdb, 0xae, 0xcf, 0x62, + 0xcb, 0xf3, 0x78, 0x1e, 0xe2, 0x3a, 0x72, 0x85, 0x7b, 0xb5, 0x8b, 0xb0, 0xe6, 0x24, 0xb9, 0x2c, + 0xdb, 0xa6, 0x8c, 0x65, 0xb9, 0xaa, 0x69, 0xae, 0x14, 0x4b, 0x73, 0xc9, 0x50, 0xa3, 0xbe, 0x35, + 0xf5, 0xa8, 0x23, 0x6f, 0x74, 0x84, 0x6e, 0x1d, 0xe7, 0xc7, 0xc4, 0x72, 0x47, 0x23, 0xe6, 0x06, + 0xbe, 0xbc, 0xd9, 0x11, 0xba, 0x55, 0x9c, 0x1f, 0x51, 0x17, 0x24, 0xcb, 0xf3, 0x82, 0xdf, 0xa8, + 0x43, 0x6e, 0xe8, 0x82, 0x78, 0x2e, 0x8b, 0xe5, 0x5a, 0xa7, 0xd2, 0x6d, 0xe1, 0x76, 0x86, 0x9f, + 0xd3, 0xc5, 0x85, 0xcb, 0x62, 0x74, 0x0c, 0x3b, 0x53, 0x2f, 0xb0, 0x6f, 0xa8, 0x43, 0xec, 0x6b, + 0x2b, 0x4e, 0x5d, 0xeb, 0xdc, 0x75, 0x3b, 0x33, 0x0c, 0xae, 0xad, 0x98, 0xfb, 0xbe, 0x00, 0x98, + 0xfb, 0x11, 0xef, 0x0f, 0x8d, 0xe4, 0x06, 0x7f, 0x4c, 0x01, 0x41, 0xbb, 0xb0, 0x71, 0x15, 0x59, + 0x7e, 0x2c, 0x43, 0x47, 0xe8, 0xb6, 0x70, 0x7a, 0x40, 0x2f, 0x41, 0xe6, 0x77, 0x92, 0x59, 0x14, + 0xdc, 0x12, 0x3b, 0xf0, 0x63, 0xcb, 0x8e, 0x19, 0x09, 0x7c, 0x6f, 0x21, 0x37, 0x79, 0x8e, 0x3d, + 0x6e, 0x1f, 0x46, 0xc1, 0xed, 0x20, 0xb3, 0x1a, 0xbe, 0xb7, 0x40, 0x1f, 0x41, 0xc3, 0x0a, 0x7d, + 0x12, 0x07, 0xa1, 0x6b, 0xcb, 0x2d, 0xde, 0x98, 0xba, 0x15, 0xfa, 0x66, 0x72, 0x46, 0x5f, 0x40, + 0x9b, 0x3f, 0x8f, 0xdc, 0x52, 0x9f, 0x2f, 0x86, 0xbc, 0xc5, 0x73, 0x6d, 0x71, 0x74, 0x94, 0x81, + 0xe8, 0x7b, 0x38, 0xcc, 0x1b, 0x91, 0x3b, 0x16, 0xea, 0x6c, 0xf3, 0x3a, 0x0f, 0x32, 0x8f, 0x3c, + 0x28, 0xaf, 0x57, 0x19, 0x42, 0x63, 0xb9, 0x00, 0x68, 0x1f, 0xd0, 0xa5, 0x7e, 0xae, 0x1b, 0x6f, + 0x74, 0x62, 0x1a, 0xe7, 0xaa, 0x4e, 0xcc, 0xb7, 0x63, 0x55, 0xfa, 0x00, 0x6d, 0x41, 0xa3, 0x3f, + 0xce, 0x30, 0x49, 0x40, 0x08, 0xda, 0x43, 0x0d, 0xab, 0x3f, 0xf6, 0x27, 0x6a, 0x86, 0x89, 0xca, + 0x3f, 0x22, 0x7c, 0xbe, 0x6e, 0xcd, 0x30, 0x65, 0x61, 0xe0, 0x33, 0x9a, 0x0c, 0x94, 0xcd, 0xf9, + 0xe8, 0xf9, 0x9e, 0xd6, 0x71, 0x7e, 0x44, 0x3a, 0x6c, 0xd0, 0x28, 0x0a, 0x22, 0xbe, 0x6c, 0xed, + 0x93, 0x57, 0xcf, 0xdb, 0xdf, 0x3c, 0x71, 0x4f, 0x4d, 0x62, 0xf9, 0x1e, 0xa7, 0x69, 0xd0, 0x11, + 0x40, 0x44, 0x7f, 0x9d, 0x53, 0x16, 0xe7, 0xbb, 0xd9, 0xc2, 0x8d, 0x0c, 0xd1, 0x1c, 0xe5, 0x77, + 0x01, 0x1a, 0xcb, 0x98, 0x62, 0xe9, 0x2a, 0xc6, 0x06, 0xce, 0x4b, 0xdf, 0x83, 0x9d, 0x51, 0xff, + 0x62, 0x68, 0xe0, 0x91, 0x7a, 0x4a, 0x46, 0xea, 0x64, 0xd2, 0x3f, 0x53, 0x25, 0x01, 0xed, 0x82, + 0xf4, 0xb3, 0x8a, 0x27, 0x9a, 0xa1, 0x93, 0x91, 0x36, 0x19, 0xf5, 0xcd, 0xc1, 0x6b, 0x49, 0x44, + 0x87, 0xb0, 0x7f, 0xa9, 0x4f, 0x2e, 0xc7, 0x63, 0x03, 0x9b, 0xea, 0x69, 0xb1, 0x87, 0x95, 0xa4, + 0x69, 0x9a, 0x6e, 0xaa, 0x58, 0xef, 0x5f, 0xa4, 0x37, 0x48, 0x55, 0x65, 0x0e, 0x72, 0xb6, 0x0d, + 0x83, 0xc0, 0xa1, 0x7d, 0xe7, 0x8e, 0x46, 0xb1, 0xcb, 0x68, 0x32, 0x45, 0xf4, 0x16, 0xf6, 0x57, + 0x94, 0x81, 0xb8, 0xfe, 0x2c, 0x90, 0x85, 0x4e, 0xa5, 0xdb, 0x3c, 0xf9, 0xec, 0xf1, 0xf6, 0xfc, + 0x34, 0xa7, 0xd1, 0x42, 0xf3, 0x67, 0x01, 0xde, 0x0d, 0x1f, 0x98, 0x12, 0x54, 0x79, 0x05, 0x7b, + 0xa5, 0x21, 0xe8, 0x13, 0x68, 0x86, 0xf3, 0xa9, 0xe7, 0xda, 0x09, 0xa3, 0x18, 0xbf, 0xa8, 0x85, + 0x21, 0x85, 0xce, 0xe9, 0x82, 0x29, 0x7f, 0x88, 0xf0, 0xe1, 0xa3, 0xb7, 0xad, 0x10, 0x5d, 0x58, + 0x25, 0x7a, 0x89, 0x68, 0x88, 0xa5, 0xa2, 0x71, 0x04, 0xf0, 0xee, 0x29, 0xf9, 0xf0, 0x96, 0x2f, + 0x29, 0x25, 0x7f, 0xb5, 0x94, 0xfc, 0x4b, 0xc2, 0x6e, 0x14, 0x09, 0xfb, 0xb8, 0xac, 0x1c, 0xc3, + 0x0e, 0xa3, 0xd1, 0x1d, 0x8d, 0x48, 0xe1, 0xfe, 0x1a, 0x8f, 0xdd, 0x4e, 0x0d, 0xe3, 0xfc, 0x15, + 0xca, 0x9f, 0x02, 0x1c, 0x95, 0xb6, 0x63, 0xb9, 0xed, 0x2f, 0xa1, 0xfa, 0x7f, 0x67, 0xc6, 0x03, + 0x92, 0xfa, 0x6f, 0x29, 0x63, 0xd6, 0x15, 0xcd, 0x7b, 0xd4, 0xc2, 0x8d, 0x0c, 0xd1, 0x9c, 0x22, + 0x8b, 0x2a, 0xf7, 0x58, 0xa4, 0xfc, 0x2b, 0x82, 0xf4, 0x30, 0xf9, 0x73, 0x26, 0x73, 0x00, 0x35, + 0x2e, 0x1a, 0xcb, 0xdb, 0x36, 0x93, 0xe3, 0xd3, 0x93, 0x28, 0x99, 0x68, 0xb5, 0x74, 0xa2, 0x32, + 0xd4, 0xb2, 0xf7, 0x67, 0xa3, 0xc8, 0x8f, 0x68, 0x00, 0x55, 0xfe, 0xdd, 0xda, 0xe4, 0xbc, 0xff, + 0xea, 0xf1, 0x26, 0xad, 0x00, 0x9c, 0xee, 0x3c, 0x58, 0x31, 0x61, 0xb7, 0xcc, 0x8a, 0x14, 0x78, + 0x91, 0x13, 0x7b, 0x7c, 0x39, 0x79, 0x4d, 0x74, 0xc3, 0xd4, 0x86, 0xda, 0xa0, 0x6f, 0x26, 0xdc, + 0xcd, 0x48, 0xde, 0x84, 0xda, 0x3b, 0x6a, 0xf3, 0x83, 0x9e, 0x98, 0x25, 0x51, 0x09, 0xe1, 0x60, + 0x55, 0x7c, 0xb8, 0x82, 0xa0, 0x6f, 0xa1, 0x9e, 0x89, 0x09, 0xcb, 0xc6, 0x7b, 0xb8, 0x46, 0xb1, + 0x96, 0xbe, 0x4f, 0x4c, 0x56, 0xf9, 0x4b, 0x84, 0xfd, 0xd5, 0x2b, 0xc3, 0x20, 0x8a, 0xd7, 0x48, + 0xe7, 0x0f, 0xf7, 0xa5, 0xf3, 0x78, 0x9d, 0x74, 0x26, 0xa9, 0x4a, 0xc5, 0xf2, 0x7d, 0x4c, 0x59, + 0xf9, 0xe5, 0x39, 0xa2, 0xba, 0x0d, 0xcd, 0x37, 0xd8, 0xd0, 0xcf, 0x8a, 0x5f, 0x94, 0x07, 0xe2, + 0x28, 0x26, 0x98, 0x6e, 0x98, 0x04, 0xab, 0x67, 0xda, 0xc4, 0x54, 0xb1, 0x7a, 0x2a, 0x55, 0x12, + 0xc1, 0x5c, 0x2d, 0x28, 0xa3, 0xda, 0xfd, 0xbe, 0x0a, 0x0f, 0x19, 0xf3, 0x1d, 0xd4, 0x22, 0x5e, + 0x3b, 0x93, 0x45, 0x3e, 0xad, 0xce, 0x53, 0x4d, 0xc2, 0x79, 0xc0, 0x74, 0x93, 0x7b, 0x7e, 0xf3, + 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x65, 0x07, 0x55, 0xdb, 0xac, 0x09, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index c7f00c3eb..c92882a2d 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -20,6 +20,8 @@ message PushNotificationRegistration { bytes grant = 10; bool allow_from_contacts_only = 11; string apn_topic = 12; + bool block_mentions = 13; + repeated bytes allowed_mentions_chat_list = 14; } message PushNotificationRegistrationResponse { diff --git a/protocol/pushnotificationclient/client.go b/protocol/pushnotificationclient/client.go index 3bae76e44..892691059 100644 --- a/protocol/pushnotificationclient/client.go +++ b/protocol/pushnotificationclient/client.go @@ -176,6 +176,10 @@ type Client struct { registrationSubscriptions []chan struct{} } +type MessagePersistence interface { + MessageByID(string) (*common.Message, error) +} + func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { return &Client{ quit: make(chan struct{}), diff --git a/protocol/transaction_validator.go b/protocol/transaction_validator.go index e56a76554..2e70cb088 100644 --- a/protocol/transaction_validator.go +++ b/protocol/transaction_validator.go @@ -16,6 +16,7 @@ import ( coretypes "github.com/status-im/status-go/eth-node/core/types" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" ) const ( @@ -92,7 +93,7 @@ func (t *TransactionValidator) verifyTransactionSignature(ctx context.Context, f return nil } -func (t *TransactionValidator) validateTokenTransfer(parameters *CommandParameters, transaction coretypes.Message) (*VerifyTransactionResponse, error) { +func (t *TransactionValidator) validateTokenTransfer(parameters *common.CommandParameters, transaction coretypes.Message) (*VerifyTransactionResponse, error) { data := transaction.Data() if len(data) != tokenTransferDataLength { @@ -153,7 +154,7 @@ func (t *TransactionValidator) validateToAddress(specifiedTo, actualTo string) b return t.addresses[actualTo] } -func (t *TransactionValidator) validateEthereumTransfer(parameters *CommandParameters, transaction coretypes.Message) (*VerifyTransactionResponse, error) { +func (t *TransactionValidator) validateEthereumTransfer(parameters *common.CommandParameters, transaction coretypes.Message) (*VerifyTransactionResponse, error) { toAddress := strings.ToLower(transaction.To().Hex()) if !t.validateToAddress(parameters.Address, toAddress) { @@ -197,7 +198,7 @@ type VerifyTransactionResponse struct { // The address the transaction was actually sent Address string - Message *Message + Message *common.Message Transaction *TransactionToValidate } @@ -205,7 +206,7 @@ type VerifyTransactionResponse struct { // If a negative response is returned, i.e `Valid` is false, it should // not be retried. // If an error is returned, validation can be retried. -func (t *TransactionValidator) validateTransaction(ctx context.Context, message coretypes.Message, parameters *CommandParameters, from *ecdsa.PublicKey) (*VerifyTransactionResponse, error) { +func (t *TransactionValidator) validateTransaction(ctx context.Context, message coretypes.Message, parameters *common.CommandParameters, from *ecdsa.PublicKey) (*VerifyTransactionResponse, error) { fromAddress := types.BytesToAddress(message.From().Bytes()) err := t.verifyTransactionSignature(ctx, from, fromAddress, parameters.TransactionHash, parameters.Signature) @@ -268,7 +269,7 @@ func (t *TransactionValidator) ValidateTransactions(ctx context.Context) ([]*Ver } validationResult.Message = message } else { - commandParameters := &CommandParameters{} + commandParameters := &common.CommandParameters{} commandParameters.TransactionHash = transaction.TransactionHash commandParameters.Signature = transaction.Signature @@ -304,7 +305,7 @@ func (t *TransactionValidator) ValidateTransactions(ctx context.Context) ([]*Ver return response, nil } -func (t *TransactionValidator) ValidateTransaction(ctx context.Context, parameters *CommandParameters, from *ecdsa.PublicKey) (*VerifyTransactionResponse, error) { +func (t *TransactionValidator) ValidateTransaction(ctx context.Context, parameters *common.CommandParameters, from *ecdsa.PublicKey) (*VerifyTransactionResponse, error) { t.logger.Debug("validating transaction", zap.Any("transaction", parameters), zap.Any("from", from)) hash := parameters.TransactionHash c, cancel := context.WithTimeout(ctx, 10*time.Second) diff --git a/protocol/transaction_validator_test.go b/protocol/transaction_validator_test.go index 503025fbb..1ca5b0d85 100644 --- a/protocol/transaction_validator_test.go +++ b/protocol/transaction_validator_test.go @@ -14,6 +14,7 @@ import ( coretypes "github.com/status-im/status-go/eth-node/core/types" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/tt" ) @@ -105,7 +106,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { Transaction coretypes.Message OverrideSignatureChatKey *ecdsa.PublicKey OverrideTransactionHash string - Parameters *CommandParameters + Parameters *common.CommandParameters WalletKey *ecdsa.PrivateKey From *ecdsa.PublicKey }{ @@ -123,7 +124,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", }, WalletKey: senderWalletKey, @@ -143,7 +144,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", Address: strings.ToLower(myAddress1.Hex()), }, @@ -162,7 +163,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", Address: strings.ToLower(myAddress1.Hex()), }, @@ -182,7 +183,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", Address: strings.ToLower(myAddress1.Hex()), }, @@ -202,7 +203,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { false, ), OverrideTransactionHash: "0xdd9202df5e2f3611b5b6b716aef2a3543cc0bdd7506f50926e0869b83c8383b9", - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", }, WalletKey: senderWalletKey, @@ -221,7 +222,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", Address: strings.ToLower(myAddress2.Hex()), }, @@ -240,7 +241,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", }, WalletKey: senderWalletKey, @@ -259,7 +260,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { nil, false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Value: "23", }, WalletKey: senderWalletKey, @@ -279,7 +280,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(transferFunction, myAddress1, big.NewInt(int64(23))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Value: "23", }, @@ -300,7 +301,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(transferFunction, myAddress1, big.NewInt(int64(23))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Address: strings.ToLower(myAddress1.Hex()), Value: "23", @@ -322,7 +323,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(transferFunction, myAddress1, big.NewInt(int64(13))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Value: "23", }, @@ -341,7 +342,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(transferFunction, myAddress1, big.NewInt(int64(23))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Address: strings.ToLower(myAddress1.Hex()), Value: "23", @@ -362,7 +363,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(transferFunction, myAddress1, big.NewInt(int64(23))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Address: strings.ToLower(senderAddress.Hex()), Value: "23", @@ -383,7 +384,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(transferFunction, myAddress2, big.NewInt(int64(23))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Address: strings.ToLower(myAddress1.Hex()), Value: "23", @@ -403,7 +404,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() { buildData(notTransferFunction, myAddress1, big.NewInt(int64(23))), false, ), - Parameters: &CommandParameters{ + Parameters: &common.CommandParameters{ Contract: contractString, Value: "23", }, diff --git a/services/ext/api.go b/services/ext/api.go index 9a5f302c4..e60654d3a 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -16,6 +16,7 @@ import ( "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/mailserver" "github.com/status-im/status-go/protocol" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/pushnotificationclient" @@ -315,8 +316,8 @@ func (api *PublicAPI) SetInstallationMetadata(installationID string, data *multi } type ApplicationMessagesResponse struct { - Messages []*protocol.Message `json:"messages"` - Cursor string `json:"cursor"` + Messages []*common.Message `json:"messages"` + Cursor string `json:"cursor"` } func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) { @@ -355,7 +356,7 @@ func (api *PublicAPI) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) return api.service.messenger.UpdateMessageOutgoingStatus(id, newOutgoingStatus) } -func (api *PublicAPI) SendChatMessage(ctx context.Context, message *protocol.Message) (*protocol.MessengerResponse, error) { +func (api *PublicAPI) SendChatMessage(ctx context.Context, message *common.Message) (*protocol.MessengerResponse, error) { return api.service.messenger.SendChatMessage(ctx, message) } diff --git a/services/shhext/api_nimbus.go b/services/shhext/api_nimbus.go index 1be38b016..5cf64e089 100644 --- a/services/shhext/api_nimbus.go +++ b/services/shhext/api_nimbus.go @@ -4,6 +4,7 @@ package shhext import ( "context" + "github.com/status-im/status-go/protocol/common" "github.com/ethereum/go-ethereum/log" @@ -465,8 +466,8 @@ func (api *NimbusPublicAPI) VerifyENSNames(details []enstypes.ENSDetails) (map[s } type ApplicationMessagesResponse struct { - Messages []*protocol.Message `json:"messages"` - Cursor string `json:"cursor"` + Messages []*common.Message `json:"messages"` + Cursor string `json:"cursor"` } func (api *NimbusPublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) { @@ -501,7 +502,7 @@ func (api *PublicAPI) StartMessenger() error { return api.service.StartMessenger() } -func (api *NimbusPublicAPI) SendChatMessage(ctx context.Context, message *protocol.Message) (*protocol.MessengerResponse, error) { +func (api *NimbusPublicAPI) SendChatMessage(ctx context.Context, message *common.Message) (*protocol.MessengerResponse, error) { return api.service.messenger.SendChatMessage(ctx, message) } From 8ee3c755106493e8a75a974a1aea72b2449e453a Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 2 Sep 2020 16:11:16 +0200 Subject: [PATCH 3/9] Support for basic push notification for mentions --- protocol/common/message_processor.go | 17 +- protocol/messenger.go | 23 +- protocol/push_notification_test.go | 130 +++++++ protocol/pushnotificationclient/client.go | 316 +++++++++++++----- .../pushnotificationclient/client_test.go | 52 ++- .../migrations/migrations.go | 62 +++- .../1599053776_add_chat_id_and_type.down.sql | 0 .../1599053776_add_chat_id_and_type.up.sql | 4 + .../pushnotificationclient/persistence.go | 8 +- protocol/pushnotificationserver/server.go | 2 +- 10 files changed, 479 insertions(+), 135 deletions(-) create mode 100644 protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.down.sql create mode 100644 protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.up.sql diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index a0138f3e2..a67f76f1a 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -325,11 +325,13 @@ func (p *MessageProcessor) SendPublic( } var newMessage *types.NewMessage + + messageSpec, err := p.protocol.BuildPublicMessage(p.identity, wrappedMessage) + if err != nil { + return nil, errors.Wrap(err, "failed to wrap a public message in the encryption layer") + } + if !rawMessage.SkipEncryption { - messageSpec, err := p.protocol.BuildPublicMessage(p.identity, wrappedMessage) - if err != nil { - return nil, errors.Wrap(err, "failed to wrap a public message in the encryption layer") - } newMessage, err = MessageSpecToWhisper(messageSpec) if err != nil { return nil, err @@ -354,6 +356,13 @@ func (p *MessageProcessor) SendPublic( return nil, err } + sentMessage := &SentMessage{ + Spec: messageSpec, + MessageIDs: [][]byte{messageID}, + } + + p.notifyOnSentMessage(sentMessage) + p.transport.Track([][]byte{messageID}, hash, newMessage) return messageID, nil diff --git a/protocol/messenger.go b/protocol/messenger.go index 25628ee50..14ca43701 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -227,7 +227,7 @@ func NewMessenger( pushNotificationClientConfig.Logger = logger pushNotificationClientConfig.InstallationID = installationID - pushNotificationClient := pushnotificationclient.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor) + pushNotificationClient := pushnotificationclient.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor, &sqlitePersistence{db: database}) handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database}) @@ -1490,8 +1490,7 @@ func (m *Messenger) reregisterForPushNotifications() error { return nil } - contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() - return m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs) + return m.pushNotificationClient.Reregister(m.pushNotificationOptions()) } func (m *Messenger) SaveContact(contact *Contact) error { @@ -1758,7 +1757,7 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *common.Message id, err := m.dispatchMessage(ctx, common.RawMessage{ LocalChatID: chat.ID, - SendPushNotification: m.featureFlags.PushNotifications && !chat.Public(), + SendPushNotification: m.featureFlags.PushNotifications, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, ResendAutomatically: true, @@ -3488,7 +3487,7 @@ func (m *Messenger) EnableSendingPushNotifications() error { return nil } -func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string) { +func (m *Messenger) pushNotificationOptions() *pushnotificationclient.RegistrationOptions { var contactIDs []*ecdsa.PublicKey var mutedChatIDs []string @@ -3510,7 +3509,10 @@ func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string } } - return contactIDs, mutedChatIDs + return &pushnotificationclient.RegistrationOptions{ + ContactIDs: contactIDs, + MutedChatIDs: mutedChatIDs, + } } // RegisterForPushNotification register deviceToken with any push notification server enabled @@ -3521,8 +3523,7 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke m.mutex.Lock() defer m.mutex.Unlock() - contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() - err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, contactIDs, mutedChatIDs) + err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, m.pushNotificationOptions()) if err != nil { m.logger.Error("failed to register for push notifications", zap.Error(err)) return err @@ -3546,8 +3547,7 @@ func (m *Messenger) EnablePushNotificationsFromContactsOnly() error { m.mutex.Lock() defer m.mutex.Unlock() - contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() - return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) + return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(m.pushNotificationOptions()) } // DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone @@ -3558,8 +3558,7 @@ func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { m.mutex.Lock() defer m.mutex.Unlock() - contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() - return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) + return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(m.pushNotificationOptions()) } // GetPushNotificationsServers returns the servers used for push notifications diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 9828f5cda..030d9ddb6 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -853,3 +853,133 @@ func (s *MessengerPushNotificationSuite) TestContactCode() { s.Require().NoError(alice.Shutdown()) s.Require().NoError(server.Shutdown()) } + +func (s *MessengerPushNotificationSuite) TestReceivePushNotificationMention() { + + bob := s.m + + serverKey, err := crypto.GenerateKey() + s.Require().NoError(err) + server := s.newPushNotificationServer(s.shh, serverKey) + + alice := s.newMessenger(s.shh) + // start alice and enable sending push notifications + s.Require().NoError(alice.Start()) + s.Require().NoError(alice.EnableSendingPushNotifications()) + bobInstallationIDs := []string{bob.installationID} + + // Create public chat and join for both alice and bob + chat := CreatePublicChat("status", s.m.transport) + err = bob.SaveChat(&chat) + s.Require().NoError(err) + + err = bob.Join(chat) + s.Require().NoError(err) + + err = alice.SaveChat(&chat) + s.Require().NoError(err) + + err = alice.Join(chat) + s.Require().NoError(err) + + // Register bob + err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom) + s.Require().NoError(err) + + err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN) + + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob.RetrieveAll() + if err != nil { + return err + } + registered, err := bob.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bobServers, err := bob.GetPushNotificationsServers() + s.Require().NoError(err) + + inputMessage := buildTestMessage(chat) + // message contains a mention + inputMessage.Text = "Hey @" + types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey)) + response, err := alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + messageIDString := response.Messages[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) + s.Require().NoError(err) + + var bobInfo []*pushnotificationclient.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + + bobInfo, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for bob + if len(bobInfo) != 1 { + return errors.New("info not fetched") + } + return nil + + }) + + s.Require().NoError(err) + + s.Require().NotEmpty(bobInfo) + s.Require().Equal(bob.installationID, bobInfo[0].InstallationID) + s.Require().Equal(bobServers[0].AccessToken, bobInfo[0].AccessToken) + s.Require().Equal(&bob.identity.PublicKey, bobInfo[0].PublicKey) + + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + + s.Require().NoError(err) + s.Require().NotNil(retrievedNotificationInfo) + s.Require().Len(retrievedNotificationInfo, 1) + + var sentNotification *pushnotificationclient.SentNotification + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID) + if err != nil { + return err + } + if sentNotification == nil { + return errors.New("sent notification not found") + } + if !sentNotification.Success { + return errors.New("sent notification not successul") + } + return nil + }) + s.Require().NoError(err) + s.Require().NoError(alice.Shutdown()) + s.Require().NoError(server.Shutdown()) +} diff --git a/protocol/pushnotificationclient/client.go b/protocol/pushnotificationclient/client.go index 892691059..66db70c29 100644 --- a/protocol/pushnotificationclient/client.go +++ b/protocol/pushnotificationclient/client.go @@ -7,6 +7,7 @@ import ( "crypto/cipher" "crypto/ecdsa" "crypto/rand" + "database/sql" "encoding/hex" "encoding/json" "errors" @@ -47,6 +48,8 @@ import ( const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 const staleQueryTimeInSeconds = 86400 +const mentionInstallationID = "mention" +const oneToOneChatIDLength = 65 // maxRegistrationRetries is the maximum number of attempts we do before giving up registering with a server const maxRegistrationRetries int64 = 12 @@ -103,13 +106,21 @@ type PushNotificationInfo struct { } type SentNotification struct { - PublicKey *ecdsa.PublicKey - InstallationID string - LastTriedAt int64 - RetryCount int64 - MessageID []byte - Success bool - Error protobuf.PushNotificationReport_ErrorType + PublicKey *ecdsa.PublicKey + InstallationID string + LastTriedAt int64 + RetryCount int64 + MessageID []byte + ChatID string + NotificationType protobuf.PushNotification_PushNotificationType + Success bool + Error protobuf.PushNotificationReport_ErrorType +} + +type RegistrationOptions struct { + PublicChatIDs []string + MutedChatIDs []string + ContactIDs []*ecdsa.PublicKey } func (s *SentNotification) HashedPublicKey() []byte { @@ -128,6 +139,9 @@ type Config struct { // only from contacts AllowFromContactsOnly bool + // BlockMentions indicates whether we should not receive notification for mentions + BlockMentions bool + // InstallationID is the installation-id for this device InstallationID string @@ -138,8 +152,13 @@ type Config struct { DefaultServers []*ecdsa.PublicKey } +type MessagePersistence interface { + MessageByID(string) (*common.Message, error) +} + type Client struct { - persistence *Persistence + persistence *Persistence + messagePersistence MessagePersistence config *Config @@ -176,17 +195,14 @@ type Client struct { registrationSubscriptions []chan struct{} } -type MessagePersistence interface { - MessageByID(string) (*common.Message, error) -} - -func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { +func New(persistence *Persistence, config *Config, processor *common.MessageProcessor, messagePersistence MessagePersistence) *Client { return &Client{ - quit: make(chan struct{}), - config: config, - messageProcessor: processor, - persistence: persistence, - reader: rand.Reader, + quit: make(chan struct{}), + config: config, + messageProcessor: processor, + messagePersistence: messagePersistence, + persistence: persistence, + reader: rand.Reader, } } @@ -202,8 +218,7 @@ func (c *Client) Start() error { return err } - c.subscribeForSentMessages() - c.subscribeForScheduledMessages() + c.subscribeForMessageEvents() // We start even if push notifications are disabled, as we might // actually be sending an unregister message @@ -303,7 +318,7 @@ func (c *Client) GetServers() ([]*PushNotificationServer, error) { return c.persistence.GetServers() } -func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { +func (c *Client) Reregister(options *RegistrationOptions) error { c.config.Logger.Debug("re-registering") if len(c.deviceToken) == 0 { c.config.Logger.Info("no device token, not registering") @@ -315,7 +330,7 @@ func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string return nil } - return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs) + return c.Register(c.deviceToken, c.apnTopic, c.tokenType, options) } // pickDefaultServesr picks n servers at random @@ -337,7 +352,7 @@ func (c *Client) pickDefaultServers(servers []*ecdsa.PublicKey) []*ecdsa.PublicK } // Register registers with all the servers -func (c *Client) Register(deviceToken, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { +func (c *Client) Register(deviceToken, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType, options *RegistrationOptions) error { // stop registration loop c.stopRegistrationLoop() @@ -368,12 +383,12 @@ func (c *Client) Register(deviceToken, apnTopic string, tokenType protobuf.PushN c.apnTopic = apnTopic c.tokenType = tokenType - registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) + registration, err := c.buildPushNotificationRegistrationMessage(options) if err != nil { return err } - err = c.saveLastPushNotificationRegistration(registration, contactIDs) + err = c.saveLastPushNotificationRegistration(registration, options.ContactIDs) if err != nil { return err } @@ -592,22 +607,22 @@ func (c *Client) DisableSending() { c.config.SendEnabled = false } -func (c *Client) EnablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { +func (c *Client) EnablePushNotificationsFromContactsOnly(options *RegistrationOptions) error { c.config.Logger.Debug("enabling push notification from contacts only") c.config.AllowFromContactsOnly = true if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled { c.config.Logger.Debug("re-registering after enabling push notifications from contacts only") - return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs) + return c.Register(c.deviceToken, c.apnTopic, c.tokenType, options) } return nil } -func (c *Client) DisablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { +func (c *Client) DisablePushNotificationsFromContactsOnly(options *RegistrationOptions) error { c.config.Logger.Debug("disabling push notification from contacts only") c.config.AllowFromContactsOnly = false if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled { c.config.Logger.Debug("re-registering after disabling push notifications from contacts only") - return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs) + return c.Register(c.deviceToken, c.apnTopic, c.tokenType, options) } return nil } @@ -648,37 +663,21 @@ func (c *Client) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { ) } -// subscribeForSentMessages subscribes for newly sent messages so we can check if we need to send a push notification -func (c *Client) subscribeForSentMessages() { +// subscribeForMessageEvents subscribes for newly sent/scheduled messages so we can check if we need to send a push notification +func (c *Client) subscribeForMessageEvents() { go func() { - c.config.Logger.Debug("subscribing for sent messages") - subscription := c.messageProcessor.SubscribeToSentMessages() + c.config.Logger.Debug("subscribing for message events") + sentMessagesSubscription := c.messageProcessor.SubscribeToSentMessages() + scheduledMessagesSubscription := c.messageProcessor.SubscribeToScheduledMessages() for { select { - case m, more := <-subscription: - if !more { - c.config.Logger.Debug("no more sent messages, quitting") - return - } - c.config.Logger.Debug("handling message sent") - if err := c.handleMessageSent(m); err != nil { - c.config.Logger.Error("failed to handle message", zap.Error(err)) - } - case <-c.quit: - return - } - } - }() -} - -// subscribeForScheduledMessages subscribes for messages scheduler for dispatch -func (c *Client) subscribeForScheduledMessages() { - go func() { - c.config.Logger.Debug("subscribing for scheduled messages") - subscription := c.messageProcessor.SubscribeToScheduledMessages() - for { - select { - case m, more := <-subscription: + // order is important, since both are asynchronous, we want to process + // first scheduled messages, and after sent messages, otherwise we might + // have some race conditions. + // This does not completely rules them out, but reduced the window + // where it might happen, a single channel should be used + // if this actually happens. + case m, more := <-scheduledMessagesSubscription: if !more { c.config.Logger.Debug("no more scheduled messages, quitting") return @@ -687,6 +686,16 @@ func (c *Client) subscribeForScheduledMessages() { if err := c.handleMessageScheduled(m); err != nil { c.config.Logger.Error("failed to handle message", zap.Error(err)) } + + case m, more := <-sentMessagesSubscription: + if !more { + c.config.Logger.Debug("no more sent messages, quitting") + return + } + c.config.Logger.Debug("handling message sent") + if err := c.handleMessageSent(m); err != nil { + c.config.Logger.Error("failed to handle message", zap.Error(err)) + } case <-c.quit: return } @@ -789,10 +798,9 @@ func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey, force bool) e return nil } -// handleMessageSent is called every time a message is sent. It will check if -// we need to notify on the message, and if so it will try to dispatch a push notification -// messages might be batched, if coming from datasync for example. +// handleMessageSent is called every time a message is sent func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error { + c.config.Logger.Debug("sent messages", zap.Any("messageIDs", sentMessage.MessageIDs)) // Ignore if we are not sending notifications @@ -801,6 +809,109 @@ func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error { return nil } + if sentMessage.PublicKey == nil { + return c.handlePublicMessageSent(sentMessage) + } + return c.handleDirectMessageSent(sentMessage) +} + +// saving to the database might happen after we fetch the message, so we retry +// for a reasonable amount of time before giving up +func (c *Client) getMessage(messageID string) (*common.Message, error) { + retries := 0 + for retries < 10 { + message, err := c.messagePersistence.MessageByID(messageID) + if err == sql.ErrNoRows { + retries++ + time.Sleep(300 * time.Millisecond) + continue + } else if err != nil { + return nil, err + } + + return message, nil + } + return nil, sql.ErrNoRows +} + +// handlePublicMessageSent handles public messages, we notify only on mentions +func (c *Client) handlePublicMessageSent(sentMessage *common.SentMessage) error { + // We always expect a single message, as we never batch them + if len(sentMessage.MessageIDs) != 1 { + return errors.New("batched public messages not handled") + } + + messageID := sentMessage.MessageIDs[0] + c.config.Logger.Debug("handling public messages", zap.Binary("messageID", messageID)) + tracked, err := c.persistence.TrackedMessage(messageID) + if err != nil { + return err + } + + if !tracked { + c.config.Logger.Debug("messageID not tracked, nothing to do", zap.Binary("messageID", messageID)) + } + + c.config.Logger.Debug("messageID tracked", zap.Binary("messageID", messageID)) + + message, err := c.getMessage(types.EncodeHex(messageID)) + if err != nil { + c.config.Logger.Error("could not retrieve message", zap.Error(err)) + } + + // This might happen if the user deleted their messages for example + if message == nil { + c.config.Logger.Warn("message not retrieved") + return nil + } + + c.config.Logger.Debug("message found", zap.Binary("messageID", messageID)) + for _, pkString := range message.Mentions { + c.config.Logger.Debug("handling mention", zap.String("publickey", pkString)) + pubkeyBytes, err := types.DecodeHex(pkString) + if err != nil { + return err + } + + publicKey, err := crypto.UnmarshalPubkey(pubkeyBytes) + if err != nil { + return err + } + + // we use a synthetic installationID for mentions, as all devices need to be notified + shouldNotify, err := c.shouldNotifyOn(publicKey, mentionInstallationID, messageID) + if err != nil { + return err + } + + c.config.Logger.Debug("should no mention", zap.Any("publickey", shouldNotify)) + // we send the notifications and return the info of the devices notified + infos, err := c.sendNotification(publicKey, nil, messageID, message.LocalChatID, protobuf.PushNotification_MENTION) + if err != nil { + return err + } + + // mark message as sent so we don't notify again + for _, i := range infos { + c.config.Logger.Debug("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) + if err := c.notifiedOn(publicKey, i.InstallationID, messageID, message.LocalChatID, protobuf.PushNotification_MESSAGE); err != nil { + return err + } + + } + + } + + return nil +} + +// handleDirectMessageSent handles one to ones and private group chat messages +// It will check if we need to notify on the message, and if so it will try to +// dispatch a push notification messages might be batched, if coming +// from datasync for example. +func (c *Client) handleDirectMessageSent(sentMessage *common.SentMessage) error { + c.config.Logger.Debug("handling direct messages", zap.Any("messageIDs", sentMessage.MessageIDs)) + publicKey := sentMessage.PublicKey // Collect the messageIDs we want to notify on @@ -853,8 +964,27 @@ func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error { c.config.Logger.Debug("actionable messages", zap.Any("message-ids", trackedMessageIDs), zap.Any("installation-ids", installationIDs)) + // Get message to check chatID. Again we use the first message for simplicity, but we should send one for each chatID. Messages though are very rarely batched. + message, err := c.getMessage(types.EncodeHex(trackedMessageIDs[0])) + if err != nil { + return err + } + + // This is not the prettiest. + // because chatIDs are asymettric, we need to check if it's a one-to-one message or a group chat message. + // to do that we fingerprint the chatID. + // If it's a public key, we use our own public key as chatID, which correspond to the chatID used by the other peer + // otherwise we use the group chat ID + var chatID string + if len(chatID) == oneToOneChatIDLength { + chatID = types.EncodeHex(crypto.FromECDSAPub(&c.config.Identity.PublicKey)) + } else { + // this is a group chat + chatID = message.ChatId + } + // we send the notifications and return the info of the devices notified - infos, err := c.sendNotification(publicKey, installationIDs, trackedMessageIDs[0]) + infos, err := c.sendNotification(publicKey, installationIDs, trackedMessageIDs[0], chatID, protobuf.PushNotification_MESSAGE) if err != nil { return err } @@ -864,7 +994,7 @@ func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error { for _, messageID := range trackedMessageIDs { c.config.Logger.Debug("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) - if err := c.notifiedOn(publicKey, i.InstallationID, messageID); err != nil { + if err := c.notifiedOn(publicKey, i.InstallationID, messageID, chatID, protobuf.PushNotification_MESSAGE); err != nil { return err } @@ -894,17 +1024,19 @@ func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID strin return c.persistence.ShouldSendNotificationFor(publicKey, installationID, messageID) } -// notifiedOn marks a combination of publickey/installationid/messageID as notified -func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { +// notifiedOn marks a combination of publickey/installationid/messageID/chatID/type as notified +func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte, chatID string, notificationType protobuf.PushNotification_PushNotificationType) error { return c.persistence.UpsertSentNotification(&SentNotification{ - PublicKey: publicKey, - LastTriedAt: time.Now().Unix(), - InstallationID: installationID, - MessageID: messageID, + PublicKey: publicKey, + LastTriedAt: time.Now().Unix(), + InstallationID: installationID, + MessageID: messageID, + ChatID: chatID, + NotificationType: notificationType, }) } -func (c *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { +func (c *Client) chatIDsHashes(chatIDs []string) [][]byte { var mutedChatListHashes [][]byte for _, chatID := range chatIDs { @@ -983,26 +1115,27 @@ func (c *Client) getVersion() uint64 { return c.lastPushNotificationRegistration.Version + 1 } -func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { - token := c.getToken(contactIDs) - allowedKeyList, err := c.allowedKeyList([]byte(token), contactIDs) +func (c *Client) buildPushNotificationRegistrationMessage(options *RegistrationOptions) (*protobuf.PushNotificationRegistration, error) { + token := c.getToken(options.ContactIDs) + allowedKeyList, err := c.allowedKeyList([]byte(token), options.ContactIDs) if err != nil { return nil, err } - options := &protobuf.PushNotificationRegistration{ - AccessToken: token, - TokenType: c.tokenType, - ApnTopic: c.apnTopic, - Version: c.getVersion(), - InstallationId: c.config.InstallationID, - DeviceToken: c.deviceToken, - AllowFromContactsOnly: c.config.AllowFromContactsOnly, - Enabled: c.config.RemoteNotificationsEnabled, - BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), - AllowedKeyList: allowedKeyList, - } - return options, nil + return &protobuf.PushNotificationRegistration{ + AccessToken: token, + TokenType: c.tokenType, + ApnTopic: c.apnTopic, + Version: c.getVersion(), + InstallationId: c.config.InstallationID, + DeviceToken: c.deviceToken, + AllowFromContactsOnly: c.config.AllowFromContactsOnly, + Enabled: c.config.RemoteNotificationsEnabled, + BlockedChatList: c.chatIDsHashes(options.MutedChatIDs), + BlockMentions: c.config.BlockMentions, + AllowedMentionsChatList: c.chatIDsHashes(options.PublicChatIDs), + AllowedKeyList: allowedKeyList, + }, nil } func (c *Client) buildPushNotificationUnregisterMessage() *protobuf.PushNotificationRegistration { @@ -1132,7 +1265,8 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis // sendNotification sends an actual notification to the push notification server. // the notification is sent using an ephemeral key to shield the real identity of the sender -func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []string, messageID []byte) ([]*PushNotificationInfo, error) { +func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []string, messageID []byte, chatID string, notificationType protobuf.PushNotification_PushNotificationType) ([]*PushNotificationInfo, error) { + // get latest push notification infos err := c.queryNotificationInfo(publicKey, false) if err != nil { @@ -1191,12 +1325,11 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] for _, infos := range actionableInfos { var pushNotifications []*protobuf.PushNotification for _, i := range infos { - // TODO: Add group chat ChatID pushNotifications = append(pushNotifications, &protobuf.PushNotification{ - Type: protobuf.PushNotification_MESSAGE, + Type: notificationType, // For now we set the ChatID to our own identity key, this will work fine for blocked users // and muted 1-to-1 chats, but not for group chats. - ChatId: common.Shake256([]byte(types.EncodeHex(crypto.FromECDSAPub(&c.config.Identity.PublicKey)))), + ChatId: common.Shake256([]byte(chatID)), AccessToken: i.AccessToken, PublicKey: common.HashPublicKey(publicKey), InstallationId: i.InstallationID, @@ -1255,7 +1388,7 @@ func (c *Client) resendNotification(pn *SentNotification) error { return err } - _, err = c.sendNotification(pn.PublicKey, []string{pn.InstallationID}, pn.MessageID) + _, err = c.sendNotification(pn.PublicKey, []string{pn.InstallationID}, pn.MessageID, pn.ChatID, pn.NotificationType) return err } @@ -1308,6 +1441,9 @@ func (c *Client) resendingLoop() error { // registrationLoop is a loop that is running when we need to register with a push notification server, it only runs when needed, it will quit if no work is necessary. func (c *Client) registrationLoop() error { + if c.lastPushNotificationRegistration == nil { + return nil + } for { c.config.Logger.Debug("running registration loop") servers, err := c.persistence.GetServers() diff --git a/protocol/pushnotificationclient/client_test.go b/protocol/pushnotificationclient/client_test.go index ed6613024..64518f428 100644 --- a/protocol/pushnotificationclient/client_test.go +++ b/protocol/pushnotificationclient/client_test.go @@ -58,7 +58,7 @@ func (s *ClientSuite) SetupTest() { InstallationID: s.installationID, } - s.client = New(s.persistence, config, nil) + s.client = New(s.persistence, config, nil, nil) } func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { @@ -74,6 +74,11 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { s.Require().NoError(err) contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} + options := &RegistrationOptions{ + ContactIDs: contactIDs, + MutedChatIDs: mutedChatList, + } + // Set random generator for uuid var seed int64 = 1 uuid.SetRand(rand.New(rand.NewSource(seed))) @@ -88,7 +93,7 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { // Set reader s.client.reader = bytes.NewReader([]byte(expectedUUID)) - options := &protobuf.PushNotificationRegistration{ + registration := &protobuf.PushNotificationRegistration{ Version: 1, AccessToken: expectedUUID, DeviceToken: testDeviceToken, @@ -97,24 +102,36 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { BlockedChatList: mutedChatListHashes, } - actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) + actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options) s.Require().NoError(err) - s.Require().Equal(options, actualMessage) + s.Require().Equal(registration, actualMessage) } func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() { mutedChatList := []string{"a", "b"} + publicChatList := []string{"c", "d"} - // build chat lish hashes + // build muted chat lish hashes var mutedChatListHashes [][]byte for _, chatID := range mutedChatList { mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID))) } + // build public chat lish hashes + var publicChatListHashes [][]byte + for _, chatID := range publicChatList { + publicChatListHashes = append(publicChatListHashes, common.Shake256([]byte(chatID))) + } + contactKey, err := crypto.GenerateKey() s.Require().NoError(err) contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} + options := &RegistrationOptions{ + ContactIDs: contactIDs, + MutedChatIDs: mutedChatList, + PublicChatIDs: publicChatList, + } // Set random generator for uuid var seed int64 = 1 @@ -144,21 +161,22 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsO // Set reader s.client.reader = bytes.NewReader([]byte(expectedUUID)) - options := &protobuf.PushNotificationRegistration{ - Version: 1, - AccessToken: expectedUUID, - DeviceToken: testDeviceToken, - InstallationId: s.installationID, - AllowFromContactsOnly: true, - Enabled: true, - BlockedChatList: mutedChatListHashes, - AllowedKeyList: [][]byte{encryptedToken}, + registration := &protobuf.PushNotificationRegistration{ + Version: 1, + AccessToken: expectedUUID, + DeviceToken: testDeviceToken, + InstallationId: s.installationID, + AllowFromContactsOnly: true, + Enabled: true, + BlockedChatList: mutedChatListHashes, + AllowedKeyList: [][]byte{encryptedToken}, + AllowedMentionsChatList: publicChatListHashes, } - actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) + actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options) s.Require().NoError(err) - s.Require().Equal(options, actualMessage) + s.Require().Equal(registration, actualMessage) } func (s *ClientSuite) TestHandleMessageScheduled() { @@ -183,7 +201,7 @@ func (s *ClientSuite) TestHandleMessageScheduled() { s.Require().True(response) // Save notification - s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID)) + s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID, chatID, protobuf.PushNotification_MESSAGE)) // Second time, should not notify response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID) diff --git a/protocol/pushnotificationclient/migrations/migrations.go b/protocol/pushnotificationclient/migrations/migrations.go index 106ce8bb9..a0f8f4e51 100644 --- a/protocol/pushnotificationclient/migrations/migrations.go +++ b/protocol/pushnotificationclient/migrations/migrations.go @@ -4,6 +4,8 @@ // 1593601729_initial_schema.up.sql (1.773kB) // 1597909626_add_server_type.down.sql (0) // 1597909626_add_server_type.up.sql (145B) +// 1599053776_add_chat_id_and_type.down.sql (0) +// 1599053776_add_chat_id_and_type.up.sql (264B) // doc.go (382B) package migrations @@ -128,7 +130,7 @@ func _1597909626_add_server_typeDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1597909626_add_server_type.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1597909727, 0)} + info := bindataFileInfo{name: "1597909626_add_server_type.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1598949727, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -148,11 +150,51 @@ func _1597909626_add_server_typeUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1597909626_add_server_type.up.sql", size: 145, mode: os.FileMode(0644), modTime: time.Unix(1597909704, 0)} + info := bindataFileInfo{name: "1597909626_add_server_type.up.sql", size: 145, mode: os.FileMode(0644), modTime: time.Unix(1598949727, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc8, 0x3f, 0xe0, 0xe7, 0x57, 0x0, 0x5d, 0x60, 0xf3, 0x55, 0x64, 0x71, 0x80, 0x3c, 0xca, 0x8, 0x61, 0xb5, 0x3c, 0xe, 0xa1, 0xe4, 0x61, 0xd1, 0x4e, 0xd8, 0xb2, 0x55, 0xdd, 0x87, 0x62, 0x9b}} return a, nil } +var __1599053776_add_chat_id_and_typeDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00") + +func _1599053776_add_chat_id_and_typeDownSqlBytes() ([]byte, error) { + return bindataRead( + __1599053776_add_chat_id_and_typeDownSql, + "1599053776_add_chat_id_and_type.down.sql", + ) +} + +func _1599053776_add_chat_id_and_typeDownSql() (*asset, error) { + bytes, err := _1599053776_add_chat_id_and_typeDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1599053776_add_chat_id_and_type.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1599053859, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} + return a, nil +} + +var __1599053776_add_chat_id_and_typeUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x4f\xce\xc9\x4c\xcd\x2b\x89\x2f\x06\x11\xc8\x12\xc5\x0a\x8e\x2e\x2e\x0a\xce\xfe\x3e\xa1\xbe\x7e\x0a\xc9\x19\x89\x25\xf1\x99\x29\x0a\x21\xae\x11\x21\xd6\x5c\x54\x30\x10\x45\x47\x49\x65\x41\xaa\x82\xa7\x5f\x88\x35\x17\x57\x68\x80\x8b\x63\x08\x69\xa6\x06\xbb\x86\xc0\xdd\x67\xab\xa0\xa4\xa4\x83\xc5\x70\x5b\x05\x43\x6b\x2e\x40\x00\x00\x00\xff\xff\x22\xaf\x2b\x87\x08\x01\x00\x00") + +func _1599053776_add_chat_id_and_typeUpSqlBytes() ([]byte, error) { + return bindataRead( + __1599053776_add_chat_id_and_typeUpSql, + "1599053776_add_chat_id_and_type.up.sql", + ) +} + +func _1599053776_add_chat_id_and_typeUpSql() (*asset, error) { + bytes, err := _1599053776_add_chat_id_and_typeUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1599053776_add_chat_id_and_type.up.sql", size: 264, mode: os.FileMode(0644), modTime: time.Unix(1599053853, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x7a, 0xf9, 0xc4, 0xa2, 0x96, 0x2e, 0xf9, 0x8f, 0x7, 0xf1, 0x1e, 0x73, 0x8a, 0xa6, 0x3a, 0x13, 0x4, 0x73, 0x82, 0x83, 0xb, 0xe3, 0xb5, 0x3b, 0x7e, 0xd, 0x23, 0xce, 0x98, 0xd4, 0xdc}} + return a, nil +} + var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00") func docGoBytes() ([]byte, error) { @@ -272,6 +314,10 @@ var _bindata = map[string]func() (*asset, error){ "1597909626_add_server_type.up.sql": _1597909626_add_server_typeUpSql, + "1599053776_add_chat_id_and_type.down.sql": _1599053776_add_chat_id_and_typeDownSql, + + "1599053776_add_chat_id_and_type.up.sql": _1599053776_add_chat_id_and_typeUpSql, + "doc.go": docGo, } @@ -316,11 +362,13 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "1593601729_initial_schema.down.sql": &bintree{_1593601729_initial_schemaDownSql, map[string]*bintree{}}, - "1593601729_initial_schema.up.sql": &bintree{_1593601729_initial_schemaUpSql, map[string]*bintree{}}, - "1597909626_add_server_type.down.sql": &bintree{_1597909626_add_server_typeDownSql, map[string]*bintree{}}, - "1597909626_add_server_type.up.sql": &bintree{_1597909626_add_server_typeUpSql, map[string]*bintree{}}, - "doc.go": &bintree{docGo, map[string]*bintree{}}, + "1593601729_initial_schema.down.sql": &bintree{_1593601729_initial_schemaDownSql, map[string]*bintree{}}, + "1593601729_initial_schema.up.sql": &bintree{_1593601729_initial_schemaUpSql, map[string]*bintree{}}, + "1597909626_add_server_type.down.sql": &bintree{_1597909626_add_server_typeDownSql, map[string]*bintree{}}, + "1597909626_add_server_type.up.sql": &bintree{_1597909626_add_server_typeUpSql, map[string]*bintree{}}, + "1599053776_add_chat_id_and_type.down.sql": &bintree{_1599053776_add_chat_id_and_typeDownSql, map[string]*bintree{}}, + "1599053776_add_chat_id_and_type.up.sql": &bintree{_1599053776_add_chat_id_and_typeUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.down.sql b/protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.up.sql b/protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.up.sql new file mode 100644 index 000000000..01749b6a2 --- /dev/null +++ b/protocol/pushnotificationclient/migrations/sql/1599053776_add_chat_id_and_type.up.sql @@ -0,0 +1,4 @@ +ALTER TABLE push_notification_client_sent_notifications ADD COLUMN chat_id TEXT; +ALTER TABLE push_notification_client_sent_notifications ADD COLUMN notification_type INT; + +UPDATE push_notification_client_sent_notifications SET chat_id = "", notification_type = 1; diff --git a/protocol/pushnotificationclient/persistence.go b/protocol/pushnotificationclient/persistence.go index 0bcc32e5f..793eefd24 100644 --- a/protocol/pushnotificationclient/persistence.go +++ b/protocol/pushnotificationclient/persistence.go @@ -277,7 +277,7 @@ func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecds } func (p *Persistence) UpsertSentNotification(n *SentNotification) error { - _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, last_tried_at, retry_count, success, error, hashed_public_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.LastTriedAt, n.RetryCount, n.Success, n.Error, n.HashedPublicKey()) + _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, last_tried_at, retry_count, success, error, hashed_public_key,chat_id, notification_type) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.LastTriedAt, n.RetryCount, n.Success, n.Error, n.HashedPublicKey(), n.ChatID, n.NotificationType) return err } @@ -287,7 +287,7 @@ func (p *Persistence) GetSentNotification(hashedPublicKey []byte, installationID InstallationID: installationID, MessageID: messageID, } - err := p.db.QueryRow(`SELECT retry_count, last_tried_at, error, success, public_key FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.RetryCount, &sentNotification.LastTriedAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes) + err := p.db.QueryRow(`SELECT retry_count, last_tried_at, error, success, public_key,chat_id,notification_type FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.RetryCount, &sentNotification.LastTriedAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes, &sentNotification.ChatID, &sentNotification.NotificationType) if err != nil { return nil, err } @@ -309,7 +309,7 @@ func (p *Persistence) UpdateNotificationResponse(messageID []byte, response *pro func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, error) { var notifications []*SentNotification - rows, err := p.db.Query(`SELECT retry_count, last_tried_at, error, success, public_key, installation_id, message_id FROM push_notification_client_sent_notifications WHERE NOT success AND error = ?`, protobuf.PushNotificationReport_WRONG_TOKEN) + rows, err := p.db.Query(`SELECT retry_count, last_tried_at, error, success, public_key, installation_id, message_id,chat_id, notification_type FROM push_notification_client_sent_notifications WHERE NOT success AND error = ?`, protobuf.PushNotificationReport_WRONG_TOKEN) if err != nil { return nil, err } @@ -318,7 +318,7 @@ func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, erro for rows.Next() { var publicKeyBytes []byte notification := &SentNotification{} - err = rows.Scan(¬ification.RetryCount, ¬ification.LastTriedAt, ¬ification.Error, ¬ification.Success, &publicKeyBytes, ¬ification.InstallationID, ¬ification.MessageID) + err = rows.Scan(¬ification.RetryCount, ¬ification.LastTriedAt, ¬ification.Error, ¬ification.Success, &publicKeyBytes, ¬ification.InstallationID, ¬ification.MessageID, ¬ification.ChatID, ¬ification.NotificationType) if err != nil { return nil, err } diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index e239278a0..73a1b4f7b 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -365,7 +365,7 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request InstallationId: pn.InstallationId, } - if pn.Type != protobuf.PushNotification_MESSAGE { + if pn.Type == protobuf.PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE { s.config.Logger.Warn("unhandled type") continue } From 18877cae6fb52dc74275ecb81e56f85fc2ef491e Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 3 Sep 2020 08:53:26 +0200 Subject: [PATCH 4/9] Handle mentions on server --- protocol/pushnotificationserver/server.go | 54 +++++++----- .../pushnotificationserver/server_test.go | 88 +++++++++++++++++++ 2 files changed, 120 insertions(+), 22 deletions(-) diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index 73a1b4f7b..bac78ac8d 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -152,10 +152,16 @@ func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, return nil } - response := s.buildPushNotificationRequestResponseAndSendNotification(&request) + response, requestsAndRegistrations := s.buildPushNotificationRequestResponse(&request) + //AndSendNotification(&request) if response == nil { return nil } + err = s.sendPushNotification(requestsAndRegistrations) + if err != nil { + s.config.Logger.Error("failed to send go rush notification", zap.Error(err)) + return err + } encodedMessage, err := proto.Marshal(response) if err != nil { return err @@ -334,23 +340,22 @@ func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotifica return response } -func (s *Server) blockedChatID(blockedChatIDs [][]byte, chatID []byte) bool { - for _, blockedChatID := range blockedChatIDs { - if bytes.Equal(blockedChatID, chatID) { +func (s *Server) contains(list [][]byte, chatID []byte) bool { + for _, list := range list { + if bytes.Equal(list, chatID) { return true } } return false } -// buildPushNotificationRequestResponseAndSendNotification will build a response -// and fire-and-forget send a query to the gorush instance -func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { +// buildPushNotificationRequestResponse will build a response +func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNotificationRequest) (*protobuf.PushNotificationResponse, []*RequestAndRegistration) { response := &protobuf.PushNotificationResponse{} // We don't even send a response in this case if request == nil || len(request.MessageId) == 0 { s.config.Logger.Warn("empty message id") - return nil + return nil, nil } response.MessageId = request.MessageId @@ -378,10 +383,10 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request report.Error = protobuf.PushNotificationReport_NOT_REGISTERED } else if registration.AccessToken != pn.AccessToken { report.Error = protobuf.PushNotificationReport_WRONG_TOKEN - } else if s.blockedChatID(registration.BlockedChatList, pn.ChatId) { + } else if s.contains(registration.BlockedChatList, pn.ChatId) { // We report as successful but don't send the notification report.Success = true - } else { + } else if s.isMessageNotification(pn) || s.isValidMentionNotification(pn, registration) { // For now we just assume that the notification will be successful requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ Request: pn, @@ -389,27 +394,24 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request }) report.Success = true } - response.Reports = append(response.Reports, report) } s.config.Logger.Info("built pn request") if len(requestAndRegistrations) == 0 { s.config.Logger.Warn("no request and registration") - return response + return response, nil } - // This can be done asynchronously + return response, requestAndRegistrations +} + +func (s *Server) sendPushNotification(requestAndRegistrations []*RequestAndRegistration) error { + if len(requestAndRegistrations) == 0 { + return nil + } goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations) - err := sendGoRushNotification(goRushRequest, s.config.GorushURL, s.config.Logger) - if err != nil { - s.config.Logger.Error("failed to send go rush notification", zap.Error(err)) - // TODO: handle this error? - // GoRush will not let us know that the sending of the push notification has failed, - // so this likely mean that the actual HTTP request has failed, or there was some unexpected error - } - - return response + return sendGoRushNotification(goRushRequest, s.config.GorushURL, s.config.Logger) } // listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key @@ -468,3 +470,11 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ return response } + +func (s *Server) isMessageNotification(pn *protobuf.PushNotification) bool { + return pn.Type == protobuf.PushNotification_MESSAGE +} + +func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { + return !registration.BlockMentions && pn.Type == protobuf.PushNotification_MENTION && s.contains(registration.AllowedMentionsChatList, pn.ChatId) +} diff --git a/protocol/pushnotificationserver/server_test.go b/protocol/pushnotificationserver/server_test.go index 3cdde09a6..68f15f686 100644 --- a/protocol/pushnotificationserver/server_test.go +++ b/protocol/pushnotificationserver/server_test.go @@ -582,3 +582,91 @@ func (s *ServerSuite) TestbuildPushNotificationQueryResponseWithFiltering() { s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) s.Require().Equal(allowedKeyList, queryResponse.Info[0].AllowedKeyList) } + +func (s *ServerSuite) TestPushNotificationMentions() { + existingChatID := []byte("existing-chat-id") + nonExistingChatID := []byte("non-existing-chat-id") + registration := &protobuf.PushNotificationRegistration{ + DeviceToken: "abc", + AccessToken: s.accessToken, + Grant: s.grant, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + InstallationId: s.installationID, + AllowedMentionsChatList: [][]byte{existingChatID}, + Version: 1, + } + payload, err := proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + pushNotificationRequest := &protobuf.PushNotificationRequest{ + MessageId: []byte("message-id"), + Requests: []*protobuf.PushNotification{ + { + AccessToken: s.accessToken, + PublicKey: common.HashPublicKey(&s.key.PublicKey), + ChatId: existingChatID, + InstallationId: s.installationID, + Type: protobuf.PushNotification_MENTION, + }, + { + AccessToken: s.accessToken, + PublicKey: common.HashPublicKey(&s.key.PublicKey), + ChatId: nonExistingChatID, + InstallationId: s.installationID, + Type: protobuf.PushNotification_MENTION, + }, + }, + } + + pushNotificationResponse, requestAndRegistrations := s.server.buildPushNotificationRequestResponse(pushNotificationRequest) + s.Require().NotNil(pushNotificationResponse) + s.Require().NotNil(requestAndRegistrations) + + // only one should succeed + s.Require().Len(requestAndRegistrations, 1) +} + +func (s *ServerSuite) TestPushNotificationDisabledMentions() { + existingChatID := []byte("existing-chat-id") + registration := &protobuf.PushNotificationRegistration{ + DeviceToken: "abc", + AccessToken: s.accessToken, + Grant: s.grant, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + BlockMentions: true, + InstallationId: s.installationID, + AllowedMentionsChatList: [][]byte{existingChatID}, + Version: 1, + } + payload, err := proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + pushNotificationRequest := &protobuf.PushNotificationRequest{ + MessageId: []byte("message-id"), + Requests: []*protobuf.PushNotification{ + { + AccessToken: s.accessToken, + PublicKey: common.HashPublicKey(&s.key.PublicKey), + ChatId: existingChatID, + InstallationId: s.installationID, + Type: protobuf.PushNotification_MENTION, + }, + }, + } + + pushNotificationResponse, requestAndRegistrations := s.server.buildPushNotificationRequestResponse(pushNotificationRequest) + s.Require().NotNil(pushNotificationResponse) + s.Require().Nil(requestAndRegistrations) +} From 0d998e1858d7bfa3ef2b58520c5092d80c7f3ae8 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 3 Sep 2020 08:58:58 +0200 Subject: [PATCH 5/9] Show correct text in case of mentions --- protocol/pushnotificationserver/gorush.go | 11 +++++++++-- protocol/pushnotificationserver/gorush_test.go | 9 ++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/protocol/pushnotificationserver/gorush.go b/protocol/pushnotificationserver/gorush.go index 18b28ce71..29f9483f9 100644 --- a/protocol/pushnotificationserver/gorush.go +++ b/protocol/pushnotificationserver/gorush.go @@ -12,7 +12,8 @@ import ( "github.com/status-im/status-go/protocol/protobuf" ) -const defaultNotificationMessage = "You have a new message" +const defaultNewMessageNotificationText = "You have a new message" +const defaultMentionNotificationText = "Someone mentioned you" type GoRushRequestData struct { EncryptedMessage string `json:"encryptedMessage"` @@ -52,11 +53,17 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ for _, requestAndRegistration := range requestAndRegistrations { request := requestAndRegistration.Request registration := requestAndRegistration.Registration + var text string + if request.Type == protobuf.PushNotification_MESSAGE { + text = defaultNewMessageNotificationText + } else { + text = defaultMentionNotificationText + } goRushRequests.Notifications = append(goRushRequests.Notifications, &GoRushRequestNotification{ Tokens: []string{registration.DeviceToken}, Platform: tokenTypeToGoRushPlatform(registration.TokenType), - Message: defaultNotificationMessage, + Message: text, Topic: registration.ApnTopic, Data: &GoRushRequestData{ EncryptedMessage: types.EncodeHex(request.Message), diff --git a/protocol/pushnotificationserver/gorush_test.go b/protocol/pushnotificationserver/gorush_test.go index e6e99128e..7b949f4e2 100644 --- a/protocol/pushnotificationserver/gorush_test.go +++ b/protocol/pushnotificationserver/gorush_test.go @@ -33,6 +33,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { { Request: &protobuf.PushNotification{ ChatId: chatID, + Type: protobuf.PushNotification_MESSAGE, PublicKey: publicKey1, InstallationId: installationID1, Message: message1, @@ -45,6 +46,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { { Request: &protobuf.PushNotification{ ChatId: chatID, + Type: protobuf.PushNotification_MESSAGE, PublicKey: publicKey1, InstallationId: installationID2, Message: message2, @@ -57,6 +59,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { { Request: &protobuf.PushNotification{ ChatId: chatID, + Type: protobuf.PushNotification_MENTION, PublicKey: publicKey2, InstallationId: installationID3, Message: message3, @@ -73,7 +76,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { { Tokens: []string{token1}, Platform: platform1, - Message: defaultNotificationMessage, + Message: defaultNewMessageNotificationText, Data: &GoRushRequestData{ EncryptedMessage: hexMessage1, ChatID: types.EncodeHex(chatID), @@ -83,7 +86,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { { Tokens: []string{token2}, Platform: platform2, - Message: defaultNotificationMessage, + Message: defaultNewMessageNotificationText, Data: &GoRushRequestData{ EncryptedMessage: hexMessage2, ChatID: types.EncodeHex(chatID), @@ -93,7 +96,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { { Tokens: []string{token3}, Platform: platform3, - Message: defaultNotificationMessage, + Message: defaultMentionNotificationText, Data: &GoRushRequestData{ EncryptedMessage: hexMessage3, ChatID: types.EncodeHex(chatID), From 4e18d21129828c08dc0c9ac7137e101cd15d6f9f Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 3 Sep 2020 09:19:46 +0200 Subject: [PATCH 6/9] Re-register on adding or removing a chat --- protocol/message_handler.go | 6 ++-- protocol/messenger.go | 41 ++++++++++++++++++++--- protocol/pushnotificationserver/server.go | 11 +++--- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/protocol/message_handler.go b/protocol/message_handler.go index 67ec97c0f..574ccb393 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -248,11 +248,11 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta return nil } -func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) error { +func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) (bool, error) { chatID := message.Id _, ok := state.AllChats[chatID] if ok { - return nil + return false, nil } chat := CreatePublicChat(chatID, state.Timesource) @@ -260,7 +260,7 @@ func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessage state.AllChats[chat.ID] = &chat state.ModifiedChats[chat.ID] = true - return nil + return true, nil } func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error { diff --git a/protocol/messenger.go b/protocol/messenger.go index 14ca43701..6187d50ea 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1358,7 +1358,7 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bo } func (m *Messenger) saveChat(chat *Chat) error { - _, ok := m.allChats[chat.ID] + previousChat, ok := m.allChats[chat.ID] if chat.OneToOne() { name, identicon, err := generateAliasAndIdenticon(chat.ID) if err != nil { @@ -1370,11 +1370,21 @@ func (m *Messenger) saveChat(chat *Chat) error { } // Sync chat if it's a new active public chat if !ok && chat.Active && chat.Public() { + if err := m.syncPublicChat(context.Background(), chat); err != nil { return err } } + // We check if it's a new chat, or chat.Active has changed + if chat.Public() && (!ok && chat.Active) || (ok && chat.Active != previousChat.Active) { + // Re-register for push notifications, as we want to receive mentions + if err := m.reregisterForPushNotifications(); err != nil { + return err + } + + } + err := m.persistence.SaveChat(*chat) if err != nil { return err @@ -1424,7 +1434,12 @@ func (m *Messenger) DeleteChat(chatID string) error { if err != nil { return err } - delete(m.allChats, chatID) + chat, ok := m.allChats[chatID] + + if ok && chat.Active && chat.Public() { + delete(m.allChats, chatID) + return m.reregisterForPushNotifications() + } return nil } @@ -2250,12 +2265,23 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationPublicChat) logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p)) - err = m.handler.HandleSyncInstallationPublicChat(messageState, p) + added, err := m.handler.HandleSyncInstallationPublicChat(messageState, p) if err != nil { logger.Warn("failed to handle SyncInstallationPublicChat", zap.Error(err)) continue } + // We re-register as we want to receive mentions from the newly joined public chat + if added { + logger.Debug("newly synced public chat, re-registering for push notifications") + err := m.reregisterForPushNotifications() + if err != nil { + + logger.Warn("could not re-register for push notifications", zap.Error(err)) + continue + } + } + case protobuf.RequestAddressForTransaction: command := msg.ParsedMessage.Interface().(protobuf.RequestAddressForTransaction) logger.Debug("Handling RequestAddressForTransaction", zap.Any("message", command)) @@ -3490,6 +3516,7 @@ func (m *Messenger) EnableSendingPushNotifications() error { func (m *Messenger) pushNotificationOptions() *pushnotificationclient.RegistrationOptions { var contactIDs []*ecdsa.PublicKey var mutedChatIDs []string + var publicChatIDs []string for _, contact := range m.allContacts { if contact.IsAdded() { @@ -3507,11 +3534,15 @@ func (m *Messenger) pushNotificationOptions() *pushnotificationclient.Registrati if chat.Muted { mutedChatIDs = append(mutedChatIDs, chat.ID) } + if chat.Active && chat.Public() { + publicChatIDs = append(publicChatIDs, chat.ID) + } } return &pushnotificationclient.RegistrationOptions{ - ContactIDs: contactIDs, - MutedChatIDs: mutedChatIDs, + ContactIDs: contactIDs, + MutedChatIDs: mutedChatIDs, + PublicChatIDs: publicChatIDs, } } diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index bac78ac8d..1db29bb2d 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -383,10 +383,13 @@ func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNoti report.Error = protobuf.PushNotificationReport_NOT_REGISTERED } else if registration.AccessToken != pn.AccessToken { report.Error = protobuf.PushNotificationReport_WRONG_TOKEN - } else if s.contains(registration.BlockedChatList, pn.ChatId) { + } else if s.contains(registration.BlockedChatList, pn.ChatId) || !s.isValidMentionNotification(pn, registration) { // We report as successful but don't send the notification + // for privacy reasons, as otherwise we would disclose that + // the sending client has been blocked or that the registering + // client has not joined a given public chat report.Success = true - } else if s.isMessageNotification(pn) || s.isValidMentionNotification(pn, registration) { + } else { // For now we just assume that the notification will be successful requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ Request: pn, @@ -471,10 +474,6 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ return response } -func (s *Server) isMessageNotification(pn *protobuf.PushNotification) bool { - return pn.Type == protobuf.PushNotification_MESSAGE -} - func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { return !registration.BlockMentions && pn.Type == protobuf.PushNotification_MENTION && s.contains(registration.AllowedMentionsChatList, pn.ChatId) } From 2d525f95035251d02c289c7f68a199353dfbeb7d Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 3 Sep 2020 09:30:03 +0200 Subject: [PATCH 7/9] Set block mentions --- appdatabase/migrations/bindata.go | 54 +++++++++++++++++-- .../sql/0010_add_block_mentions.down.sql | 1 + .../sql/0010_add_block_mentions.up.sql | 1 + multiaccounts/accounts/database.go | 13 ++++- peers/peerpool_test.go | 34 ++---------- protocol/messenger.go | 22 ++++++++ protocol/pushnotificationclient/client.go | 20 +++++++ services/ext/api.go | 16 ++++++ services/ext/service.go | 1 + 9 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 appdatabase/migrations/sql/0010_add_block_mentions.down.sql create mode 100644 appdatabase/migrations/sql/0010_add_block_mentions.up.sql diff --git a/appdatabase/migrations/bindata.go b/appdatabase/migrations/bindata.go index a4e8248ee..f2511c2c9 100644 --- a/appdatabase/migrations/bindata.go +++ b/appdatabase/migrations/bindata.go @@ -15,6 +15,8 @@ // 0008_add_push_notifications.up.sql (349B) // 0009_enable_sending_push_notifications.down.sql (49B) // 0009_enable_sending_push_notifications.up.sql (49B) +// 0010_add_block_mentions.down.sql (83B) +// 0010_add_block_mentions.up.sql (89B) // doc.go (74B) package migrations @@ -359,7 +361,7 @@ func _0009_enable_sending_push_notificationsDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0009_enable_sending_push_notifications.down.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1597918101, 0)} + info := bindataFileInfo{name: "0009_enable_sending_push_notifications.down.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1598949727, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe9, 0xae, 0x1b, 0x41, 0xcb, 0x9c, 0x2c, 0x93, 0xc6, 0x2a, 0x77, 0x3, 0xb9, 0x51, 0xe0, 0x68, 0x68, 0x0, 0xf7, 0x5b, 0xb3, 0x1e, 0x94, 0x44, 0xba, 0x9c, 0xd0, 0x3b, 0x80, 0x21, 0x6f, 0xb5}} return a, nil } @@ -379,11 +381,51 @@ func _0009_enable_sending_push_notificationsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0009_enable_sending_push_notifications.up.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1597918090, 0)} + info := bindataFileInfo{name: "0009_enable_sending_push_notifications.up.sql", size: 49, mode: os.FileMode(0644), modTime: time.Unix(1598949727, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1b, 0x80, 0xe4, 0x9c, 0xc8, 0xb8, 0xd5, 0xef, 0xce, 0x74, 0x9b, 0x7b, 0xdd, 0xa, 0x99, 0x1e, 0xef, 0x7f, 0xb8, 0x99, 0x84, 0x4, 0x0, 0x6b, 0x1d, 0x2c, 0xa, 0xf8, 0x2c, 0x4f, 0xb5, 0x44}} return a, nil } +var __0010_add_block_mentionsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x28\x4e\x2d\x29\xc9\xcc\x4b\x2f\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x2b\x8e\x4f\xca\xc9\x4f\xce\x8e\xcf\x4d\xcd\x03\x73\x15\x9c\xfc\xfd\x7d\x5c\x1d\xfd\x14\x5c\x5c\xdd\x1c\x43\x7d\x42\x14\xdc\x1c\x7d\x82\x5d\xad\xb9\x00\x01\x00\x00\xff\xff\xa8\x45\x75\x3b\x53\x00\x00\x00") + +func _0010_add_block_mentionsDownSqlBytes() ([]byte, error) { + return bindataRead( + __0010_add_block_mentionsDownSql, + "0010_add_block_mentions.down.sql", + ) +} + +func _0010_add_block_mentionsDownSql() (*asset, error) { + bytes, err := _0010_add_block_mentionsDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "0010_add_block_mentions.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1599117686, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6d, 0x9e, 0x27, 0x1e, 0xba, 0x9f, 0xca, 0xae, 0x98, 0x2e, 0x6e, 0xe3, 0xdd, 0xac, 0x73, 0x34, 0x4e, 0x69, 0x92, 0xb5, 0xf6, 0x9, 0xab, 0x50, 0x35, 0xd, 0xee, 0xeb, 0x3e, 0xcc, 0x7e, 0xce}} + return a, nil +} + +var __0010_add_block_mentionsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x1c\xc7\x31\x0a\x42\x31\x0c\x06\xe0\xdd\x53\xfc\xf7\x70\xca\xb3\x79\x53\x7c\x05\x6d\xe7\xa2\xa5\x6a\x50\x53\x21\xf1\xfe\x82\xe3\x47\x52\xf8\x84\x42\x8b\x30\x7c\x44\xa8\xdd\x1d\x94\x12\x0e\x59\xea\x71\xc3\xe7\xeb\x8f\x66\x33\xf4\xa6\xfd\x12\x3a\xcd\xdb\xf5\x35\xfb\xb3\xbd\x87\xfd\x89\x25\x67\x61\xda\x90\x78\xa5\x2a\x05\x2b\xc9\x99\xf7\xbb\x5f\x00\x00\x00\xff\xff\x2b\x4e\x3f\xc5\x59\x00\x00\x00") + +func _0010_add_block_mentionsUpSqlBytes() ([]byte, error) { + return bindataRead( + __0010_add_block_mentionsUpSql, + "0010_add_block_mentions.up.sql", + ) +} + +func _0010_add_block_mentionsUpSql() (*asset, error) { + bytes, err := _0010_add_block_mentionsUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "0010_add_block_mentions.up.sql", size: 89, mode: os.FileMode(0644), modTime: time.Unix(1599118789, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd7, 0x23, 0x85, 0xa2, 0xb5, 0xb6, 0xb4, 0x3f, 0xdc, 0x4e, 0xff, 0xe2, 0x6b, 0x66, 0x68, 0x5e, 0xb2, 0xb4, 0x14, 0xb2, 0x1b, 0x4d, 0xb1, 0xce, 0xf7, 0x6, 0x58, 0xa7, 0xaf, 0x93, 0x3f, 0x25}} + return a, nil +} + var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00") func docGoBytes() ([]byte, error) { @@ -525,6 +567,10 @@ var _bindata = map[string]func() (*asset, error){ "0009_enable_sending_push_notifications.up.sql": _0009_enable_sending_push_notificationsUpSql, + "0010_add_block_mentions.down.sql": _0010_add_block_mentionsDownSql, + + "0010_add_block_mentions.up.sql": _0010_add_block_mentionsUpSql, + "doc.go": docGo, } @@ -584,7 +630,9 @@ var _bintree = &bintree{nil, map[string]*bintree{ "0008_add_push_notifications.up.sql": &bintree{_0008_add_push_notificationsUpSql, map[string]*bintree{}}, "0009_enable_sending_push_notifications.down.sql": &bintree{_0009_enable_sending_push_notificationsDownSql, map[string]*bintree{}}, "0009_enable_sending_push_notifications.up.sql": &bintree{_0009_enable_sending_push_notificationsUpSql, map[string]*bintree{}}, - "doc.go": &bintree{docGo, map[string]*bintree{}}, + "0010_add_block_mentions.down.sql": &bintree{_0010_add_block_mentionsDownSql, map[string]*bintree{}}, + "0010_add_block_mentions.up.sql": &bintree{_0010_add_block_mentionsUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/appdatabase/migrations/sql/0010_add_block_mentions.down.sql b/appdatabase/migrations/sql/0010_add_block_mentions.down.sql new file mode 100644 index 000000000..d7c56e1ca --- /dev/null +++ b/appdatabase/migrations/sql/0010_add_block_mentions.down.sql @@ -0,0 +1 @@ +ALTER settings ADD COLUMN push_notifications_block_mentions BOOLEAN DEFAULT FALSE; diff --git a/appdatabase/migrations/sql/0010_add_block_mentions.up.sql b/appdatabase/migrations/sql/0010_add_block_mentions.up.sql new file mode 100644 index 000000000..6d7edcf97 --- /dev/null +++ b/appdatabase/migrations/sql/0010_add_block_mentions.up.sql @@ -0,0 +1 @@ +ALTER TABLE settings ADD COLUMN push_notifications_block_mentions BOOLEAN DEFAULT FALSE; diff --git a/multiaccounts/accounts/database.go b/multiaccounts/accounts/database.go index 740893a6d..040e4cf50 100644 --- a/multiaccounts/accounts/database.go +++ b/multiaccounts/accounts/database.go @@ -70,7 +70,9 @@ type Settings struct { PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled?,omitempty"` // PushNotificationsFromContactsOnly indicates whether we should only receive push notifications from contacts PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only?,omitempty"` - RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` + // PushNotificationsBlockMentions indicates whether we should receive notifications for mentions + PushNotificationsBlockMentions bool `json:"push-notifications-block-mentions?,omitempty"` + RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` // RemotePushNotificationsEnabled indicates whether we should be using remote notifications (ios only for now) RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled?,omitempty"` SigningPhrase string `json:"signing-phrase"` @@ -273,6 +275,12 @@ func (db *Database) SaveSetting(setting string, value interface{}) error { return ErrInvalidConfig } update, err = db.db.Prepare("UPDATE settings SET push_notifications_from_contacts_only = ? WHERE synthetic_id = 'id'") + case "push-notifications-block-mentions?": + _, ok := value.(bool) + if !ok { + return ErrInvalidConfig + } + update, err = db.db.Prepare("UPDATE settings SET push_notifications_block_mentions = ? WHERE synthetic_id = 'id'") case "send-push-notifications?": _, ok := value.(bool) if !ok { @@ -337,7 +345,7 @@ func (db *Database) GetNodeConfig(nodecfg interface{}) error { func (db *Database) GetSettings() (Settings, error) { var s Settings - err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, push_notifications_server_enabled, push_notifications_from_contacts_only, remote_push_notifications_enabled, send_push_notifications, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan( + err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, push_notifications_server_enabled, push_notifications_from_contacts_only, remote_push_notifications_enabled, send_push_notifications, push_notifications_block_mentions, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan( &s.Address, &s.ChaosMode, &s.Currency, @@ -364,6 +372,7 @@ func (db *Database) GetSettings() (Settings, error) { &s.PushNotificationsFromContactsOnly, &s.RemotePushNotificationsEnabled, &s.SendPushNotifications, + &s.PushNotificationsBlockMentions, &s.PhotoPath, &s.PinnedMailserver, &s.PreferredName, diff --git a/peers/peerpool_test.go b/peers/peerpool_test.go index 45e7018e8..e7a3684c4 100644 --- a/peers/peerpool_test.go +++ b/peers/peerpool_test.go @@ -30,50 +30,21 @@ import ( "github.com/status-im/status-go/whisper/v6" ) -// GetFreePort asks the kernel for a free open port that is ready to use. -func getFreePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil -} - type PeerPoolSimulationSuite struct { suite.Suite bootnode *p2p.Server peers []*p2p.Server discovery []discovery.Discovery - port uint16 rendezvousServer *server.Server } func TestPeerPoolSimulationSuite(t *testing.T) { s := &PeerPoolSimulationSuite{} - port, err := getFreePort() - if err != nil { - panic(err) - } - s.port = uint16(port) - suite.Run(t, s) } -func (s *PeerPoolSimulationSuite) nextPort() uint16 { - port, err := getFreePort() - s.Require().NoError(err) - return uint16(port) -} - func (s *PeerPoolSimulationSuite) SetupTest() { - bootnodePort := s.nextPort() key, _ := crypto.GenerateKey() name := common.MakeName("bootnode", "1.0") // 127.0.0.1 is invalidated by discovery v5 @@ -81,13 +52,14 @@ func (s *PeerPoolSimulationSuite) SetupTest() { Config: p2p.Config{ MaxPeers: 10, Name: name, - ListenAddr: fmt.Sprintf("0.0.0.0:%d", bootnodePort), + ListenAddr: ":0", PrivateKey: key, DiscoveryV5: true, NoDiscovery: true, }, } s.Require().NoError(s.bootnode.Start()) + bootnodePort := uint16(s.bootnode.NodeInfo().Ports.Listener) bootnodeV5 := discv5.NewNode(s.bootnode.DiscV5.Self().ID, net.ParseIP("127.0.0.1"), bootnodePort, bootnodePort) // 1 peer to initiate connection, 1 peer as a first candidate, 1 peer - for failover @@ -100,7 +72,7 @@ func (s *PeerPoolSimulationSuite) SetupTest() { Config: p2p.Config{ MaxPeers: 10, Name: common.MakeName("peer-"+strconv.Itoa(i), "1.0"), - ListenAddr: fmt.Sprintf("0.0.0.0:%d", s.nextPort()), + ListenAddr: ":0", PrivateKey: key, NoDiscovery: true, BootstrapNodesV5: []*discv5.Node{bootnodeV5}, diff --git a/protocol/messenger.go b/protocol/messenger.go index 6187d50ea..88a1fecb9 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3592,6 +3592,28 @@ func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(m.pushNotificationOptions()) } +// 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()) +} + // GetPushNotificationsServers returns the servers used for push notifications func (m *Messenger) GetPushNotificationsServers() ([]*pushnotificationclient.PushNotificationServer, error) { if m.pushNotificationClient == nil { diff --git a/protocol/pushnotificationclient/client.go b/protocol/pushnotificationclient/client.go index 66db70c29..7593fd6be 100644 --- a/protocol/pushnotificationclient/client.go +++ b/protocol/pushnotificationclient/client.go @@ -627,6 +627,26 @@ func (c *Client) DisablePushNotificationsFromContactsOnly(options *RegistrationO return nil } +func (c *Client) EnablePushNotificationsBlockMentions(options *RegistrationOptions) error { + c.config.Logger.Debug("disabling push notifications for mentions") + c.config.BlockMentions = true + if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled { + c.config.Logger.Debug("re-registering after disabling push notifications for mentions") + return c.Register(c.deviceToken, c.apnTopic, c.tokenType, options) + } + return nil +} + +func (c *Client) DisablePushNotificationsBlockMentions(options *RegistrationOptions) error { + c.config.Logger.Debug("enabling push notifications for mentions") + c.config.BlockMentions = false + if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled { + c.config.Logger.Debug("re-registering after enabling push notifications for mentions") + return c.Register(c.deviceToken, c.apnTopic, c.tokenType, options) + } + return nil +} + func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { c, err := aes.NewCipher(key) if err != nil { diff --git a/services/ext/api.go b/services/ext/api.go index e60654d3a..1cb89ad0c 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -506,6 +506,22 @@ func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Conte return api.service.messenger.DisablePushNotificationsFromContactsOnly() } +func (api *PublicAPI) EnablePushNotificationsBlockMentions(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("push-notifications-block-mentions?", true) + if err != nil { + return err + } + return api.service.messenger.EnablePushNotificationsBlockMentions() +} + +func (api *PublicAPI) DisablePushNotificationsBlockMentions(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("push-notifications-block-mentions?", false) + if err != nil { + return err + } + return api.service.messenger.DisablePushNotificationsBlockMentions() +} + func (api *PublicAPI) AddPushNotificationsServer(ctx context.Context, publicKeyBytes types.HexBytes) error { publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) if err != nil { diff --git a/services/ext/service.go b/services/ext/service.go index c2a609bc9..a288d1051 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -484,6 +484,7 @@ func buildMessengerOptions( options = append(options, protocol.WithPushNotificationClientConfig(&pushnotificationclient.Config{ DefaultServers: config.DefaultPushNotificationsServers, + BlockMentions: settings.PushNotificationsBlockMentions, SendEnabled: settings.SendPushNotifications, AllowFromContactsOnly: settings.PushNotificationsFromContactsOnly, RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled, From 28e06daf6d82b220cbd8456876f90963c633c07f Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 3 Sep 2020 11:54:05 +0200 Subject: [PATCH 8/9] Dont sent messages to user who have not joined --- protocol/chat.go | 10 ++ protocol/messenger.go | 17 ++- protocol/push_notification_test.go | 162 ---------------------- protocol/pushnotificationserver/server.go | 15 +- 4 files changed, 35 insertions(+), 169 deletions(-) diff --git a/protocol/chat.go b/protocol/chat.go index a0b902f44..b02fc23a1 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -115,6 +115,16 @@ func (c *Chat) MembersAsPublicKeys() ([]*ecdsa.PublicKey, error) { return stringSliceToPublicKeys(publicKeys, true) } +func (c *Chat) JoinedMembersAsPublicKeys() ([]*ecdsa.PublicKey, error) { + var publicKeys []string + for _, item := range c.Members { + if item.Joined { + publicKeys = append(publicKeys, item.ID) + } + } + return stringSliceToPublicKeys(publicKeys, true) +} + func (c *Chat) HasMember(memberID string) bool { for _, member := range c.Members { if memberID == member.ID { diff --git a/protocol/messenger.go b/protocol/messenger.go index 88a1fecb9..147c9c0ee 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1663,9 +1663,18 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage) case ChatTypePrivateGroupChat: logger.Debug("sending group message", zap.String("chatName", chat.Name)) if spec.Recipients == nil { - spec.Recipients, err = chat.MembersAsPublicKeys() - if err != nil { - return nil, err + // Chat messages are only dispatched to users who joined + if spec.MessageType == protobuf.ApplicationMetadataMessage_CHAT_MESSAGE { + spec.Recipients, err = chat.JoinedMembersAsPublicKeys() + if err != nil { + return nil, err + } + + } else { + spec.Recipients, err = chat.MembersAsPublicKeys() + if err != nil { + return nil, err + } } } hasPairedDevices := m.hasPairedDevices() @@ -3519,7 +3528,7 @@ func (m *Messenger) pushNotificationOptions() *pushnotificationclient.Registrati var publicChatIDs []string for _, contact := range m.allContacts { - if contact.IsAdded() { + if contact.IsAdded() && !contact.IsBlocked() { pk, err := contact.PublicKey() if err != nil { m.logger.Warn("could not parse contact public key") diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 030d9ddb6..f334ed9b4 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -641,168 +641,6 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { s.Require().NoError(server.Shutdown()) } -// Here bob acts as his own server -func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer() { - bob1 := s.m - server := s.newPushNotificationServer(s.shh, s.m.identity) - bob2 := server - alice := s.newMessenger(s.shh) - // start alice and enable sending push notifications - s.Require().NoError(alice.Start()) - s.Require().NoError(alice.EnableSendingPushNotifications()) - bobInstallationIDs := []string{bob1.installationID, bob2.installationID} - - // Register bob1 - err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom) - s.Require().NoError(err) - - err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN) - - // Pull servers and check we registered - err = tt.RetryWithBackOff(func() error { - _, err = server.RetrieveAll() - if err != nil { - return err - } - _, err = bob1.RetrieveAll() - if err != nil { - return err - } - registered, err := bob1.RegisteredForPushNotifications() - if err != nil { - return err - } - if !registered { - return errors.New("not registered") - } - return nil - }) - // Make sure we receive it - s.Require().NoError(err) - bob1Servers, err := bob1.GetPushNotificationsServers() - s.Require().NoError(err) - - // Register bob2 - err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom) - s.Require().NoError(err) - - err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN) - s.Require().NoError(err) - - err = tt.RetryWithBackOff(func() error { - _, err = server.RetrieveAll() - if err != nil { - return err - } - _, err = bob2.RetrieveAll() - if err != nil { - return err - } - - registered, err := bob2.RegisteredForPushNotifications() - if err != nil { - return err - } - if !registered { - return errors.New("not registered") - } - return nil - }) - // Make sure we receive it - s.Require().NoError(err) - bob2Servers, err := bob2.GetPushNotificationsServers() - s.Require().NoError(err) - - // Create one to one chat & send message - pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) - chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) - s.Require().NoError(alice.SaveChat(&chat)) - inputMessage := buildTestMessage(chat) - response, err := alice.SendChatMessage(context.Background(), inputMessage) - s.Require().NoError(err) - messageIDString := response.Messages[0].ID - messageID, err := hex.DecodeString(messageIDString[2:]) - s.Require().NoError(err) - - var info []*pushnotificationclient.PushNotificationInfo - err = tt.RetryWithBackOff(func() error { - _, err = server.RetrieveAll() - if err != nil { - return err - } - _, err = alice.RetrieveAll() - if err != nil { - return err - } - - info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) - if err != nil { - return err - } - // Check we have replies for both bob1 and bob2 - if len(info) != 2 { - return errors.New("info not fetched") - } - return nil - - }) - - s.Require().NoError(err) - - // Check we have replies for both bob1 and bob2 - var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo - - if info[0].AccessToken == bob1Servers[0].AccessToken { - bob1Info = info[0] - bob2Info = info[1] - } else { - bob2Info = info[0] - bob1Info = info[1] - } - - s.Require().NotNil(bob1Info) - s.Require().Equal(bob1.installationID, bob1Info.InstallationID) - s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken) - s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey) - - s.Require().NotNil(bob2Info) - s.Require().Equal(bob2.installationID, bob2Info.InstallationID) - s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken) - s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey) - - retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) - - s.Require().NoError(err) - s.Require().NotNil(retrievedNotificationInfo) - s.Require().Len(retrievedNotificationInfo, 2) - - var sentNotification *pushnotificationclient.SentNotification - err = tt.RetryWithBackOff(func() error { - _, err = server.RetrieveAll() - if err != nil { - return err - } - _, err = alice.RetrieveAll() - if err != nil { - return err - } - sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID) - if err != nil { - return err - } - if sentNotification == nil { - return errors.New("sent notification not found") - } - if !sentNotification.Success { - return errors.New("sent notification not successul") - } - return nil - }) - s.Require().NoError(err) - s.Require().NoError(bob2.Shutdown()) - s.Require().NoError(alice.Shutdown()) -} - func (s *MessengerPushNotificationSuite) TestContactCode() { bob1 := s.m diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index 1db29bb2d..a992b805a 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -363,12 +363,14 @@ func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNoti // collect successful requests & registrations var requestAndRegistrations []*RequestAndRegistration + // TODO: this logic needs to be extracted and tested for _, pn := range request.Requests { registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) report := &protobuf.PushNotificationReport{ PublicKey: pn.PublicKey, InstallationId: pn.InstallationId, } + s.config.Logger.Info("registration and request", zap.Any("registration", registration), zap.Any("request", pn)) if pn.Type == protobuf.PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE { s.config.Logger.Warn("unhandled type") @@ -382,14 +384,17 @@ func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNoti s.config.Logger.Warn("empty registration") report.Error = protobuf.PushNotificationReport_NOT_REGISTERED } else if registration.AccessToken != pn.AccessToken { + s.config.Logger.Debug("invalid token") report.Error = protobuf.PushNotificationReport_WRONG_TOKEN - } else if s.contains(registration.BlockedChatList, pn.ChatId) || !s.isValidMentionNotification(pn, registration) { + } else if s.contains(registration.BlockedChatList, pn.ChatId) || (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration)) { // We report as successful but don't send the notification // for privacy reasons, as otherwise we would disclose that // the sending client has been blocked or that the registering // client has not joined a given public chat report.Success = true + s.config.Logger.Debug("invalid token", zap.Bool("blocked", s.contains(registration.BlockedChatList, pn.ChatId)), zap.Bool("mention", (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration)))) } else { + s.config.Logger.Debug("sending push notification") // For now we just assume that the notification will be successful requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ Request: pn, @@ -474,6 +479,10 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ return response } -func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { - return !registration.BlockMentions && pn.Type == protobuf.PushNotification_MENTION && s.contains(registration.AllowedMentionsChatList, pn.ChatId) +func (s *Server) isMentionNotification(pn *protobuf.PushNotification) bool { + return pn.Type == protobuf.PushNotification_MENTION +} + +func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { + return s.isMentionNotification(pn) && !registration.BlockMentions && s.contains(registration.AllowedMentionsChatList, pn.ChatId) } From 3c6b741f71fdcb42022e97de79eb1cb700facda1 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 7 Sep 2020 10:25:57 +0200 Subject: [PATCH 9/9] Add tests for send notification & review feedback --- VERSION | 2 +- protocol/chat.go | 6 +- protocol/common/message.go | 1 + protocol/message_handler.go | 6 +- protocol/messenger.go | 6 +- .../application_metadata_message.pb.go | 4 +- protocol/protobuf/group_chat_invitation.pb.go | 4 +- protocol/protobuf/pairing.pb.go | 4 +- protocol/protobuf/push_notifications.pb.go | 135 ++++++------ protocol/protobuf/push_notifications.proto | 1 + protocol/pushnotificationclient/client.go | 7 +- protocol/pushnotificationserver/server.go | 135 ++++++++---- .../pushnotificationserver/server_test.go | 207 ++++++++++++++++++ 13 files changed, 387 insertions(+), 131 deletions(-) diff --git a/VERSION b/VERSION index aba2fdc24..0b0945503 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.60.2 +0.61.0 diff --git a/protocol/chat.go b/protocol/chat.go index b02fc23a1..120100f80 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -117,9 +117,9 @@ func (c *Chat) MembersAsPublicKeys() ([]*ecdsa.PublicKey, error) { func (c *Chat) JoinedMembersAsPublicKeys() ([]*ecdsa.PublicKey, error) { var publicKeys []string - for _, item := range c.Members { - if item.Joined { - publicKeys = append(publicKeys, item.ID) + for _, member := range c.Members { + if member.Joined { + publicKeys = append(publicKeys, member.ID) } } return stringSliceToPublicKeys(publicKeys, true) diff --git a/protocol/common/message.go b/protocol/common/message.go index 51a031f77..7fba8393e 100644 --- a/protocol/common/message.go +++ b/protocol/common/message.go @@ -302,6 +302,7 @@ func (m *Message) parseAudio() error { return nil } +// implement interface of https://github.com/status-im/markdown/blob/b9fe921681227b1dace4b56364e15edb3b698308/ast/node.go#L701 type MentionNodeVisitor struct { mentions []string } diff --git a/protocol/message_handler.go b/protocol/message_handler.go index 574ccb393..2be57ae2c 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -248,11 +248,11 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta return nil } -func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) (bool, error) { +func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) bool { chatID := message.Id _, ok := state.AllChats[chatID] if ok { - return false, nil + return false } chat := CreatePublicChat(chatID, state.Timesource) @@ -260,7 +260,7 @@ func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessage state.AllChats[chat.ID] = &chat state.ModifiedChats[chat.ID] = true - return true, nil + return true } func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error { diff --git a/protocol/messenger.go b/protocol/messenger.go index 147c9c0ee..e8cc759c1 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -2274,11 +2274,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationPublicChat) logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p)) - added, err := m.handler.HandleSyncInstallationPublicChat(messageState, p) - if err != nil { - logger.Warn("failed to handle SyncInstallationPublicChat", zap.Error(err)) - continue - } + added := m.handler.HandleSyncInstallationPublicChat(messageState, p) // We re-register as we want to receive mentions from the newly joined public chat if added { diff --git a/protocol/protobuf/application_metadata_message.pb.go b/protocol/protobuf/application_metadata_message.pb.go index 378b07e1d..bb849a14c 100644 --- a/protocol/protobuf/application_metadata_message.pb.go +++ b/protocol/protobuf/application_metadata_message.pb.go @@ -174,9 +174,7 @@ func init() { proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage") } -func init() { - proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) -} +func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) } var fileDescriptor_ad09a6406fcf24c7 = []byte{ // 493 bytes of a gzipped FileDescriptorProto diff --git a/protocol/protobuf/group_chat_invitation.pb.go b/protocol/protobuf/group_chat_invitation.pb.go index b44a31b8e..548bdfbfe 100644 --- a/protocol/protobuf/group_chat_invitation.pb.go +++ b/protocol/protobuf/group_chat_invitation.pb.go @@ -123,9 +123,7 @@ func init() { proto.RegisterType((*GroupChatInvitation)(nil), "protobuf.GroupChatInvitation") } -func init() { - proto.RegisterFile("group_chat_invitation.proto", fileDescriptor_a6a73333de6a8ebe) -} +func init() { proto.RegisterFile("group_chat_invitation.proto", fileDescriptor_a6a73333de6a8ebe) } var fileDescriptor_a6a73333de6a8ebe = []byte{ // 234 bytes of a gzipped FileDescriptorProto diff --git a/protocol/protobuf/pairing.pb.go b/protocol/protobuf/pairing.pb.go index 8f85ad616..f8cc82f04 100644 --- a/protocol/protobuf/pairing.pb.go +++ b/protocol/protobuf/pairing.pb.go @@ -335,9 +335,7 @@ func init() { proto.RegisterType((*SyncInstallation)(nil), "protobuf.SyncInstallation") } -func init() { - proto.RegisterFile("pairing.proto", fileDescriptor_d61ab7221f0b5518) -} +func init() { proto.RegisterFile("pairing.proto", fileDescriptor_d61ab7221f0b5518) } var fileDescriptor_d61ab7221f0b5518 = []byte{ // 397 bytes of a gzipped FileDescriptorProto diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index bd636ac53..c518c228c 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -566,6 +566,7 @@ type PushNotification struct { InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` Message []byte `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` Type PushNotification_PushNotificationType `protobuf:"varint,6,opt,name=type,proto3,enum=protobuf.PushNotification_PushNotificationType" json:"type,omitempty"` + Author []byte `protobuf:"bytes,7,opt,name=author,proto3" json:"author,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -638,6 +639,13 @@ func (m *PushNotification) GetType() PushNotification_PushNotificationType { return PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE } +func (m *PushNotification) GetAuthor() []byte { + if m != nil { + return m.Author + } + return nil +} + type PushNotificationRequest struct { Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"` MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` @@ -815,67 +823,68 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 990 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x4d, 0x6f, 0xe3, 0x44, - 0x18, 0xc6, 0x4e, 0xda, 0x24, 0x6f, 0xd2, 0xd4, 0x1d, 0xf5, 0xc3, 0x14, 0xba, 0x04, 0x03, 0x22, - 0xea, 0x21, 0xa0, 0x22, 0xb1, 0x2b, 0xb8, 0x10, 0x52, 0xa7, 0x6b, 0xb5, 0xb1, 0xc3, 0xc4, 0x65, - 0xb5, 0x12, 0xd2, 0xc8, 0xb1, 0x27, 0xad, 0x55, 0xd7, 0x36, 0x1e, 0xa7, 0x28, 0x37, 0x7e, 0x00, - 0x17, 0xae, 0x9c, 0xf8, 0x0d, 0xfc, 0x0d, 0xfe, 0x14, 0xf2, 0xd8, 0xce, 0xba, 0x8d, 0x9b, 0x16, - 0x69, 0x4f, 0xc9, 0x3c, 0xef, 0xc7, 0xcc, 0xfb, 0xf1, 0x3c, 0x06, 0x39, 0x9c, 0xb3, 0x6b, 0xe2, - 0x07, 0xb1, 0x3b, 0x73, 0x6d, 0x2b, 0x76, 0x03, 0x9f, 0xf5, 0xc2, 0x28, 0x88, 0x03, 0x54, 0xe7, - 0x3f, 0xd3, 0xf9, 0x4c, 0xf9, 0x7b, 0x03, 0x3e, 0x1e, 0xcf, 0xd9, 0xb5, 0x5e, 0xf0, 0xc2, 0xf4, - 0xca, 0x65, 0x71, 0xc4, 0xff, 0x23, 0x03, 0x20, 0x0e, 0x6e, 0xa8, 0x4f, 0xe2, 0x45, 0x48, 0x65, - 0xa1, 0x23, 0x74, 0xdb, 0x27, 0x5f, 0xf7, 0xf2, 0xf8, 0xde, 0xba, 0xd8, 0x9e, 0x99, 0x04, 0x9a, - 0x8b, 0x90, 0xe2, 0x46, 0x9c, 0xff, 0x45, 0x9f, 0x42, 0xcb, 0xa1, 0x77, 0xae, 0x4d, 0x09, 0xc7, - 0x64, 0xb1, 0x23, 0x74, 0x1b, 0xb8, 0x99, 0x62, 0x3c, 0x02, 0x7d, 0x09, 0xdb, 0xae, 0xcf, 0x62, - 0xcb, 0xf3, 0x78, 0x1e, 0xe2, 0x3a, 0x72, 0x85, 0x7b, 0xb5, 0x8b, 0xb0, 0xe6, 0x24, 0xb9, 0x2c, - 0xdb, 0xa6, 0x8c, 0x65, 0xb9, 0xaa, 0x69, 0xae, 0x14, 0x4b, 0x73, 0xc9, 0x50, 0xa3, 0xbe, 0x35, - 0xf5, 0xa8, 0x23, 0x6f, 0x74, 0x84, 0x6e, 0x1d, 0xe7, 0xc7, 0xc4, 0x72, 0x47, 0x23, 0xe6, 0x06, - 0xbe, 0xbc, 0xd9, 0x11, 0xba, 0x55, 0x9c, 0x1f, 0x51, 0x17, 0x24, 0xcb, 0xf3, 0x82, 0xdf, 0xa8, - 0x43, 0x6e, 0xe8, 0x82, 0x78, 0x2e, 0x8b, 0xe5, 0x5a, 0xa7, 0xd2, 0x6d, 0xe1, 0x76, 0x86, 0x9f, - 0xd3, 0xc5, 0x85, 0xcb, 0x62, 0x74, 0x0c, 0x3b, 0x53, 0x2f, 0xb0, 0x6f, 0xa8, 0x43, 0xec, 0x6b, - 0x2b, 0x4e, 0x5d, 0xeb, 0xdc, 0x75, 0x3b, 0x33, 0x0c, 0xae, 0xad, 0x98, 0xfb, 0xbe, 0x00, 0x98, - 0xfb, 0x11, 0xef, 0x0f, 0x8d, 0xe4, 0x06, 0x7f, 0x4c, 0x01, 0x41, 0xbb, 0xb0, 0x71, 0x15, 0x59, - 0x7e, 0x2c, 0x43, 0x47, 0xe8, 0xb6, 0x70, 0x7a, 0x40, 0x2f, 0x41, 0xe6, 0x77, 0x92, 0x59, 0x14, - 0xdc, 0x12, 0x3b, 0xf0, 0x63, 0xcb, 0x8e, 0x19, 0x09, 0x7c, 0x6f, 0x21, 0x37, 0x79, 0x8e, 0x3d, - 0x6e, 0x1f, 0x46, 0xc1, 0xed, 0x20, 0xb3, 0x1a, 0xbe, 0xb7, 0x40, 0x1f, 0x41, 0xc3, 0x0a, 0x7d, - 0x12, 0x07, 0xa1, 0x6b, 0xcb, 0x2d, 0xde, 0x98, 0xba, 0x15, 0xfa, 0x66, 0x72, 0x46, 0x5f, 0x40, - 0x9b, 0x3f, 0x8f, 0xdc, 0x52, 0x9f, 0x2f, 0x86, 0xbc, 0xc5, 0x73, 0x6d, 0x71, 0x74, 0x94, 0x81, - 0xe8, 0x7b, 0x38, 0xcc, 0x1b, 0x91, 0x3b, 0x16, 0xea, 0x6c, 0xf3, 0x3a, 0x0f, 0x32, 0x8f, 0x3c, - 0x28, 0xaf, 0x57, 0x19, 0x42, 0x63, 0xb9, 0x00, 0x68, 0x1f, 0xd0, 0xa5, 0x7e, 0xae, 0x1b, 0x6f, - 0x74, 0x62, 0x1a, 0xe7, 0xaa, 0x4e, 0xcc, 0xb7, 0x63, 0x55, 0xfa, 0x00, 0x6d, 0x41, 0xa3, 0x3f, - 0xce, 0x30, 0x49, 0x40, 0x08, 0xda, 0x43, 0x0d, 0xab, 0x3f, 0xf6, 0x27, 0x6a, 0x86, 0x89, 0xca, - 0x3f, 0x22, 0x7c, 0xbe, 0x6e, 0xcd, 0x30, 0x65, 0x61, 0xe0, 0x33, 0x9a, 0x0c, 0x94, 0xcd, 0xf9, - 0xe8, 0xf9, 0x9e, 0xd6, 0x71, 0x7e, 0x44, 0x3a, 0x6c, 0xd0, 0x28, 0x0a, 0x22, 0xbe, 0x6c, 0xed, - 0x93, 0x57, 0xcf, 0xdb, 0xdf, 0x3c, 0x71, 0x4f, 0x4d, 0x62, 0xf9, 0x1e, 0xa7, 0x69, 0xd0, 0x11, - 0x40, 0x44, 0x7f, 0x9d, 0x53, 0x16, 0xe7, 0xbb, 0xd9, 0xc2, 0x8d, 0x0c, 0xd1, 0x1c, 0xe5, 0x77, - 0x01, 0x1a, 0xcb, 0x98, 0x62, 0xe9, 0x2a, 0xc6, 0x06, 0xce, 0x4b, 0xdf, 0x83, 0x9d, 0x51, 0xff, - 0x62, 0x68, 0xe0, 0x91, 0x7a, 0x4a, 0x46, 0xea, 0x64, 0xd2, 0x3f, 0x53, 0x25, 0x01, 0xed, 0x82, - 0xf4, 0xb3, 0x8a, 0x27, 0x9a, 0xa1, 0x93, 0x91, 0x36, 0x19, 0xf5, 0xcd, 0xc1, 0x6b, 0x49, 0x44, - 0x87, 0xb0, 0x7f, 0xa9, 0x4f, 0x2e, 0xc7, 0x63, 0x03, 0x9b, 0xea, 0x69, 0xb1, 0x87, 0x95, 0xa4, - 0x69, 0x9a, 0x6e, 0xaa, 0x58, 0xef, 0x5f, 0xa4, 0x37, 0x48, 0x55, 0x65, 0x0e, 0x72, 0xb6, 0x0d, - 0x83, 0xc0, 0xa1, 0x7d, 0xe7, 0x8e, 0x46, 0xb1, 0xcb, 0x68, 0x32, 0x45, 0xf4, 0x16, 0xf6, 0x57, - 0x94, 0x81, 0xb8, 0xfe, 0x2c, 0x90, 0x85, 0x4e, 0xa5, 0xdb, 0x3c, 0xf9, 0xec, 0xf1, 0xf6, 0xfc, - 0x34, 0xa7, 0xd1, 0x42, 0xf3, 0x67, 0x01, 0xde, 0x0d, 0x1f, 0x98, 0x12, 0x54, 0x79, 0x05, 0x7b, - 0xa5, 0x21, 0xe8, 0x13, 0x68, 0x86, 0xf3, 0xa9, 0xe7, 0xda, 0x09, 0xa3, 0x18, 0xbf, 0xa8, 0x85, - 0x21, 0x85, 0xce, 0xe9, 0x82, 0x29, 0x7f, 0x88, 0xf0, 0xe1, 0xa3, 0xb7, 0xad, 0x10, 0x5d, 0x58, - 0x25, 0x7a, 0x89, 0x68, 0x88, 0xa5, 0xa2, 0x71, 0x04, 0xf0, 0xee, 0x29, 0xf9, 0xf0, 0x96, 0x2f, - 0x29, 0x25, 0x7f, 0xb5, 0x94, 0xfc, 0x4b, 0xc2, 0x6e, 0x14, 0x09, 0xfb, 0xb8, 0xac, 0x1c, 0xc3, - 0x0e, 0xa3, 0xd1, 0x1d, 0x8d, 0x48, 0xe1, 0xfe, 0x1a, 0x8f, 0xdd, 0x4e, 0x0d, 0xe3, 0xfc, 0x15, - 0xca, 0x9f, 0x02, 0x1c, 0x95, 0xb6, 0x63, 0xb9, 0xed, 0x2f, 0xa1, 0xfa, 0x7f, 0x67, 0xc6, 0x03, - 0x92, 0xfa, 0x6f, 0x29, 0x63, 0xd6, 0x15, 0xcd, 0x7b, 0xd4, 0xc2, 0x8d, 0x0c, 0xd1, 0x9c, 0x22, - 0x8b, 0x2a, 0xf7, 0x58, 0xa4, 0xfc, 0x2b, 0x82, 0xf4, 0x30, 0xf9, 0x73, 0x26, 0x73, 0x00, 0x35, - 0x2e, 0x1a, 0xcb, 0xdb, 0x36, 0x93, 0xe3, 0xd3, 0x93, 0x28, 0x99, 0x68, 0xb5, 0x74, 0xa2, 0x32, - 0xd4, 0xb2, 0xf7, 0x67, 0xa3, 0xc8, 0x8f, 0x68, 0x00, 0x55, 0xfe, 0xdd, 0xda, 0xe4, 0xbc, 0xff, - 0xea, 0xf1, 0x26, 0xad, 0x00, 0x9c, 0xee, 0x3c, 0x58, 0x31, 0x61, 0xb7, 0xcc, 0x8a, 0x14, 0x78, - 0x91, 0x13, 0x7b, 0x7c, 0x39, 0x79, 0x4d, 0x74, 0xc3, 0xd4, 0x86, 0xda, 0xa0, 0x6f, 0x26, 0xdc, - 0xcd, 0x48, 0xde, 0x84, 0xda, 0x3b, 0x6a, 0xf3, 0x83, 0x9e, 0x98, 0x25, 0x51, 0x09, 0xe1, 0x60, - 0x55, 0x7c, 0xb8, 0x82, 0xa0, 0x6f, 0xa1, 0x9e, 0x89, 0x09, 0xcb, 0xc6, 0x7b, 0xb8, 0x46, 0xb1, - 0x96, 0xbe, 0x4f, 0x4c, 0x56, 0xf9, 0x4b, 0x84, 0xfd, 0xd5, 0x2b, 0xc3, 0x20, 0x8a, 0xd7, 0x48, - 0xe7, 0x0f, 0xf7, 0xa5, 0xf3, 0x78, 0x9d, 0x74, 0x26, 0xa9, 0x4a, 0xc5, 0xf2, 0x7d, 0x4c, 0x59, - 0xf9, 0xe5, 0x39, 0xa2, 0xba, 0x0d, 0xcd, 0x37, 0xd8, 0xd0, 0xcf, 0x8a, 0x5f, 0x94, 0x07, 0xe2, - 0x28, 0x26, 0x98, 0x6e, 0x98, 0x04, 0xab, 0x67, 0xda, 0xc4, 0x54, 0xb1, 0x7a, 0x2a, 0x55, 0x12, - 0xc1, 0x5c, 0x2d, 0x28, 0xa3, 0xda, 0xfd, 0xbe, 0x0a, 0x0f, 0x19, 0xf3, 0x1d, 0xd4, 0x22, 0x5e, - 0x3b, 0x93, 0x45, 0x3e, 0xad, 0xce, 0x53, 0x4d, 0xc2, 0x79, 0xc0, 0x74, 0x93, 0x7b, 0x7e, 0xf3, - 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x65, 0x07, 0x55, 0xdb, 0xac, 0x09, 0x00, 0x00, + // 1002 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0xc6, 0x4e, 0xda, 0x24, 0x27, 0x69, 0x9a, 0x8e, 0xfa, 0x63, 0x0a, 0x5d, 0x82, 0x01, 0x11, + 0xf5, 0xa2, 0xa0, 0x22, 0xb1, 0x2b, 0xb8, 0x21, 0xa4, 0x6e, 0xd7, 0x6a, 0x63, 0x87, 0x89, 0xcb, + 0x6a, 0x25, 0xa4, 0x91, 0x9b, 0x4c, 0x5b, 0xab, 0xae, 0xc7, 0x78, 0xc6, 0x45, 0xb9, 0xe3, 0x01, + 0xb8, 0xe1, 0x96, 0x2b, 0x9e, 0x81, 0x57, 0xe2, 0x45, 0x90, 0xc7, 0x76, 0xd6, 0x6d, 0xdc, 0xb4, + 0x48, 0x7b, 0x95, 0x9c, 0x6f, 0xce, 0x39, 0x33, 0xe7, 0xe7, 0xfb, 0x0c, 0x5a, 0x18, 0xf3, 0x6b, + 0x12, 0x30, 0xe1, 0x5d, 0x7a, 0x13, 0x57, 0x78, 0x2c, 0xe0, 0x07, 0x61, 0xc4, 0x04, 0x43, 0x75, + 0xf9, 0x73, 0x11, 0x5f, 0xea, 0x7f, 0xaf, 0xc0, 0xc7, 0xa3, 0x98, 0x5f, 0x5b, 0x05, 0x2f, 0x4c, + 0xaf, 0x3c, 0x2e, 0x22, 0xf9, 0x1f, 0xd9, 0x00, 0x82, 0xdd, 0xd0, 0x80, 0x88, 0x59, 0x48, 0x35, + 0xa5, 0xab, 0xf4, 0xda, 0x87, 0x5f, 0x1f, 0xe4, 0xf1, 0x07, 0xcb, 0x62, 0x0f, 0x9c, 0x24, 0xd0, + 0x99, 0x85, 0x14, 0x37, 0x44, 0xfe, 0x17, 0x7d, 0x0a, 0xad, 0x29, 0xbd, 0xf3, 0x26, 0x94, 0x48, + 0x4c, 0x53, 0xbb, 0x4a, 0xaf, 0x81, 0x9b, 0x29, 0x26, 0x23, 0xd0, 0x97, 0xb0, 0xee, 0x05, 0x5c, + 0xb8, 0xbe, 0x2f, 0xf3, 0x10, 0x6f, 0xaa, 0x55, 0xa4, 0x57, 0xbb, 0x08, 0x9b, 0xd3, 0x24, 0x97, + 0x3b, 0x99, 0x50, 0xce, 0xb3, 0x5c, 0xd5, 0x34, 0x57, 0x8a, 0xa5, 0xb9, 0x34, 0xa8, 0xd1, 0xc0, + 0xbd, 0xf0, 0xe9, 0x54, 0x5b, 0xe9, 0x2a, 0xbd, 0x3a, 0xce, 0xcd, 0xe4, 0xe4, 0x8e, 0x46, 0xdc, + 0x63, 0x81, 0xb6, 0xda, 0x55, 0x7a, 0x55, 0x9c, 0x9b, 0xa8, 0x07, 0x1d, 0xd7, 0xf7, 0xd9, 0x6f, + 0x74, 0x4a, 0x6e, 0xe8, 0x8c, 0xf8, 0x1e, 0x17, 0x5a, 0xad, 0x5b, 0xe9, 0xb5, 0x70, 0x3b, 0xc3, + 0x4f, 0xe9, 0xec, 0xcc, 0xe3, 0x02, 0xed, 0xc3, 0xc6, 0x85, 0xcf, 0x26, 0x37, 0x74, 0x4a, 0x26, + 0xd7, 0xae, 0x48, 0x5d, 0xeb, 0xd2, 0x75, 0x3d, 0x3b, 0x18, 0x5c, 0xbb, 0x42, 0xfa, 0xbe, 0x00, + 0x88, 0x83, 0x48, 0xf6, 0x87, 0x46, 0x5a, 0x43, 0x3e, 0xa6, 0x80, 0xa0, 0x4d, 0x58, 0xb9, 0x8a, + 0xdc, 0x40, 0x68, 0xd0, 0x55, 0x7a, 0x2d, 0x9c, 0x1a, 0xe8, 0x25, 0x68, 0xf2, 0x4e, 0x72, 0x19, + 0xb1, 0x5b, 0x32, 0x61, 0x81, 0x70, 0x27, 0x82, 0x13, 0x16, 0xf8, 0x33, 0xad, 0x29, 0x73, 0x6c, + 0xc9, 0xf3, 0xe3, 0x88, 0xdd, 0x0e, 0xb2, 0x53, 0x3b, 0xf0, 0x67, 0xe8, 0x23, 0x68, 0xb8, 0x61, + 0x40, 0x04, 0x0b, 0xbd, 0x89, 0xd6, 0x92, 0x8d, 0xa9, 0xbb, 0x61, 0xe0, 0x24, 0x36, 0xfa, 0x02, + 0xda, 0xf2, 0x79, 0xe4, 0x96, 0x06, 0x72, 0x31, 0xb4, 0x35, 0x99, 0x6b, 0x4d, 0xa2, 0xc3, 0x0c, + 0x44, 0xdf, 0xc3, 0x6e, 0xde, 0x88, 0xdc, 0xb1, 0x50, 0x67, 0x5b, 0xd6, 0xb9, 0x93, 0x79, 0xe4, + 0x41, 0x79, 0xbd, 0xfa, 0x31, 0x34, 0xe6, 0x0b, 0x80, 0xb6, 0x01, 0x9d, 0x5b, 0xa7, 0x96, 0xfd, + 0xc6, 0x22, 0x8e, 0x7d, 0x6a, 0x58, 0xc4, 0x79, 0x3b, 0x32, 0x3a, 0x1f, 0xa0, 0x35, 0x68, 0xf4, + 0x47, 0x19, 0xd6, 0x51, 0x10, 0x82, 0xf6, 0xb1, 0x89, 0x8d, 0x1f, 0xfb, 0x63, 0x23, 0xc3, 0x54, + 0xfd, 0x1f, 0x15, 0x3e, 0x5f, 0xb6, 0x66, 0x98, 0xf2, 0x90, 0x05, 0x9c, 0x26, 0x03, 0xe5, 0xb1, + 0x1c, 0xbd, 0xdc, 0xd3, 0x3a, 0xce, 0x4d, 0x64, 0xc1, 0x0a, 0x8d, 0x22, 0x16, 0xc9, 0x65, 0x6b, + 0x1f, 0xbe, 0x7a, 0xde, 0xfe, 0xe6, 0x89, 0x0f, 0x8c, 0x24, 0x56, 0xee, 0x71, 0x9a, 0x06, 0xed, + 0x01, 0x44, 0xf4, 0xd7, 0x98, 0x72, 0x91, 0xef, 0x66, 0x0b, 0x37, 0x32, 0xc4, 0x9c, 0xea, 0xbf, + 0x2b, 0xd0, 0x98, 0xc7, 0x14, 0x4b, 0x37, 0x30, 0xb6, 0x71, 0x5e, 0xfa, 0x16, 0x6c, 0x0c, 0xfb, + 0x67, 0xc7, 0x36, 0x1e, 0x1a, 0x47, 0x64, 0x68, 0x8c, 0xc7, 0xfd, 0x13, 0xa3, 0xa3, 0xa0, 0x4d, + 0xe8, 0xfc, 0x6c, 0xe0, 0xb1, 0x69, 0x5b, 0x64, 0x68, 0x8e, 0x87, 0x7d, 0x67, 0xf0, 0xba, 0xa3, + 0xa2, 0x5d, 0xd8, 0x3e, 0xb7, 0xc6, 0xe7, 0xa3, 0x91, 0x8d, 0x1d, 0xe3, 0xa8, 0xd8, 0xc3, 0x4a, + 0xd2, 0x34, 0xd3, 0x72, 0x0c, 0x6c, 0xf5, 0xcf, 0xd2, 0x1b, 0x3a, 0x55, 0x3d, 0x06, 0x2d, 0xdb, + 0x86, 0x01, 0x9b, 0xd2, 0xfe, 0xf4, 0x8e, 0x46, 0xc2, 0xe3, 0x34, 0x99, 0x22, 0x7a, 0x0b, 0xdb, + 0x0b, 0xca, 0x40, 0xbc, 0xe0, 0x92, 0x69, 0x4a, 0xb7, 0xd2, 0x6b, 0x1e, 0x7e, 0xf6, 0x78, 0x7b, + 0x7e, 0x8a, 0x69, 0x34, 0x33, 0x83, 0x4b, 0x86, 0x37, 0xc3, 0x07, 0x47, 0x09, 0xaa, 0xbf, 0x82, + 0xad, 0xd2, 0x10, 0xf4, 0x09, 0x34, 0xc3, 0xf8, 0xc2, 0xf7, 0x26, 0x09, 0xa3, 0xb8, 0xbc, 0xa8, + 0x85, 0x21, 0x85, 0x4e, 0xe9, 0x8c, 0xeb, 0x7f, 0xa8, 0xf0, 0xe1, 0xa3, 0xb7, 0x2d, 0x10, 0x5d, + 0x59, 0x24, 0x7a, 0x89, 0x68, 0xa8, 0xa5, 0xa2, 0xb1, 0x07, 0xf0, 0xee, 0x29, 0xf9, 0xf0, 0xe6, + 0x2f, 0x29, 0x25, 0x7f, 0xb5, 0x94, 0xfc, 0x73, 0xc2, 0xae, 0x14, 0x09, 0xfb, 0xb8, 0xac, 0xec, + 0xc3, 0x06, 0xa7, 0xd1, 0x1d, 0x8d, 0x48, 0xe1, 0xfe, 0x9a, 0x8c, 0x5d, 0x4f, 0x0f, 0x46, 0xf9, + 0x2b, 0xf4, 0x3f, 0x15, 0xd8, 0x2b, 0x6d, 0xc7, 0x7c, 0xdb, 0x5f, 0x42, 0xf5, 0xff, 0xce, 0x4c, + 0x06, 0x24, 0xf5, 0xdf, 0x52, 0xce, 0xdd, 0x2b, 0x9a, 0xf7, 0xa8, 0x85, 0x1b, 0x19, 0x62, 0x4e, + 0x8b, 0x2c, 0xaa, 0xdc, 0x63, 0x91, 0xfe, 0xaf, 0x0a, 0x9d, 0x87, 0xc9, 0x9f, 0x33, 0x99, 0x1d, + 0xa8, 0x49, 0xd1, 0x98, 0xdf, 0xb6, 0x9a, 0x98, 0x4f, 0x4f, 0xa2, 0x64, 0xa2, 0xd5, 0xd2, 0x89, + 0x6a, 0x50, 0xcb, 0xde, 0x9f, 0x8d, 0x22, 0x37, 0xd1, 0x00, 0xaa, 0xf2, 0xbb, 0xb5, 0x2a, 0x79, + 0xff, 0xd5, 0xe3, 0x4d, 0x5a, 0x00, 0x24, 0xdd, 0x65, 0x30, 0xda, 0x86, 0x55, 0x37, 0x16, 0xd7, + 0x2c, 0xca, 0x86, 0x95, 0x59, 0xba, 0x03, 0x9b, 0x65, 0x51, 0x48, 0x87, 0x17, 0x39, 0xe1, 0x47, + 0xe7, 0xe3, 0xd7, 0xc4, 0xb2, 0x1d, 0xf3, 0xd8, 0x1c, 0xf4, 0x9d, 0x84, 0xd3, 0x19, 0xf9, 0x9b, + 0x50, 0x7b, 0x47, 0x79, 0x69, 0x58, 0xc9, 0x71, 0x47, 0xd5, 0x43, 0xd8, 0x59, 0x14, 0x25, 0xa9, + 0x2c, 0xe8, 0x5b, 0xa8, 0x67, 0x22, 0xc3, 0xb3, 0xb1, 0xef, 0x2e, 0x51, 0xb2, 0xb9, 0xef, 0x13, + 0x13, 0xd7, 0xff, 0x52, 0x61, 0x7b, 0xf1, 0xca, 0x90, 0x45, 0x62, 0x89, 0xa4, 0xfe, 0x70, 0x5f, + 0x52, 0xf7, 0x97, 0x49, 0x6a, 0x92, 0xaa, 0x54, 0x44, 0xdf, 0xc7, 0xf4, 0xf5, 0x5f, 0x9e, 0x23, + 0xb6, 0xeb, 0xd0, 0x7c, 0x83, 0x6d, 0xeb, 0xa4, 0xf8, 0xa5, 0x79, 0x20, 0x9a, 0x6a, 0x82, 0x59, + 0xb6, 0x43, 0xb0, 0x71, 0x62, 0x8e, 0x1d, 0x03, 0x1b, 0x47, 0x9d, 0x4a, 0x22, 0xa4, 0x8b, 0x05, + 0x65, 0x14, 0xbc, 0xdf, 0x57, 0xe5, 0x21, 0x93, 0xbe, 0x83, 0x5a, 0x24, 0x6b, 0xe7, 0x9a, 0x2a, + 0xa7, 0xd5, 0x7d, 0xaa, 0x49, 0x38, 0x0f, 0xb8, 0x58, 0x95, 0x9e, 0xdf, 0xfc, 0x17, 0x00, 0x00, + 0xff, 0xff, 0xd4, 0x90, 0x1f, 0x72, 0xc4, 0x09, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index c92882a2d..1d801edc4 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -74,6 +74,7 @@ message PushNotification { MESSAGE = 1; MENTION = 2; } + bytes author = 7; } message PushNotificationRequest { diff --git a/protocol/pushnotificationclient/client.go b/protocol/pushnotificationclient/client.go index 7593fd6be..c22f28fd4 100644 --- a/protocol/pushnotificationclient/client.go +++ b/protocol/pushnotificationclient/client.go @@ -49,7 +49,7 @@ const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 const staleQueryTimeInSeconds = 86400 const mentionInstallationID = "mention" -const oneToOneChatIDLength = 65 +const oneToOneChatIDLength = 132 // maxRegistrationRetries is the maximum number of attempts we do before giving up registering with a server const maxRegistrationRetries int64 = 12 @@ -211,8 +211,6 @@ func (c *Client) Start() error { return errors.New("can't start, missing message processor") } - c.config.Logger.Debug("starting push notification client", zap.Any("config", c.config)) - err := c.loadLastPushNotificationRegistration() if err != nil { return err @@ -996,7 +994,7 @@ func (c *Client) handleDirectMessageSent(sentMessage *common.SentMessage) error // If it's a public key, we use our own public key as chatID, which correspond to the chatID used by the other peer // otherwise we use the group chat ID var chatID string - if len(chatID) == oneToOneChatIDLength { + if len(message.ChatId) == oneToOneChatIDLength { chatID = types.EncodeHex(crypto.FromECDSAPub(&c.config.Identity.PublicKey)) } else { // this is a group chat @@ -1350,6 +1348,7 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] // For now we set the ChatID to our own identity key, this will work fine for blocked users // and muted 1-to-1 chats, but not for group chats. ChatId: common.Shake256([]byte(chatID)), + Author: common.Shake256([]byte(types.EncodeHex(crypto.FromECDSAPub(&c.config.Identity.PublicKey)))), AccessToken: i.AccessToken, PublicKey: common.HashPublicKey(publicKey), InstallationId: i.InstallationID, diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index a992b805a..718ccfe9a 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -21,6 +21,8 @@ import ( const encryptedPayloadKeyLength = 16 const defaultGorushURL = "https://gorush.status.im" +var errUnhandledPushNotificationType = errors.New("unhandled push notification type") + type Config struct { Enabled bool // Identity is our identity key @@ -56,7 +58,7 @@ func (s *Server) Start() error { s.config.Logger.Info("starting push notification server") if s.config.Identity == nil { - s.config.Logger.Info("Identity nil") + s.config.Logger.Debug("Identity nil") // Pull identity from database identity, err := s.persistence.GetIdentity() if err != nil { @@ -139,7 +141,7 @@ func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, message func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, messageID []byte, request protobuf.PushNotificationRequest) error { - s.config.Logger.Info("handling pn request", zap.Binary("message-id", messageID)) + s.config.Logger.Debug("handling pn request", zap.Binary("message-id", messageID)) // This is at-most-once semantic for now exists, err := s.persistence.PushNotificationExists(messageID) @@ -148,7 +150,7 @@ func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, } if exists { - s.config.Logger.Info("already handled") + s.config.Logger.Debug("already handled") return nil } @@ -304,7 +306,7 @@ func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte // buildPushNotificationQueryResponse check if we have the client information and send them back func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { - s.config.Logger.Info("handling push notification query") + s.config.Logger.Debug("handling push notification query") response := &protobuf.PushNotificationQueryResponse{} if query == nil || len(query.PublicKeys) == 0 { return response @@ -349,6 +351,49 @@ func (s *Server) contains(list [][]byte, chatID []byte) bool { return false } +type reportResult struct { + sendNotification bool + report *protobuf.PushNotificationReport +} + +// buildPushNotificationReport checks the request against the registration and +// returns whether we should send the notification and what the response should be +func (s *Server) buildPushNotificationReport(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) (*reportResult, error) { + response := &reportResult{} + report := &protobuf.PushNotificationReport{ + PublicKey: pn.PublicKey, + InstallationId: pn.InstallationId, + } + + if pn.Type == protobuf.PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE { + s.config.Logger.Warn("unhandled type") + return nil, errUnhandledPushNotificationType + } + + if registration == nil { + s.config.Logger.Warn("empty registration") + report.Error = protobuf.PushNotificationReport_NOT_REGISTERED + } else if registration.AccessToken != pn.AccessToken { + s.config.Logger.Debug("invalid token") + report.Error = protobuf.PushNotificationReport_WRONG_TOKEN + } else if (s.isMessageNotification(pn) && !s.isValidMessageNotification(pn, registration)) || (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration)) { + s.config.Logger.Debug("filtered notification") + // We report as successful but don't send the notification + // for privacy reasons, as otherwise we would disclose that + // the sending client has been blocked or that the registering + // client has not joined a given public chat + report.Success = true + } else { + response.sendNotification = true + s.config.Logger.Debug("sending push notification") + report.Success = true + } + + response.report = report + + return response, nil +} + // buildPushNotificationRequestResponse will build a response func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNotificationRequest) (*protobuf.PushNotificationResponse, []*RequestAndRegistration) { response := &protobuf.PushNotificationResponse{} @@ -363,49 +408,36 @@ func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNoti // collect successful requests & registrations var requestAndRegistrations []*RequestAndRegistration - // TODO: this logic needs to be extracted and tested for _, pn := range request.Requests { registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) - report := &protobuf.PushNotificationReport{ - PublicKey: pn.PublicKey, - InstallationId: pn.InstallationId, - } - s.config.Logger.Info("registration and request", zap.Any("registration", registration), zap.Any("request", pn)) - - if pn.Type == protobuf.PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE { - s.config.Logger.Warn("unhandled type") - continue - } - + var report *protobuf.PushNotificationReport if err != nil { - s.config.Logger.Error("failed to retrieve registration", zap.Error(err)) - report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE - } else if registration == nil { - s.config.Logger.Warn("empty registration") - report.Error = protobuf.PushNotificationReport_NOT_REGISTERED - } else if registration.AccessToken != pn.AccessToken { - s.config.Logger.Debug("invalid token") - report.Error = protobuf.PushNotificationReport_WRONG_TOKEN - } else if s.contains(registration.BlockedChatList, pn.ChatId) || (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration)) { - // We report as successful but don't send the notification - // for privacy reasons, as otherwise we would disclose that - // the sending client has been blocked or that the registering - // client has not joined a given public chat - report.Success = true - s.config.Logger.Debug("invalid token", zap.Bool("blocked", s.contains(registration.BlockedChatList, pn.ChatId)), zap.Bool("mention", (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration)))) + report = &protobuf.PushNotificationReport{ + PublicKey: pn.PublicKey, + Error: protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE, + InstallationId: pn.InstallationId, + } } else { - s.config.Logger.Debug("sending push notification") - // For now we just assume that the notification will be successful - requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ - Request: pn, - Registration: registration, - }) - report.Success = true + response, err := s.buildPushNotificationReport(pn, registration) + if err != nil { + s.config.Logger.Warn("unhandled type") + continue + } + + if response.sendNotification { + requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ + Request: pn, + Registration: registration, + }) + } + report = response.report + } + response.Reports = append(response.Reports, report) } - s.config.Logger.Info("built pn request") + s.config.Logger.Debug("built pn request") if len(requestAndRegistrations) == 0 { s.config.Logger.Warn("no request and registration") return response, nil @@ -434,7 +466,7 @@ func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { // buildPushNotificationRegistrationResponse will check the registration is valid, save it, and listen to the topic for the queries func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { - s.config.Logger.Info("handling push notification registration") + s.config.Logger.Debug("handling push notification registration") response := &protobuf.PushNotificationRegistrationResponse{ RequestId: common.Shake256(payload), } @@ -452,7 +484,7 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ } if registration.Unregister { - s.config.Logger.Info("unregistering client") + s.config.Logger.Debug("unregistering client") // We save an empty registration, only keeping version and installation-id if err := s.persistence.UnregisterPushNotificationRegistration(common.HashPublicKey(publicKey), registration.InstallationId, registration.Version); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR @@ -474,7 +506,7 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ } response.Success = true - s.config.Logger.Info("handled push notification registration successfully") + s.config.Logger.Debug("handled push notification registration successfully") return response } @@ -483,6 +515,23 @@ func (s *Server) isMentionNotification(pn *protobuf.PushNotification) bool { return pn.Type == protobuf.PushNotification_MENTION } +// isValidMentionNotification checks: +// this is a mention +// mentions are enabled +// the user joined the public chat +// the author is not blocked func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { - return s.isMentionNotification(pn) && !registration.BlockMentions && s.contains(registration.AllowedMentionsChatList, pn.ChatId) + return s.isMentionNotification(pn) && !registration.BlockMentions && s.contains(registration.AllowedMentionsChatList, pn.ChatId) && !s.contains(registration.BlockedChatList, pn.Author) +} + +func (s *Server) isMessageNotification(pn *protobuf.PushNotification) bool { + return pn.Type == protobuf.PushNotification_MESSAGE +} + +// isValidMentionNotification checks: +// this is a message +// the chat is not muted +// the author is not blocked +func (s *Server) isValidMessageNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { + return s.isMessageNotification(pn) && !s.contains(registration.BlockedChatList, pn.ChatId) && !s.contains(registration.BlockedChatList, pn.Author) } diff --git a/protocol/pushnotificationserver/server_test.go b/protocol/pushnotificationserver/server_test.go index 68f15f686..c46977d29 100644 --- a/protocol/pushnotificationserver/server_test.go +++ b/protocol/pushnotificationserver/server_test.go @@ -670,3 +670,210 @@ func (s *ServerSuite) TestPushNotificationDisabledMentions() { s.Require().NotNil(pushNotificationResponse) s.Require().Nil(requestAndRegistrations) } + +func (s *ServerSuite) TestBuildPushNotificationReport() { + accessToken := "a" + chatID := []byte("chat-id") + author := []byte("author") + blockedAuthor := []byte("blocked-author") + blockedChatID := []byte("blocked-chat-id") + blockedChatList := [][]byte{blockedChatID, blockedAuthor} + nonJoinedChatID := []byte("non-joined-chat-id") + allowedMentionsChatList := [][]byte{chatID} + validMessagePN := &protobuf.PushNotification{ + Type: protobuf.PushNotification_MESSAGE, + ChatId: chatID, + Author: author, + AccessToken: accessToken, + } + validMentionPN := &protobuf.PushNotification{ + Type: protobuf.PushNotification_MENTION, + ChatId: chatID, + AccessToken: accessToken, + } + validRegistration := &protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + BlockedChatList: blockedChatList, + AllowedMentionsChatList: allowedMentionsChatList, + } + blockedMentionsRegistration := &protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + BlockMentions: true, + BlockedChatList: blockedChatList, + AllowedMentionsChatList: allowedMentionsChatList, + } + + testCases := []struct { + name string + pn *protobuf.PushNotification + registration *protobuf.PushNotificationRegistration + expectedError error + expectedResponse *reportResult + }{ + { + name: "valid message", + pn: validMessagePN, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: true, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + { + name: "valid mention", + pn: validMentionPN, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: true, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + { + name: "unknow push notification", + pn: &protobuf.PushNotification{ + ChatId: chatID, + AccessToken: accessToken, + }, + registration: validRegistration, + expectedError: errUnhandledPushNotificationType, + }, + { + name: "empty registration", + pn: validMessagePN, + registration: nil, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: false, + Error: protobuf.PushNotificationReport_NOT_REGISTERED, + }, + }, + }, + { + name: "invalid access token message", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MESSAGE, + Author: author, + ChatId: chatID, + AccessToken: "invalid", + }, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: false, + Error: protobuf.PushNotificationReport_WRONG_TOKEN, + }, + }, + }, + { + name: "invalid access token mention", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MENTION, + Author: author, + ChatId: chatID, + AccessToken: "invalid", + }, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: false, + Error: protobuf.PushNotificationReport_WRONG_TOKEN, + }, + }, + }, + { + name: "blocked chat list message", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MESSAGE, + ChatId: blockedChatID, + Author: author, + AccessToken: accessToken, + }, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + { + name: "blocked group chat message", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MESSAGE, + Author: blockedAuthor, + ChatId: chatID, + AccessToken: accessToken, + }, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + { + name: "blocked chat list mention", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MENTION, + Author: blockedAuthor, + ChatId: chatID, + AccessToken: accessToken, + }, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + { + name: "blocked mentions", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MENTION, + Author: author, + ChatId: chatID, + AccessToken: accessToken, + }, + registration: blockedMentionsRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + { + name: "not in allowed mention chat list", + pn: &protobuf.PushNotification{ + Type: protobuf.PushNotification_MENTION, + Author: author, + ChatId: nonJoinedChatID, + AccessToken: accessToken, + }, + registration: validRegistration, + expectedResponse: &reportResult{ + sendNotification: false, + report: &protobuf.PushNotificationReport{ + Success: true, + }, + }, + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + response, err := s.server.buildPushNotificationReport(tc.pn, tc.registration) + s.Require().Equal(tc.expectedError, err) + s.Require().Equal(tc.expectedResponse, response) + }) + } +}