Merge branch 'feature/push-notification-mentions' into develop

This commit is contained in:
Andrea Maria Piana 2020-09-09 22:11:08 +02:00
commit 19a82f68ad
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
43 changed files with 1566 additions and 684 deletions

View File

@ -1 +1 @@
0.60.2 0.61.0

View File

@ -15,6 +15,8 @@
// 0008_add_push_notifications.up.sql (349B) // 0008_add_push_notifications.up.sql (349B)
// 0009_enable_sending_push_notifications.down.sql (49B) // 0009_enable_sending_push_notifications.down.sql (49B)
// 0009_enable_sending_push_notifications.up.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) // doc.go (74B)
package migrations package migrations
@ -359,7 +361,7 @@ func _0009_enable_sending_push_notificationsDownSql() (*asset, error) {
return nil, err 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}} 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 return a, nil
} }
@ -379,11 +381,51 @@ func _0009_enable_sending_push_notificationsUpSql() (*asset, error) {
return nil, err 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}} 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 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") 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) { 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, "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, "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{}}, "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.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{}}, "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. // RestoreAsset restores an asset under the given directory.

View File

@ -0,0 +1 @@
ALTER settings ADD COLUMN push_notifications_block_mentions BOOLEAN DEFAULT FALSE;

View File

@ -0,0 +1 @@
ALTER TABLE settings ADD COLUMN push_notifications_block_mentions BOOLEAN DEFAULT FALSE;

View File

@ -70,7 +70,9 @@ type Settings struct {
PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled?,omitempty"` PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled?,omitempty"`
// PushNotificationsFromContactsOnly indicates whether we should only receive push notifications from contacts // PushNotificationsFromContactsOnly indicates whether we should only receive push notifications from contacts
PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only?,omitempty"` 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 indicates whether we should be using remote notifications (ios only for now)
RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled?,omitempty"` RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled?,omitempty"`
SigningPhrase string `json:"signing-phrase"` SigningPhrase string `json:"signing-phrase"`
@ -273,6 +275,12 @@ func (db *Database) SaveSetting(setting string, value interface{}) error {
return ErrInvalidConfig return ErrInvalidConfig
} }
update, err = db.db.Prepare("UPDATE settings SET push_notifications_from_contacts_only = ? WHERE synthetic_id = 'id'") 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?": case "send-push-notifications?":
_, ok := value.(bool) _, ok := value.(bool)
if !ok { if !ok {
@ -337,7 +345,7 @@ func (db *Database) GetNodeConfig(nodecfg interface{}) error {
func (db *Database) GetSettings() (Settings, error) { func (db *Database) GetSettings() (Settings, error) {
var s Settings 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.Address,
&s.ChaosMode, &s.ChaosMode,
&s.Currency, &s.Currency,
@ -364,6 +372,7 @@ func (db *Database) GetSettings() (Settings, error) {
&s.PushNotificationsFromContactsOnly, &s.PushNotificationsFromContactsOnly,
&s.RemotePushNotificationsEnabled, &s.RemotePushNotificationsEnabled,
&s.SendPushNotifications, &s.SendPushNotifications,
&s.PushNotificationsBlockMentions,
&s.PhotoPath, &s.PhotoPath,
&s.PinnedMailserver, &s.PinnedMailserver,
&s.PreferredName, &s.PreferredName,

View File

@ -30,50 +30,21 @@ import (
"github.com/status-im/status-go/whisper/v6" "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 { type PeerPoolSimulationSuite struct {
suite.Suite suite.Suite
bootnode *p2p.Server bootnode *p2p.Server
peers []*p2p.Server peers []*p2p.Server
discovery []discovery.Discovery discovery []discovery.Discovery
port uint16
rendezvousServer *server.Server rendezvousServer *server.Server
} }
func TestPeerPoolSimulationSuite(t *testing.T) { func TestPeerPoolSimulationSuite(t *testing.T) {
s := &PeerPoolSimulationSuite{} s := &PeerPoolSimulationSuite{}
port, err := getFreePort()
if err != nil {
panic(err)
}
s.port = uint16(port)
suite.Run(t, s) suite.Run(t, s)
} }
func (s *PeerPoolSimulationSuite) nextPort() uint16 {
port, err := getFreePort()
s.Require().NoError(err)
return uint16(port)
}
func (s *PeerPoolSimulationSuite) SetupTest() { func (s *PeerPoolSimulationSuite) SetupTest() {
bootnodePort := s.nextPort()
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
name := common.MakeName("bootnode", "1.0") name := common.MakeName("bootnode", "1.0")
// 127.0.0.1 is invalidated by discovery v5 // 127.0.0.1 is invalidated by discovery v5
@ -81,13 +52,14 @@ func (s *PeerPoolSimulationSuite) SetupTest() {
Config: p2p.Config{ Config: p2p.Config{
MaxPeers: 10, MaxPeers: 10,
Name: name, Name: name,
ListenAddr: fmt.Sprintf("0.0.0.0:%d", bootnodePort), ListenAddr: ":0",
PrivateKey: key, PrivateKey: key,
DiscoveryV5: true, DiscoveryV5: true,
NoDiscovery: true, NoDiscovery: true,
}, },
} }
s.Require().NoError(s.bootnode.Start()) 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) 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 // 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{ Config: p2p.Config{
MaxPeers: 10, MaxPeers: 10,
Name: common.MakeName("peer-"+strconv.Itoa(i), "1.0"), Name: common.MakeName("peer-"+strconv.Itoa(i), "1.0"),
ListenAddr: fmt.Sprintf("0.0.0.0:%d", s.nextPort()), ListenAddr: ":0",
PrivateKey: key, PrivateKey: key,
NoDiscovery: true, NoDiscovery: true,
BootstrapNodesV5: []*discv5.Node{bootnodeV5}, BootstrapNodesV5: []*discv5.Node{bootnodeV5},

View File

@ -8,6 +8,7 @@ import (
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "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/protobuf"
v1protocol "github.com/status-im/status-go/protocol/v1" v1protocol "github.com/status-im/status-go/protocol/v1"
) )
@ -50,8 +51,8 @@ type Chat struct {
DeletedAtClockValue uint64 `json:"deletedAtClockValue"` DeletedAtClockValue uint64 `json:"deletedAtClockValue"`
// Denormalized fields // Denormalized fields
UnviewedMessagesCount uint `json:"unviewedMessagesCount"` UnviewedMessagesCount uint `json:"unviewedMessagesCount"`
LastMessage *Message `json:"lastMessage"` LastMessage *common.Message `json:"lastMessage"`
// Group chat fields // Group chat fields
// Members are the members who have been invited to the group chat // Members are the members who have been invited to the group chat
@ -114,6 +115,16 @@ func (c *Chat) MembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
return stringSliceToPublicKeys(publicKeys, true) return stringSliceToPublicKeys(publicKeys, true)
} }
func (c *Chat) JoinedMembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
var publicKeys []string
for _, member := range c.Members {
if member.Joined {
publicKeys = append(publicKeys, member.ID)
}
}
return stringSliceToPublicKeys(publicKeys, true)
}
func (c *Chat) HasMember(memberID string) bool { func (c *Chat) HasMember(memberID string) bool {
for _, member := range c.Members { for _, member := range c.Members {
if memberID == member.ID { if memberID == member.ID {
@ -172,7 +183,7 @@ func (c *Chat) NextClockAndTimestamp(timesource TimeSource) (uint64, uint64) {
return clock, timestamp 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()) c.Timestamp = int64(timesource.GetCurrentTime())
// If the clock of the last message is lower, we set the message // If the clock of the last message is lower, we set the message

View File

@ -5,6 +5,8 @@ import (
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/status-im/status-go/protocol/common"
) )
type ChatTestSuite struct { type ChatTestSuite struct {
@ -75,7 +77,7 @@ func (s *ChatTestSuite) TestValidateChat() {
func (s *ChatTestSuite) TestUpdateFromMessage() { func (s *ChatTestSuite) TestUpdateFromMessage() {
// Base case, clock is higher // Base case, clock is higher
message := &Message{} message := &common.Message{}
chat := &Chat{} chat := &Chat{}
message.Clock = 1 message.Clock = 1
@ -84,7 +86,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() {
s.Require().Equal(uint64(1), chat.LastClockValue) s.Require().Equal(uint64(1), chat.LastClockValue)
// Clock is lower and lastMessage is not nil // Clock is lower and lastMessage is not nil
message = &Message{} message = &common.Message{}
lastMessage := message lastMessage := message
chat = &Chat{LastClockValue: 2, LastMessage: lastMessage} chat = &Chat{LastClockValue: 2, LastMessage: lastMessage}
@ -94,7 +96,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() {
s.Require().Equal(uint64(2), chat.LastClockValue) s.Require().Equal(uint64(2), chat.LastClockValue)
// Clock is lower and lastMessage is nil // Clock is lower and lastMessage is nil
message = &Message{} message = &common.Message{}
chat = &Chat{LastClockValue: 2} chat = &Chat{LastClockValue: 2}
message.Clock = 1 message.Clock = 1
@ -103,7 +105,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() {
s.Require().Equal(uint64(2), chat.LastClockValue) s.Require().Equal(uint64(2), chat.LastClockValue)
// Clock is higher but lastMessage has lower clock message then the receiving one // Clock is higher but lastMessage has lower clock message then the receiving one
message = &Message{} message = &common.Message{}
chat = &Chat{LastClockValue: 2} chat = &Chat{LastClockValue: 2}
message.Clock = 1 message.Clock = 1
@ -112,7 +114,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() {
s.Require().Equal(uint64(2), chat.LastClockValue) s.Require().Equal(uint64(2), chat.LastClockValue)
chat.LastClockValue = 4 chat.LastClockValue = 4
message = &Message{} message = &common.Message{}
message.Clock = 3 message.Clock = 3
s.Require().NoError(chat.UpdateFromMessage(message, &testTimeSource{})) s.Require().NoError(chat.UpdateFromMessage(message, &testTimeSource{}))
s.Require().Equal(chat.LastMessage, message) s.Require().Equal(chat.LastMessage, message)
@ -122,7 +124,7 @@ func (s *ChatTestSuite) TestUpdateFromMessage() {
func (s *ChatTestSuite) TestSerializeJSON() { func (s *ChatTestSuite) TestSerializeJSON() {
message := &Message{} message := &common.Message{}
chat := &Chat{} chat := &Chat{}
message.Clock = 1 message.Clock = 1

View File

@ -1,4 +1,4 @@
package protocol package common
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
@ -11,7 +11,9 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/status-im/markdown" "github.com/status-im/markdown"
"github.com/status-im/markdown/ast"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
) )
@ -117,6 +119,9 @@ type Message struct {
// that has been updated // that has been updated
Replace string `json:"replace,omitempty"` Replace string `json:"replace,omitempty"`
SigPubKey *ecdsa.PublicKey `json:"-"` SigPubKey *ecdsa.PublicKey `json:"-"`
// Mentions is an array of mentions for a given message
Mentions []string
} }
func (m *Message) MarshalJSON() ([]byte, error) { func (m *Message) MarshalJSON() ([]byte, error) {
@ -151,6 +156,7 @@ func (m *Message) MarshalJSON() ([]byte, error) {
Timestamp uint64 `json:"timestamp"` Timestamp uint64 `json:"timestamp"`
ContentType protobuf.ChatMessage_ContentType `json:"contentType"` ContentType protobuf.ChatMessage_ContentType `json:"contentType"`
MessageType protobuf.MessageType `json:"messageType"` MessageType protobuf.MessageType `json:"messageType"`
Mentions []string `json:"mentions,omitempty"`
}{ }{
ID: m.ID, ID: m.ID,
WhisperTimestamp: m.WhisperTimestamp, WhisperTimestamp: m.WhisperTimestamp,
@ -174,6 +180,7 @@ func (m *Message) MarshalJSON() ([]byte, error) {
Audio: m.Base64Audio, Audio: m.Base64Audio,
Timestamp: m.Timestamp, Timestamp: m.Timestamp,
ContentType: m.ContentType, ContentType: m.ContentType,
Mentions: m.Mentions,
MessageType: m.MessageType, MessageType: m.MessageType,
CommandParameters: m.CommandParameters, CommandParameters: m.CommandParameters,
} }
@ -295,10 +302,34 @@ func (m *Message) parseAudio() error {
return nil return nil
} }
// implement interface of https://github.com/status-im/markdown/blob/b9fe921681227b1dace4b56364e15edb3b698308/ast/node.go#L701
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 // PrepareContent return the parsed content of the message, the line-count and whether
// is a right-to-left message // is a right-to-left message
func (m *Message) PrepareContent() error { func (m *Message) PrepareContent() error {
parsedText := markdown.Parse([]byte(m.Text), nil) parsedText := markdown.Parse([]byte(m.Text), nil)
m.Mentions = extractMentions(parsedText)
jsonParsedText, err := json.Marshal(parsedText) jsonParsedText, err := json.Marshal(parsedText)
if err != nil { if err != nil {
return err return err

View File

@ -325,11 +325,13 @@ func (p *MessageProcessor) SendPublic(
} }
var newMessage *types.NewMessage 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 { 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) newMessage, err = MessageSpecToWhisper(messageSpec)
if err != nil { if err != nil {
return nil, err return nil, err
@ -354,6 +356,13 @@ func (p *MessageProcessor) SendPublic(
return nil, err return nil, err
} }
sentMessage := &SentMessage{
Spec: messageSpec,
MessageIDs: [][]byte{messageID},
}
p.notifyOnSentMessage(sentMessage)
p.transport.Track([][]byte{messageID}, hash, newMessage) p.transport.Track([][]byte{messageID}, hash, newMessage)
return messageID, nil return messageID, nil

View File

@ -1,4 +1,4 @@
package protocol package common
import ( import (
"io/ioutil" "io/ioutil"
@ -7,6 +7,8 @@ import (
"github.com/stretchr/testify/require" "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/protobuf"
) )
@ -14,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" 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) { 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) require.NoError(t, err)
defer file.Close() defer file.Close()
@ -60,7 +62,7 @@ func TestGetImageMessageMIME(t *testing.T) {
} }
func TestPrepareContentAudio(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) require.NoError(t, err)
defer file.Close() defer file.Close()
@ -94,3 +96,21 @@ func TestGetAudioMessageMIME(t *testing.T) {
_, err = getImageMessageMIME(unknown) _, err = getImageMessageMIME(unknown)
require.Error(t, err) 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)
}

View File

@ -6,6 +6,7 @@ import (
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "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/protobuf"
v1protocol "github.com/status-im/status-go/protocol/v1" v1protocol "github.com/status-im/status-go/protocol/v1"
) )
@ -27,7 +28,7 @@ func tsprintf(format string, params map[string]string) string {
return format 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 var text string
switch e.Type { switch e.Type {
case protobuf.MembershipUpdateEvent_CHAT_CREATED: case protobuf.MembershipUpdateEvent_CHAT_CREATED:
@ -56,7 +57,7 @@ func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[p
} }
timestamp := v1protocol.TimestampInMsFromTime(time.Now()) timestamp := v1protocol.TimestampInMsFromTime(time.Now())
message := &Message{ message := &common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: e.ChatID, ChatId: e.ChatID,
Text: text, Text: text,
@ -75,8 +76,8 @@ func eventToSystemMessage(e v1protocol.MembershipUpdateEvent, translations map[p
return message return message
} }
func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*Message { func buildSystemMessages(events []v1protocol.MembershipUpdateEvent, translations map[protobuf.MembershipUpdateEvent_EventType]string) []*common.Message {
var messages []*Message var messages []*common.Message
for _, e := range events { for _, e := range events {
messages = append(messages, eventToSystemMessage(e, translations)) messages = append(messages, eventToSystemMessage(e, translations))

View File

@ -5,11 +5,12 @@ import (
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "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/alias"
"github.com/status-im/status-go/protocol/identity/identicon" "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) clock, timestamp := chat.NextClockAndTimestamp(timesource)
message.LocalChatID = chat.ID message.LocalChatID = chat.ID
@ -19,7 +20,7 @@ func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, t
message.SigPubKey = key message.SigPubKey = key
message.WhisperTimestamp = timestamp message.WhisperTimestamp = timestamp
message.Seen = true message.Seen = true
message.OutgoingStatus = OutgoingStatusSending message.OutgoingStatus = common.OutgoingStatusSending
identicon, err := identicon.GenerateBase64(message.From) identicon, err := identicon.GenerateBase64(message.From)
if err != nil { if err != nil {

View File

@ -145,7 +145,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta
return nil 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.ID = state.CurrentMessageState.MessageID
message.From = state.CurrentMessageState.Contact.ID message.From = state.CurrentMessageState.Contact.ID
message.Alias = state.CurrentMessageState.Contact.Alias message.Alias = state.CurrentMessageState.Contact.Alias
@ -182,7 +182,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
message.OutgoingStatus = "" message.OutgoingStatus = ""
} else { } else {
// Our own message, mark as sent // Our own message, mark as sent
message.OutgoingStatus = OutgoingStatusSent message.OutgoingStatus = common.OutgoingStatusSent
} }
err = chat.UpdateFromMessage(message, state.Timesource) err = chat.UpdateFromMessage(message, state.Timesource)
@ -248,11 +248,11 @@ func (m *MessageHandler) HandleSyncInstallationContact(state *ReceivedMessageSta
return nil return nil
} }
func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) error { func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessageState, message protobuf.SyncInstallationPublicChat) bool {
chatID := message.Id chatID := message.Id
_, ok := state.AllChats[chatID] _, ok := state.AllChats[chatID]
if ok { if ok {
return nil return false
} }
chat := CreatePublicChat(chatID, state.Timesource) chat := CreatePublicChat(chatID, state.Timesource)
@ -260,7 +260,7 @@ func (m *MessageHandler) HandleSyncInstallationPublicChat(state *ReceivedMessage
state.AllChats[chat.ID] = &chat state.AllChats[chat.ID] = &chat
state.ModifiedChats[chat.ID] = true state.ModifiedChats[chat.ID] = true
return nil return true
} }
func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error { func (m *MessageHandler) HandleContactUpdate(state *ReceivedMessageState, message protobuf.ContactUpdate) error {
@ -330,7 +330,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
logger.Warn("failed to validate message", zap.Error(err)) logger.Warn("failed to validate message", zap.Error(err))
return err return err
} }
receivedMessage := &Message{ receivedMessage := &common.Message{
ID: state.CurrentMessageState.MessageID, ID: state.CurrentMessageState.MessageID,
ChatMessage: state.CurrentMessageState.Message, ChatMessage: state.CurrentMessageState.Message,
From: state.CurrentMessageState.Contact.ID, From: state.CurrentMessageState.Contact.ID,
@ -369,7 +369,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
chat.UnviewedMessagesCount++ chat.UnviewedMessagesCount++
} else { } else {
// Our own message, mark as sent // Our own message, mark as sent
receivedMessage.OutgoingStatus = OutgoingStatusSent receivedMessage.OutgoingStatus = common.OutgoingStatusSent
} }
err = chat.UpdateFromMessage(receivedMessage, state.Timesource) err = chat.UpdateFromMessage(receivedMessage, state.Timesource)
@ -401,7 +401,7 @@ func (m *MessageHandler) HandleRequestAddressForTransaction(messageState *Receiv
if err != nil { if err != nil {
return err return err
} }
message := &Message{ message := &common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
Clock: command.Clock, Clock: command.Clock,
Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Timestamp: messageState.CurrentMessageState.WhisperTimestamp,
@ -410,11 +410,11 @@ func (m *MessageHandler) HandleRequestAddressForTransaction(messageState *Receiv
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND,
}, },
CommandParameters: &CommandParameters{ CommandParameters: &common.CommandParameters{
ID: messageState.CurrentMessageState.MessageID, ID: messageState.CurrentMessageState.MessageID,
Value: command.Value, Value: command.Value,
Contract: command.Contract, Contract: command.Contract,
CommandState: CommandStateRequestAddressForTransaction, CommandState: common.CommandStateRequestAddressForTransaction,
}, },
} }
return m.handleCommandMessage(messageState, message) return m.handleCommandMessage(messageState, message)
@ -425,7 +425,7 @@ func (m *MessageHandler) HandleRequestTransaction(messageState *ReceivedMessageS
if err != nil { if err != nil {
return err return err
} }
message := &Message{ message := &common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
Clock: command.Clock, Clock: command.Clock,
Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Timestamp: messageState.CurrentMessageState.WhisperTimestamp,
@ -434,11 +434,11 @@ func (m *MessageHandler) HandleRequestTransaction(messageState *ReceivedMessageS
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND, ContentType: protobuf.ChatMessage_TRANSACTION_COMMAND,
}, },
CommandParameters: &CommandParameters{ CommandParameters: &common.CommandParameters{
ID: messageState.CurrentMessageState.MessageID, ID: messageState.CurrentMessageState.MessageID,
Value: command.Value, Value: command.Value,
Contract: command.Contract, Contract: command.Contract,
CommandState: CommandStateRequestTransaction, CommandState: common.CommandStateRequestTransaction,
Address: command.Address, Address: command.Address,
}, },
} }
@ -466,7 +466,7 @@ func (m *MessageHandler) HandleAcceptRequestAddressForTransaction(messageState *
return errors.New("Initial message must originate from us") 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") return errors.New("Wrong state for command")
} }
@ -475,7 +475,7 @@ func (m *MessageHandler) HandleAcceptRequestAddressForTransaction(messageState *
initialMessage.Text = requestAddressForTransactionAcceptedMessage initialMessage.Text = requestAddressForTransactionAcceptedMessage
initialMessage.CommandParameters.Address = command.Address initialMessage.CommandParameters.Address = command.Address
initialMessage.Seen = false initialMessage.Seen = false
initialMessage.CommandParameters.CommandState = CommandStateRequestAddressForTransactionAccepted initialMessage.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionAccepted
// Hide previous message // Hide previous message
previousMessage, err := m.persistence.MessageByCommandID(messageState.CurrentMessageState.Contact.ID, command.Id) 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") 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") return errors.New("Wrong state for command")
} }
@ -544,7 +544,7 @@ func (m *MessageHandler) HandleDeclineRequestAddressForTransaction(messageState
oldMessage.Timestamp = messageState.CurrentMessageState.WhisperTimestamp oldMessage.Timestamp = messageState.CurrentMessageState.WhisperTimestamp
oldMessage.Text = requestAddressForTransactionDeclinedMessage oldMessage.Text = requestAddressForTransactionDeclinedMessage
oldMessage.Seen = false oldMessage.Seen = false
oldMessage.CommandParameters.CommandState = CommandStateRequestAddressForTransactionDeclined oldMessage.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionDeclined
// Hide previous message // Hide previous message
err = m.persistence.HideMessage(command.Id) 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") 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") return errors.New("Wrong state for command")
} }
@ -585,7 +585,7 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM
oldMessage.Timestamp = messageState.CurrentMessageState.WhisperTimestamp oldMessage.Timestamp = messageState.CurrentMessageState.WhisperTimestamp
oldMessage.Text = transactionRequestDeclinedMessage oldMessage.Text = transactionRequestDeclinedMessage
oldMessage.Seen = false oldMessage.Seen = false
oldMessage.CommandParameters.CommandState = CommandStateRequestTransactionDeclined oldMessage.CommandParameters.CommandState = common.CommandStateRequestTransactionDeclined
// Hide previous message // Hide previous message
err = m.persistence.HideMessage(command.Id) err = m.persistence.HideMessage(command.Id)

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -40,6 +41,7 @@ func (db sqlitePersistence) tableUserMessagesAllFields() string {
audio_type, audio_type,
audio_duration_ms, audio_duration_ms,
audio_base64, audio_base64,
mentions,
command_id, command_id,
command_value, command_value,
command_from, command_from,
@ -74,6 +76,7 @@ func (db sqlitePersistence) tableUserMessagesAllFieldsJoin() string {
m1.image_base64, m1.image_base64,
COALESCE(m1.audio_duration_ms,0), COALESCE(m1.audio_duration_ms,0),
m1.audio_base64, m1.audio_base64,
m1.mentions,
m1.command_id, m1.command_id,
m1.command_value, m1.command_value,
m1.command_from, m1.command_from,
@ -104,18 +107,19 @@ type scanner interface {
Scan(dest ...interface{}) error 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 quotedText sql.NullString
var quotedParsedText []byte var quotedParsedText []byte
var quotedFrom sql.NullString var quotedFrom sql.NullString
var quotedImage sql.NullString var quotedImage sql.NullString
var quotedAudio sql.NullString var quotedAudio sql.NullString
var quotedAudioDuration sql.NullInt64 var quotedAudioDuration sql.NullInt64
var serializedMentions []byte
var alias sql.NullString var alias sql.NullString
var identicon sql.NullString var identicon sql.NullString
sticker := &protobuf.StickerMessage{} sticker := &protobuf.StickerMessage{}
command := &CommandParameters{} command := &common.CommandParameters{}
audio := &protobuf.AudioMessage{} audio := &protobuf.AudioMessage{}
args := []interface{}{ args := []interface{}{
@ -138,6 +142,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
&message.Base64Image, &message.Base64Image,
&audio.DurationMs, &audio.DurationMs,
&message.Base64Audio, &message.Base64Audio,
&serializedMentions,
&command.ID, &command.ID,
&command.Value, &command.Value,
&command.From, &command.From,
@ -165,7 +170,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
} }
if quotedText.Valid { if quotedText.Valid {
message.QuotedMessage = &QuotedMessage{ message.QuotedMessage = &common.QuotedMessage{
From: quotedFrom.String, From: quotedFrom.String,
Text: quotedText.String, Text: quotedText.String,
ParsedText: quotedParsedText, ParsedText: quotedParsedText,
@ -177,6 +182,13 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
message.Alias = alias.String message.Alias = alias.String
message.Identicon = identicon.String message.Identicon = identicon.String
if serializedMentions != nil {
err := json.Unmarshal(serializedMentions, &message.Mentions)
if err != nil {
return err
}
}
switch message.ContentType { switch message.ContentType {
case protobuf.ChatMessage_STICKER: case protobuf.ChatMessage_STICKER:
message.Payload = &protobuf.ChatMessage_Sticker{Sticker: sticker} message.Payload = &protobuf.ChatMessage_Sticker{Sticker: sticker}
@ -191,7 +203,7 @@ func (db sqlitePersistence) tableUserMessagesScanAllFields(row scanner, message
return nil return nil
} }
func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]interface{}, error) { func (db sqlitePersistence) tableUserMessagesAllValues(message *common.Message) ([]interface{}, error) {
sticker := message.GetSticker() sticker := message.GetSticker()
if sticker == nil { if sticker == nil {
sticker = &protobuf.StickerMessage{} sticker = &protobuf.StickerMessage{}
@ -209,7 +221,16 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte
command := message.CommandParameters command := message.CommandParameters
if command == nil { if command == nil {
command = &CommandParameters{} command = &common.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{}{ return []interface{}{
message.ID, message.ID,
@ -235,6 +256,7 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte
audio.Type, audio.Type,
audio.DurationMs, audio.DurationMs,
message.Base64Audio, message.Base64Audio,
serializedMentions,
command.ID, command.ID,
command.Value, command.Value,
command.From, command.From,
@ -250,7 +272,7 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *Message) ([]inte
}, nil }, 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 var err error
if tx == nil { if tx == nil {
tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{})
@ -267,7 +289,7 @@ func (db sqlitePersistence) messageByID(tx *sql.Tx, id string) (*Message, error)
}() }()
} }
var message Message var message common.Message
allFields := db.tableUserMessagesAllFieldsJoin() allFields := db.tableUserMessagesAllFieldsJoin()
row := tx.QueryRow( row := tx.QueryRow(
@ -301,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() allFields := db.tableUserMessagesAllFieldsJoin()
row := db.db.QueryRow( row := db.db.QueryRow(
@ -342,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) return db.messageByID(nil, id)
} }
@ -377,7 +399,7 @@ func (db sqlitePersistence) MessagesExist(ids []string) (map[string]bool, error)
return result, nil return result, nil
} }
func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*Message, error) { func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*common.Message, error) {
if len(ids) == 0 { if len(ids) == 0 {
return nil, nil return nil, nil
} }
@ -412,9 +434,9 @@ func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*Message, error) {
} }
defer rows.Close() defer rows.Close()
var result []*Message var result []*common.Message
for rows.Next() { for rows.Next() {
var message Message var message common.Message
if err := db.tableUserMessagesScanAllFields(rows, &message); err != nil { if err := db.tableUserMessagesScanAllFields(rows, &message); err != nil {
return nil, err return nil, err
} }
@ -427,7 +449,7 @@ func (db sqlitePersistence) MessagesByIDs(ids []string) ([]*Message, error) {
// MessageByChatID returns all messages for a given chatID in descending order. // MessageByChatID returns all messages for a given chatID in descending order.
// Ordering is accomplished using two concatenated values: ClockValue and ID. // 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. // 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 := "" cursorWhere := ""
if currCursor != "" { if currCursor != "" {
cursorWhere = "AND cursor <= ?" cursorWhere = "AND cursor <= ?"
@ -470,12 +492,12 @@ func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, li
defer rows.Close() defer rows.Close()
var ( var (
result []*Message result []*common.Message
cursors []string cursors []string
) )
for rows.Next() { for rows.Next() {
var ( var (
message Message message common.Message
cursor string cursor string
) )
if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil { if err := db.tableUserMessagesScanAllFields(rows, &message, &cursor); err != nil {
@ -566,7 +588,7 @@ func (db sqlitePersistence) EmojiReactionsByChatID(chatID string, currCursor str
return result, nil 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{}) tx, err := db.db.BeginTx(context.Background(), &sql.TxOptions{})
if err != nil { if err != nil {
return return

View File

@ -91,7 +91,7 @@ type RawResponse struct {
type MessengerResponse struct { type MessengerResponse struct {
Chats []*Chat `json:"chats,omitempty"` Chats []*Chat `json:"chats,omitempty"`
Messages []*Message `json:"messages,omitempty"` Messages []*common.Message `json:"messages,omitempty"`
Contacts []*Contact `json:"contacts,omitempty"` Contacts []*Contact `json:"contacts,omitempty"`
Installations []*multidevice.Installation `json:"installations,omitempty"` Installations []*multidevice.Installation `json:"installations,omitempty"`
EmojiReactions []*EmojiReaction `json:"emojiReactions,omitempty"` EmojiReactions []*EmojiReaction `json:"emojiReactions,omitempty"`
@ -227,7 +227,7 @@ func NewMessenger(
pushNotificationClientConfig.Logger = logger pushNotificationClientConfig.Logger = logger
pushNotificationClientConfig.InstallationID = installationID 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}) handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database})
@ -1358,7 +1358,7 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bo
} }
func (m *Messenger) saveChat(chat *Chat) error { func (m *Messenger) saveChat(chat *Chat) error {
_, ok := m.allChats[chat.ID] previousChat, ok := m.allChats[chat.ID]
if chat.OneToOne() { if chat.OneToOne() {
name, identicon, err := generateAliasAndIdenticon(chat.ID) name, identicon, err := generateAliasAndIdenticon(chat.ID)
if err != nil { if err != nil {
@ -1370,11 +1370,21 @@ func (m *Messenger) saveChat(chat *Chat) error {
} }
// Sync chat if it's a new active public chat // Sync chat if it's a new active public chat
if !ok && chat.Active && chat.Public() { if !ok && chat.Active && chat.Public() {
if err := m.syncPublicChat(context.Background(), chat); err != nil { if err := m.syncPublicChat(context.Background(), chat); err != nil {
return err 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) err := m.persistence.SaveChat(*chat)
if err != nil { if err != nil {
return err return err
@ -1424,7 +1434,12 @@ func (m *Messenger) DeleteChat(chatID string) error {
if err != nil { if err != nil {
return err 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 return nil
} }
@ -1490,8 +1505,7 @@ func (m *Messenger) reregisterForPushNotifications() error {
return nil return nil
} }
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.Reregister(m.pushNotificationOptions())
return m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs)
} }
func (m *Messenger) SaveContact(contact *Contact) error { func (m *Messenger) SaveContact(contact *Contact) error {
@ -1649,9 +1663,18 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage)
case ChatTypePrivateGroupChat: case ChatTypePrivateGroupChat:
logger.Debug("sending group message", zap.String("chatName", chat.Name)) logger.Debug("sending group message", zap.String("chatName", chat.Name))
if spec.Recipients == nil { if spec.Recipients == nil {
spec.Recipients, err = chat.MembersAsPublicKeys() // Chat messages are only dispatched to users who joined
if err != nil { if spec.MessageType == protobuf.ApplicationMetadataMessage_CHAT_MESSAGE {
return nil, err 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() hasPairedDevices := m.hasPairedDevices()
@ -1689,7 +1712,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage)
} }
// SendChatMessage takes a minimal message and sends it based on the corresponding chat // 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() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
@ -1758,7 +1781,7 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes
id, err := m.dispatchMessage(ctx, common.RawMessage{ id, err := m.dispatchMessage(ctx, common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
SendPushNotification: m.featureFlags.PushNotifications && !chat.Public(), SendPushNotification: m.featureFlags.PushNotifications,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
ResendAutomatically: true, ResendAutomatically: true,
@ -1778,12 +1801,12 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Messages, err = m.pullMessagesAndResponsesFromDB([]*Message{message}) response.Messages, err = m.pullMessagesAndResponsesFromDB([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2251,10 +2274,17 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationPublicChat) p := msg.ParsedMessage.Interface().(protobuf.SyncInstallationPublicChat)
logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p)) logger.Debug("Handling SyncInstallationPublicChat", zap.Any("message", p))
err = m.handler.HandleSyncInstallationPublicChat(messageState, p) added := m.handler.HandleSyncInstallationPublicChat(messageState, p)
if err != nil {
logger.Warn("failed to handle SyncInstallationPublicChat", zap.Error(err)) // We re-register as we want to receive mentions from the newly joined public chat
continue 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: case protobuf.RequestAddressForTransaction:
@ -2540,7 +2570,7 @@ func (m *Messenger) ConfirmMessagesProcessed(messageIDs [][]byte) error {
return nil return nil
} }
func (m *Messenger) MessageByID(id string) (*Message, error) { func (m *Messenger) MessageByID(id string) (*common.Message, error) {
return m.persistence.MessageByID(id) return m.persistence.MessageByID(id)
} }
@ -2548,11 +2578,11 @@ func (m *Messenger) MessagesExist(ids []string) (map[string]bool, error) {
return m.persistence.MessagesExist(ids) 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) 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) return m.persistence.SaveMessages(messages)
} }
@ -2733,7 +2763,7 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr
return nil, errors.New("Need to be a one-to-one chat") 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) err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
if err != nil { if err != nil {
return nil, err return nil, err
@ -2760,12 +2790,12 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr
ResendAutomatically: true, ResendAutomatically: true,
}) })
message.CommandParameters = &CommandParameters{ message.CommandParameters = &common.CommandParameters{
ID: types.EncodeHex(id), ID: types.EncodeHex(id),
Value: value, Value: value,
Address: address, Address: address,
Contract: contract, Contract: contract,
CommandState: CommandStateRequestTransaction, CommandState: common.CommandStateRequestTransaction,
} }
if err != nil { if err != nil {
@ -2785,13 +2815,13 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -2810,7 +2840,7 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr
return nil, errors.New("Need to be a one-to-one chat") 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) err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
if err != nil { if err != nil {
return nil, err return nil, err
@ -2836,12 +2866,12 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr
ResendAutomatically: true, ResendAutomatically: true,
}) })
message.CommandParameters = &CommandParameters{ message.CommandParameters = &common.CommandParameters{
ID: types.EncodeHex(id), ID: types.EncodeHex(id),
From: from, From: from,
Value: value, Value: value,
Contract: contract, Contract: contract,
CommandState: CommandStateRequestAddressForTransaction, CommandState: common.CommandStateRequestAddressForTransaction,
} }
if err != nil { if err != nil {
@ -2861,13 +2891,13 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -2902,7 +2932,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
message.WhisperTimestamp = timestamp message.WhisperTimestamp = timestamp
message.Timestamp = timestamp message.Timestamp = timestamp
message.Text = "Request address for transaction accepted" message.Text = "Request address for transaction accepted"
message.OutgoingStatus = OutgoingStatusSending message.OutgoingStatus = common.OutgoingStatusSending
// Hide previous message // Hide previous message
previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID) previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID)
@ -2944,7 +2974,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
message.ID = types.EncodeHex(newMessageID) message.ID = types.EncodeHex(newMessageID)
message.CommandParameters.Address = address message.CommandParameters.Address = address
message.CommandParameters.CommandState = CommandStateRequestAddressForTransactionAccepted message.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionAccepted
err = message.PrepareContent() err = message.PrepareContent()
if err != nil { if err != nil {
@ -2956,13 +2986,13 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -2997,7 +3027,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
message.WhisperTimestamp = timestamp message.WhisperTimestamp = timestamp
message.Timestamp = timestamp message.Timestamp = timestamp
message.Text = "Transaction request declined" message.Text = "Transaction request declined"
message.OutgoingStatus = OutgoingStatusSending message.OutgoingStatus = common.OutgoingStatusSending
message.Replace = messageID message.Replace = messageID
err = m.persistence.HideMessage(messageID) err = m.persistence.HideMessage(messageID)
@ -3026,7 +3056,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
} }
message.ID = types.EncodeHex(newMessageID) message.ID = types.EncodeHex(newMessageID)
message.CommandParameters.CommandState = CommandStateRequestTransactionDeclined message.CommandParameters.CommandState = common.CommandStateRequestTransactionDeclined
err = message.PrepareContent() err = message.PrepareContent()
if err != nil { if err != nil {
@ -3038,13 +3068,13 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -3079,7 +3109,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
message.WhisperTimestamp = timestamp message.WhisperTimestamp = timestamp
message.Timestamp = timestamp message.Timestamp = timestamp
message.Text = "Request address for transaction declined" message.Text = "Request address for transaction declined"
message.OutgoingStatus = OutgoingStatusSending message.OutgoingStatus = common.OutgoingStatusSending
message.Replace = messageID message.Replace = messageID
err = m.persistence.HideMessage(messageID) err = m.persistence.HideMessage(messageID)
@ -3108,7 +3138,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
} }
message.ID = types.EncodeHex(newMessageID) message.ID = types.EncodeHex(newMessageID)
message.CommandParameters.CommandState = CommandStateRequestAddressForTransactionDeclined message.CommandParameters.CommandState = common.CommandStateRequestAddressForTransactionDeclined
err = message.PrepareContent() err = message.PrepareContent()
if err != nil { if err != nil {
@ -3120,13 +3150,13 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -3161,7 +3191,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
message.WhisperTimestamp = timestamp message.WhisperTimestamp = timestamp
message.Timestamp = timestamp message.Timestamp = timestamp
message.Text = transactionSentTxt message.Text = transactionSentTxt
message.OutgoingStatus = OutgoingStatusSending message.OutgoingStatus = common.OutgoingStatusSending
// Hide previous message // Hide previous message
previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID) previousMessage, err := m.persistence.MessageByCommandID(chatID, messageID)
@ -3207,7 +3237,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
message.ID = types.EncodeHex(newMessageID) message.ID = types.EncodeHex(newMessageID)
message.CommandParameters.TransactionHash = transactionHash message.CommandParameters.TransactionHash = transactionHash
message.CommandParameters.Signature = signature message.CommandParameters.Signature = signature
message.CommandParameters.CommandState = CommandStateTransactionSent message.CommandParameters.CommandState = common.CommandStateTransactionSent
err = message.PrepareContent() err = message.PrepareContent()
if err != nil { if err != nil {
@ -3219,13 +3249,13 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -3244,7 +3274,7 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract
return nil, errors.New("Need to be a one-to-one chat") 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) err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
if err != nil { if err != nil {
return nil, err return nil, err
@ -3282,12 +3312,12 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract
} }
message.ID = types.EncodeHex(newMessageID) message.ID = types.EncodeHex(newMessageID)
message.CommandParameters = &CommandParameters{ message.CommandParameters = &common.CommandParameters{
TransactionHash: transactionHash, TransactionHash: transactionHash,
Value: value, Value: value,
Contract: contract, Contract: contract,
Signature: signature, Signature: signature,
CommandState: CommandStateTransactionSent, CommandState: common.CommandStateTransactionSent,
} }
err = message.PrepareContent() err = message.PrepareContent()
@ -3300,13 +3330,13 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract
return nil, err return nil, err
} }
err = m.persistence.SaveMessages([]*Message{message}) err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.Chats = []*Chat{chat} response.Chats = []*Chat{chat}
response.Messages = []*Message{message} response.Messages = []*common.Message{message}
return &response, m.saveChat(chat) return &response, m.saveChat(chat)
} }
@ -3335,7 +3365,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
return nil, err return nil, err
} }
for _, validationResult := range responses { for _, validationResult := range responses {
var message *Message var message *common.Message
chatID := contactIDFromPublicKey(validationResult.Transaction.From) chatID := contactIDFromPublicKey(validationResult.Transaction.From)
chat, ok := m.allChats[chatID] chat, ok := m.allChats[chatID]
if !ok { if !ok {
@ -3344,7 +3374,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
if validationResult.Message != nil { if validationResult.Message != nil {
message = validationResult.Message message = validationResult.Message
} else { } else {
message = &Message{} message = &common.Message{}
err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport) err := extendMessageFromChat(message, chat, &m.identity.PublicKey, m.transport)
if err != nil { if err != nil {
return nil, err return nil, err
@ -3365,7 +3395,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
message.ID = validationResult.Transaction.MessageID message.ID = validationResult.Transaction.MessageID
if message.CommandParameters == nil { if message.CommandParameters == nil {
message.CommandParameters = &CommandParameters{} message.CommandParameters = &common.CommandParameters{}
} else { } else {
message.CommandParameters = validationResult.Message.CommandParameters message.CommandParameters = validationResult.Message.CommandParameters
} }
@ -3373,7 +3403,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
message.CommandParameters.Value = validationResult.Value message.CommandParameters.Value = validationResult.Value
message.CommandParameters.Contract = validationResult.Contract message.CommandParameters.Contract = validationResult.Contract
message.CommandParameters.Address = validationResult.Address message.CommandParameters.Address = validationResult.Address
message.CommandParameters.CommandState = CommandStateTransactionSent message.CommandParameters.CommandState = common.CommandStateTransactionSent
message.CommandParameters.TransactionHash = validationResult.Transaction.TransactionHash message.CommandParameters.TransactionHash = validationResult.Transaction.TransactionHash
err = message.PrepareContent() err = message.PrepareContent()
@ -3422,7 +3452,7 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
// pullMessagesAndResponsesFromDB pulls all the messages and the one that have // pullMessagesAndResponsesFromDB pulls all the messages and the one that have
// been replied to from the database // 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 var messageIDs []string
for _, message := range messages { for _, message := range messages {
messageIDs = append(messageIDs, message.ID) messageIDs = append(messageIDs, message.ID)
@ -3488,12 +3518,13 @@ func (m *Messenger) EnableSendingPushNotifications() error {
return nil return nil
} }
func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string) { func (m *Messenger) pushNotificationOptions() *pushnotificationclient.RegistrationOptions {
var contactIDs []*ecdsa.PublicKey var contactIDs []*ecdsa.PublicKey
var mutedChatIDs []string var mutedChatIDs []string
var publicChatIDs []string
for _, contact := range m.allContacts { for _, contact := range m.allContacts {
if contact.IsAdded() { if contact.IsAdded() && !contact.IsBlocked() {
pk, err := contact.PublicKey() pk, err := contact.PublicKey()
if err != nil { if err != nil {
m.logger.Warn("could not parse contact public key") m.logger.Warn("could not parse contact public key")
@ -3508,9 +3539,16 @@ func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string
if chat.Muted { if chat.Muted {
mutedChatIDs = append(mutedChatIDs, chat.ID) mutedChatIDs = append(mutedChatIDs, chat.ID)
} }
if chat.Active && chat.Public() {
publicChatIDs = append(publicChatIDs, chat.ID)
}
} }
return contactIDs, mutedChatIDs return &pushnotificationclient.RegistrationOptions{
ContactIDs: contactIDs,
MutedChatIDs: mutedChatIDs,
PublicChatIDs: publicChatIDs,
}
} }
// RegisterForPushNotification register deviceToken with any push notification server enabled // RegisterForPushNotification register deviceToken with any push notification server enabled
@ -3521,8 +3559,7 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, m.pushNotificationOptions())
err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, contactIDs, mutedChatIDs)
if err != nil { if err != nil {
m.logger.Error("failed to register for push notifications", zap.Error(err)) m.logger.Error("failed to register for push notifications", zap.Error(err))
return err return err
@ -3546,8 +3583,7 @@ func (m *Messenger) EnablePushNotificationsFromContactsOnly() error {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(m.pushNotificationOptions())
return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs)
} }
// DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone // DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone
@ -3558,8 +3594,29 @@ func (m *Messenger) DisablePushNotificationsFromContactsOnly() error {
m.mutex.Lock() m.mutex.Lock()
defer m.mutex.Unlock() defer m.mutex.Unlock()
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(m.pushNotificationOptions())
return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) }
// 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 // GetPushNotificationsServers returns the servers used for push notifications

View File

@ -24,6 +24,7 @@ import (
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
enstypes "github.com/status-im/status-go/eth-node/types/ens" 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/protobuf"
"github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/protocol/tt"
v1protocol "github.com/status-im/status-go/protocol/v1" 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{}) clock, timestamp := chat.NextClockAndTimestamp(&testTimeSource{})
message := &Message{} message := &common.Message{}
message.Text = "text-input-message" message.Text = "text-input-message"
message.ChatId = chat.ID message.ChatId = chat.ID
message.Clock = clock message.Clock = clock
@ -322,7 +323,7 @@ func (s *MessengerSuite) TestMarkMessagesSeen() {
inputMessage2.ID = "2" inputMessage2.ID = "2"
inputMessage2.Seen = false inputMessage2.Seen = false
err = s.m.SaveMessages([]*Message{inputMessage1, inputMessage2}) err = s.m.SaveMessages([]*common.Message{inputMessage1, inputMessage2})
s.Require().NoError(err) s.Require().NoError(err)
count, err := s.m.MarkMessagesSeen(chat.ID, []string{inputMessage1.ID}) count, err := s.m.MarkMessagesSeen(chat.ID, []string{inputMessage1.ID})
@ -346,7 +347,7 @@ func (s *MessengerSuite) TestMarkAllRead() {
inputMessage2.ID = "2" inputMessage2.ID = "2"
inputMessage2.Seen = false inputMessage2.Seen = false
err = s.m.SaveMessages([]*Message{inputMessage1, inputMessage2}) err = s.m.SaveMessages([]*common.Message{inputMessage1, inputMessage2})
s.Require().NoError(err) s.Require().NoError(err)
err = s.m.MarkAllRead(chat.ID) 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().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().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().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().NotEmpty(outputMessage.ID, "it sets the ID field")
s.Require().Equal(protobuf.MessageType_PUBLIC_GROUP, outputMessage.MessageType) s.Require().Equal(protobuf.MessageType_PUBLIC_GROUP, outputMessage.MessageType)
@ -389,7 +390,7 @@ func (s *MessengerSuite) TestSendPrivateOneToOne() {
pkString := hex.EncodeToString(crypto.FromECDSAPub(&recipientKey.PublicKey)) pkString := hex.EncodeToString(crypto.FromECDSAPub(&recipientKey.PublicKey))
chat := CreateOneToOneChat(pkString, &recipientKey.PublicKey, s.m.transport) chat := CreateOneToOneChat(pkString, &recipientKey.PublicKey, s.m.transport)
inputMessage := &Message{} inputMessage := &common.Message{}
inputMessage.ChatId = chat.ID inputMessage.ChatId = chat.ID
chat.LastClockValue = uint64(100000000000000) chat.LastClockValue = uint64(100000000000000)
err = s.m.SaveChat(&chat) 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().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().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().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().NotEmpty(outputMessage.ID, "it sets the ID field")
s.Require().Equal(protobuf.MessageType_ONE_TO_ONE, outputMessage.MessageType) 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) _, err = s.m.AddMembersToGroupChat(context.Background(), chat.ID, members)
s.NoError(err) s.NoError(err)
inputMessage := &Message{} inputMessage := &common.Message{}
inputMessage.ChatId = chat.ID inputMessage.ChatId = chat.ID
chat.LastClockValue = uint64(100000000000000) chat.LastClockValue = uint64(100000000000000)
err = s.m.SaveChat(chat) err = s.m.SaveChat(chat)
s.NoError(err) s.NoError(err)
response, err = s.m.SendChatMessage(context.Background(), inputMessage) 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") s.Require().Equal(1, len(response.Messages), "it returns the message")
outputMessage := response.Messages[0] 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().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().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().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().NotEmpty(outputMessage.ID, "it sets the ID field")
s.Require().Equal(protobuf.MessageType_PRIVATE_GROUP, outputMessage.MessageType) s.Require().Equal(protobuf.MessageType_PRIVATE_GROUP, outputMessage.MessageType)
} }
@ -450,7 +451,7 @@ func (s *MessengerSuite) TestSendPrivateEmptyGroup() {
chat := response.Chats[0] chat := response.Chats[0]
inputMessage := &Message{} inputMessage := &common.Message{}
inputMessage.ChatId = chat.ID inputMessage.ChatId = chat.ID
chat.LastClockValue = uint64(100000000000000) chat.LastClockValue = uint64(100000000000000)
err = s.m.SaveChat(chat) 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().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().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().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().NotEmpty(outputMessage.ID, "it sets the ID field")
s.Require().Equal(protobuf.MessageType_PRIVATE_GROUP, outputMessage.MessageType) s.Require().Equal(protobuf.MessageType_PRIVATE_GROUP, outputMessage.MessageType)
} }
@ -962,7 +963,7 @@ func (s *MessengerSuite) TestChatPersistencePublic() {
LastClockValue: 20, LastClockValue: 20,
DeletedAtClockValue: 30, DeletedAtClockValue: 30,
UnviewedMessagesCount: 40, UnviewedMessagesCount: 40,
LastMessage: &Message{}, LastMessage: &common.Message{},
} }
s.Require().NoError(s.m.SaveChat(&chat)) s.Require().NoError(s.m.SaveChat(&chat))
@ -987,7 +988,7 @@ func (s *MessengerSuite) TestDeleteChat() {
LastClockValue: 20, LastClockValue: 20,
DeletedAtClockValue: 30, DeletedAtClockValue: 30,
UnviewedMessagesCount: 40, UnviewedMessagesCount: 40,
LastMessage: &Message{}, LastMessage: &common.Message{},
} }
s.Require().NoError(s.m.SaveChat(&chat)) s.Require().NoError(s.m.SaveChat(&chat))
@ -1010,7 +1011,7 @@ func (s *MessengerSuite) TestChatPersistenceUpdate() {
LastClockValue: 20, LastClockValue: 20,
DeletedAtClockValue: 30, DeletedAtClockValue: 30,
UnviewedMessagesCount: 40, UnviewedMessagesCount: 40,
LastMessage: &Message{}, LastMessage: &common.Message{},
} }
s.Require().NoError(s.m.SaveChat(&chat)) s.Require().NoError(s.m.SaveChat(&chat))
@ -1044,7 +1045,7 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() {
LastClockValue: 20, LastClockValue: 20,
DeletedAtClockValue: 30, DeletedAtClockValue: 30,
UnviewedMessagesCount: 40, UnviewedMessagesCount: 40,
LastMessage: &Message{}, LastMessage: &common.Message{},
} }
contact := Contact{ contact := Contact{
ID: testPK, ID: testPK,
@ -1133,7 +1134,7 @@ func (s *MessengerSuite) TestChatPersistencePrivateGroupChat() {
LastClockValue: 20, LastClockValue: 20,
DeletedAtClockValue: 30, DeletedAtClockValue: 30,
UnviewedMessagesCount: 40, UnviewedMessagesCount: 40,
LastMessage: &Message{}, LastMessage: &common.Message{},
} }
s.Require().NoError(s.m.SaveChat(&chat)) s.Require().NoError(s.m.SaveChat(&chat))
savedChats := s.m.Chats() savedChats := s.m.Chats()
@ -1211,7 +1212,7 @@ func (s *MessengerSuite) TestBlockContact() {
contact.Name = "blocked" contact.Name = "blocked"
messages := []*Message{ messages := []*common.Message{
{ {
ID: "test-1", ID: "test-1",
LocalChatID: chat2.ID, LocalChatID: chat2.ID,
@ -1479,7 +1480,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(value, senderMessage.CommandParameters.Value)
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(contract, senderMessage.CommandParameters.Contract)
s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) 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 // Wait for the message to reach its destination
response, err = WaitOnMessengerResponse( response, err = WaitOnMessengerResponse(
@ -1500,7 +1501,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(value, receiverMessage.CommandParameters.Value)
s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) 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 // We decline the request
response, err = theirMessenger.DeclineRequestAddressForTransaction(context.Background(), receiverMessage.ID) response, err = theirMessenger.DeclineRequestAddressForTransaction(context.Background(), receiverMessage.ID)
@ -1514,7 +1515,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
s.Require().NotNil(senderMessage.CommandParameters) s.Require().NotNil(senderMessage.CommandParameters)
s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(value, senderMessage.CommandParameters.Value)
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) 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(initialCommandID, senderMessage.CommandParameters.ID)
s.Require().Equal(receiverMessage.ID, senderMessage.Replace) s.Require().Equal(receiverMessage.ID, senderMessage.Replace)
@ -1535,7 +1536,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
s.Require().NotNil(receiverMessage.CommandParameters) s.Require().NotNil(receiverMessage.CommandParameters)
s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(value, receiverMessage.CommandParameters.Value)
s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) 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.CommandParameters.ID)
s.Require().Equal(initialCommandID, receiverMessage.Replace) s.Require().Equal(initialCommandID, receiverMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown()) s.Require().NoError(theirMessenger.Shutdown())
@ -1574,7 +1575,7 @@ func (s *MessengerSuite) TestSendEthTransaction() {
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(contract, senderMessage.CommandParameters.Contract)
s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(value, senderMessage.CommandParameters.Value)
s.Require().Equal(signature, senderMessage.CommandParameters.Signature) 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().NotEmpty(senderMessage.ID)
s.Require().Equal("", senderMessage.Replace) s.Require().Equal("", senderMessage.Replace)
@ -1638,7 +1639,7 @@ func (s *MessengerSuite) TestSendEthTransaction() {
s.Require().Equal(transactionHash, receiverMessage.CommandParameters.TransactionHash) s.Require().Equal(transactionHash, receiverMessage.CommandParameters.TransactionHash)
s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address)
s.Require().Equal("", receiverMessage.CommandParameters.ID) 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.ID, receiverMessage.ID)
s.Require().Equal("", receiverMessage.Replace) s.Require().Equal("", receiverMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown()) s.Require().NoError(theirMessenger.Shutdown())
@ -1677,7 +1678,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() {
s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(value, senderMessage.CommandParameters.Value)
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(contract, senderMessage.CommandParameters.Contract)
s.Require().Equal(signature, senderMessage.CommandParameters.Signature) 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().NotEmpty(senderMessage.ID)
var transactions []*TransactionToValidate var transactions []*TransactionToValidate
@ -1741,7 +1742,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() {
s.Require().Equal(transactionHash, receiverMessage.CommandParameters.TransactionHash) s.Require().Equal(transactionHash, receiverMessage.CommandParameters.TransactionHash)
s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address)
s.Require().Equal("", receiverMessage.CommandParameters.ID) 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.ID, receiverMessage.ID)
s.Require().Equal(senderMessage.Replace, senderMessage.Replace) s.Require().Equal(senderMessage.Replace, senderMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown()) s.Require().NoError(theirMessenger.Shutdown())
@ -1775,7 +1776,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(value, senderMessage.CommandParameters.Value)
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(contract, senderMessage.CommandParameters.Contract)
s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) 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 // Wait for the message to reach its destination
response, err = WaitOnMessengerResponse( response, err = WaitOnMessengerResponse(
@ -1796,7 +1797,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(value, receiverMessage.CommandParameters.Value)
s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) 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 // We accept the request
response, err = theirMessenger.AcceptRequestAddressForTransaction(context.Background(), receiverMessage.ID, "some-address") 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().NotNil(senderMessage.CommandParameters)
s.Require().Equal(value, senderMessage.CommandParameters.Value) s.Require().Equal(value, senderMessage.CommandParameters.Value)
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) 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(initialCommandID, senderMessage.CommandParameters.ID)
s.Require().Equal("some-address", senderMessage.CommandParameters.Address) s.Require().Equal("some-address", senderMessage.CommandParameters.Address)
s.Require().Equal(receiverMessage.ID, senderMessage.Replace) s.Require().Equal(receiverMessage.ID, senderMessage.Replace)
@ -1832,7 +1833,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
s.Require().NotNil(receiverMessage.CommandParameters) s.Require().NotNil(receiverMessage.CommandParameters)
s.Require().Equal(value, receiverMessage.CommandParameters.Value) s.Require().Equal(value, receiverMessage.CommandParameters.Value)
s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) 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(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal("some-address", receiverMessage.CommandParameters.Address) s.Require().Equal("some-address", receiverMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, receiverMessage.Replace) s.Require().Equal(initialCommandID, receiverMessage.Replace)
@ -1868,7 +1869,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(contract, senderMessage.CommandParameters.Contract)
s.Require().Equal(receiverAddressString, senderMessage.CommandParameters.Address) s.Require().Equal(receiverAddressString, senderMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) 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 // Wait for the message to reach its destination
response, err = WaitOnMessengerResponse( response, err = WaitOnMessengerResponse(
@ -1890,7 +1891,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract)
s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) 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) response, err = theirMessenger.DeclineRequestTransaction(context.Background(), initialCommandID)
s.Require().NoError(err) s.Require().NoError(err)
@ -1904,7 +1905,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
s.Require().Equal("Transaction request declined", senderMessage.Text) s.Require().Equal("Transaction request declined", senderMessage.Text)
s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID)
s.Require().Equal(receiverMessage.ID, senderMessage.Replace) 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 // Wait for the message to reach its destination
response, err = WaitOnMessengerResponse( response, err = WaitOnMessengerResponse(
@ -1924,7 +1925,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
s.Require().Equal("Transaction request declined", receiverMessage.Text) s.Require().Equal("Transaction request declined", receiverMessage.Text)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal(initialCommandID, receiverMessage.Replace) 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()) s.Require().NoError(theirMessenger.Shutdown())
} }
@ -1957,7 +1958,7 @@ func (s *MessengerSuite) TestRequestTransaction() {
s.Require().Equal(contract, senderMessage.CommandParameters.Contract) s.Require().Equal(contract, senderMessage.CommandParameters.Contract)
s.Require().Equal(receiverAddressString, senderMessage.CommandParameters.Address) s.Require().Equal(receiverAddressString, senderMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, senderMessage.CommandParameters.ID) 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 // Wait for the message to reach its destination
response, err = WaitOnMessengerResponse( response, err = WaitOnMessengerResponse(
@ -1979,7 +1980,7 @@ func (s *MessengerSuite) TestRequestTransaction() {
s.Require().Equal(contract, receiverMessage.CommandParameters.Contract) s.Require().Equal(contract, receiverMessage.CommandParameters.Contract)
s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address) s.Require().Equal(receiverAddressString, receiverMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal(CommandStateRequestTransaction, receiverMessage.CommandParameters.CommandState) s.Require().Equal(common.CommandStateRequestTransaction, receiverMessage.CommandParameters.CommandState)
transactionHash := "0x412a851ac2ae51cad34a56c8a9cfee55d577ac5e1ac71cf488a2f2093a373799" transactionHash := "0x412a851ac2ae51cad34a56c8a9cfee55d577ac5e1ac71cf488a2f2093a373799"
signature, err := buildSignature(theirMessenger.identity, &theirMessenger.identity.PublicKey, transactionHash) 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().Equal(signature, senderMessage.CommandParameters.Signature)
s.Require().NotEmpty(senderMessage.ID) s.Require().NotEmpty(senderMessage.ID)
s.Require().Equal(receiverMessage.ID, senderMessage.Replace) 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 var transactions []*TransactionToValidate
// Wait for the message to reach its destination // 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(receiverAddressString, receiverMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID) s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal(signature, receiverMessage.CommandParameters.Signature) 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.ID, receiverMessage.ID)
s.Require().Equal(senderMessage.Replace, senderMessage.Replace) s.Require().Equal(senderMessage.Replace, senderMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown()) s.Require().NoError(theirMessenger.Shutdown())
@ -2102,7 +2103,7 @@ func (m *mockSendMessagesRequest) SendMessagesRequest(peerID []byte, request typ
} }
func (s *MessengerSuite) TestMessageJSON() { func (s *MessengerSuite) TestMessageJSON() {
message := &Message{ message := &common.Message{
ID: "test-1", ID: "test-1",
LocalChatID: "local-chat-id", LocalChatID: "local-chat-id",
Alias: "alias", Alias: "alias",
@ -2121,7 +2122,7 @@ func (s *MessengerSuite) TestMessageJSON() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(expectedJSON, string(messageJSON)) s.Require().Equal(expectedJSON, string(messageJSON))
decodedMessage := &Message{} decodedMessage := &common.Message{}
err = json.Unmarshal([]byte(expectedJSON), decodedMessage) err = json.Unmarshal([]byte(expectedJSON), decodedMessage)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(message, decodedMessage) s.Require().Equal(message, decodedMessage)
@ -2186,14 +2187,14 @@ func (s *MessageHandlerSuite) TestRun() {
Name string Name string
Error bool Error bool
Chat Chat // Chat to create Chat Chat // Chat to create
Message Message Message common.Message
SigPubKey *ecdsa.PublicKey SigPubKey *ecdsa.PublicKey
ExpectedChatID string ExpectedChatID string
}{ }{
{ {
Name: "Public chat", Name: "Public chat",
Chat: CreatePublicChat("test-chat", &testTimeSource{}), Chat: CreatePublicChat("test-chat", &testTimeSource{}),
Message: Message{ Message: common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: "test-chat", ChatId: "test-chat",
MessageType: protobuf.MessageType_PUBLIC_GROUP, MessageType: protobuf.MessageType_PUBLIC_GROUP,
@ -2205,7 +2206,7 @@ func (s *MessageHandlerSuite) TestRun() {
{ {
Name: "Private message from myself with existing chat", Name: "Private message from myself with existing chat",
Chat: CreateOneToOneChat("test-private-chat", &key1.PublicKey, &testTimeSource{}), Chat: CreateOneToOneChat("test-private-chat", &key1.PublicKey, &testTimeSource{}),
Message: Message{ Message: common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: "test-chat", ChatId: "test-chat",
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
@ -2217,7 +2218,7 @@ func (s *MessageHandlerSuite) TestRun() {
{ {
Name: "Private message from other with existing chat", Name: "Private message from other with existing chat",
Chat: CreateOneToOneChat("test-private-chat", &key2.PublicKey, &testTimeSource{}), Chat: CreateOneToOneChat("test-private-chat", &key2.PublicKey, &testTimeSource{}),
Message: Message{ Message: common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: "test-chat", ChatId: "test-chat",
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
@ -2229,7 +2230,7 @@ func (s *MessageHandlerSuite) TestRun() {
}, },
{ {
Name: "Private message from myself without chat", Name: "Private message from myself without chat",
Message: Message{ Message: common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: "test-chat", ChatId: "test-chat",
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
@ -2241,7 +2242,7 @@ func (s *MessageHandlerSuite) TestRun() {
}, },
{ {
Name: "Private message from other without chat", Name: "Private message from other without chat",
Message: Message{ Message: common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: "test-chat", ChatId: "test-chat",
MessageType: protobuf.MessageType_ONE_TO_ONE, MessageType: protobuf.MessageType_ONE_TO_ONE,
@ -2258,7 +2259,7 @@ func (s *MessageHandlerSuite) TestRun() {
}, },
{ {
Name: "Private group message", Name: "Private group message",
Message: Message{ Message: common.Message{
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
ChatId: "non-existing-chat", ChatId: "non-existing-chat",
MessageType: protobuf.MessageType_PRIVATE_GROUP, MessageType: protobuf.MessageType_PRIVATE_GROUP,

View File

@ -22,6 +22,8 @@
// 1596805115_create_group_chat_invitations_table.up.sql (231B) // 1596805115_create_group_chat_invitations_table.up.sql (231B)
// 1597322655_add_invitation_admin_chat_field.up.sql (54B) // 1597322655_add_invitation_admin_chat_field.up.sql (54B)
// 1597757544_add_nickname.up.sql (52B) // 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.down.sql (67B)
// 1599641390_add_emoji_reactions_index.up.sql (126B) // 1599641390_add_emoji_reactions_index.up.sql (126B)
// doc.go (850B) // doc.go (850B)
@ -533,6 +535,46 @@ func _1597757544_add_nicknameUpSql() (*asset, error) {
return a, nil 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") 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) { func _1599641390_add_emoji_reactions_indexDownSqlBytes() ([]byte, error) {
@ -548,7 +590,7 @@ func _1599641390_add_emoji_reactions_indexDownSql() (*asset, error) {
return nil, err 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}} 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 return a, nil
} }
@ -568,7 +610,7 @@ func _1599641390_add_emoji_reactions_indexUpSql() (*asset, error) {
return nil, err 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}} 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 return a, nil
} }
@ -728,6 +770,10 @@ var _bindata = map[string]func() (*asset, error){
"1597757544_add_nickname.up.sql": _1597757544_add_nicknameUpSql, "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.down.sql": _1599641390_add_emoji_reactions_indexDownSql,
"1599641390_add_emoji_reactions_index.up.sql": _1599641390_add_emoji_reactions_indexUpSql, "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{}}, "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{}}, "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{}}, "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.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{}}, "1599641390_add_emoji_reactions_index.up.sql": &bintree{_1599641390_add_emoji_reactions_indexUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}}, "doc.go": &bintree{docGo, map[string]*bintree{}},

View File

@ -0,0 +1 @@
ALTER TABLE user_messages ADD COLUMN mentions BLOB;

View File

@ -266,7 +266,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) {
// Restore last message // Restore last message
if lastMessageBytes != nil { if lastMessageBytes != nil {
message := &Message{} message := &common.Message{}
if err = json.Unmarshal(lastMessageBytes, message); err != nil { if err = json.Unmarshal(lastMessageBytes, message); err != nil {
return return
} }
@ -346,7 +346,7 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) {
// Restore last message // Restore last message
if lastMessageBytes != nil { if lastMessageBytes != nil {
message := &Message{} message := &common.Message{}
if err = json.Unmarshal(lastMessageBytes, message); err != nil { if err = json.Unmarshal(lastMessageBytes, message); err != nil {
return nil, err return nil, err
} }

View File

@ -11,6 +11,9 @@ import (
"github.com/stretchr/testify/require" "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/common"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/sqlite"
) )
@ -101,9 +104,9 @@ func TestMessageByChatID(t *testing.T) {
count := 1000 count := 1000
pageSize := 50 pageSize := 50
var messages []*Message var messages []*common.Message
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
messages = append(messages, &Message{ messages = append(messages, &common.Message{
ID: strconv.Itoa(i), ID: strconv.Itoa(i),
LocalChatID: chatID, LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -114,7 +117,7 @@ func TestMessageByChatID(t *testing.T) {
// Add some other chats. // Add some other chats.
if count%5 == 0 { if count%5 == 0 {
messages = append(messages, &Message{ messages = append(messages, &common.Message{
ID: strconv.Itoa(count + i), ID: strconv.Itoa(count + i),
LocalChatID: "other-chat", LocalChatID: "other-chat",
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -130,7 +133,7 @@ func TestMessageByChatID(t *testing.T) {
outOfOrderCount := pageSize + 1 outOfOrderCount := pageSize + 1
allCount := count + outOfOrderCount allCount := count + outOfOrderCount
for i := 0; i < pageSize+1; i++ { for i := 0; i < pageSize+1; i++ {
messages = append(messages, &Message{ messages = append(messages, &common.Message{
ID: strconv.Itoa(count*2 + i), ID: strconv.Itoa(count*2 + i),
LocalChatID: chatID, LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -145,13 +148,13 @@ func TestMessageByChatID(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
var ( var (
result []*Message result []*common.Message
cursor string cursor string
iter int iter int
) )
for { for {
var ( var (
items []*Message items []*common.Message
err error err error
) )
@ -181,7 +184,7 @@ func TestMessageReplies(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
p := sqlitePersistence{db: db} p := sqlitePersistence{db: db}
chatID := testPublicChatID chatID := testPublicChatID
message1 := &Message{ message1 := &common.Message{
ID: "id-1", ID: "id-1",
LocalChatID: chatID, LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -190,7 +193,7 @@ func TestMessageReplies(t *testing.T) {
}, },
From: "1", From: "1",
} }
message2 := &Message{ message2 := &common.Message{
ID: "id-2", ID: "id-2",
LocalChatID: chatID, LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -202,7 +205,7 @@ func TestMessageReplies(t *testing.T) {
From: "2", From: "2",
} }
message3 := &Message{ message3 := &common.Message{
ID: "id-3", ID: "id-3",
LocalChatID: chatID, LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -213,7 +216,7 @@ func TestMessageReplies(t *testing.T) {
From: "3", From: "3",
} }
messages := []*Message{message1, message2, message3} messages := []*common.Message{message1, message2, message3}
err = p.SaveMessages(messages) err = p.SaveMessages(messages)
require.NoError(t, err) require.NoError(t, err)
@ -225,7 +228,7 @@ func TestMessageReplies(t *testing.T) {
require.Nil(t, retrievedMessages[0].QuotedMessage) require.Nil(t, retrievedMessages[0].QuotedMessage)
require.Equal(t, "id-1", retrievedMessages[1].ResponseTo) 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.Equal(t, "", retrievedMessages[2].ResponseTo)
require.Nil(t, retrievedMessages[2].QuotedMessage) require.Nil(t, retrievedMessages[2].QuotedMessage)
@ -240,10 +243,10 @@ func TestMessageByChatIDWithTheSameClocks(t *testing.T) {
count := len(clockValues) count := len(clockValues)
pageSize := 2 pageSize := 2
var messages []*Message var messages []*common.Message
for i, clock := range clockValues { for i, clock := range clockValues {
messages = append(messages, &Message{ messages = append(messages, &common.Message{
ID: strconv.Itoa(i), ID: strconv.Itoa(i),
LocalChatID: chatID, LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{ ChatMessage: protobuf.ChatMessage{
@ -257,13 +260,13 @@ func TestMessageByChatIDWithTheSameClocks(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
var ( var (
result []*Message result []*common.Message
cursor string cursor string
iter int iter int
) )
for { for {
var ( var (
items []*Message items []*common.Message
err error err error
) )
@ -322,14 +325,14 @@ func TestDeleteMessagesByChatID(t *testing.T) {
err = insertMinimalMessage(p, "2") err = insertMinimalMessage(p, "2")
require.NoError(t, err) require.NoError(t, err)
m, _, err := p.MessageByChatID("chat-id", "", 10) m, _, err := p.MessageByChatID(testPublicChatID, "", 10)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m)) require.Equal(t, 2, len(m))
err = p.DeleteMessagesByChatID("chat-id") err = p.DeleteMessagesByChatID(testPublicChatID)
require.NoError(t, err) require.NoError(t, err)
m, _, err = p.MessageByChatID("chat-id", "", 10) m, _, err = p.MessageByChatID(testPublicChatID, "", 10)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 0, len(m)) require.Equal(t, 0, len(m))
@ -388,7 +391,7 @@ func TestPersistenceEmojiReactions(t *testing.T) {
from2 := "from-2" from2 := "from-2"
from3 := "from-3" from3 := "from-3"
chatID := "chat-id" chatID := testPublicChatID
err = insertMinimalMessage(p, id1) err = insertMinimalMessage(p, id1)
require.NoError(t, err) require.NoError(t, err)
@ -485,9 +488,9 @@ func openTestDB() (*sql.DB, error) {
} }
func insertMinimalMessage(p sqlitePersistence, id string) error { func insertMinimalMessage(p sqlitePersistence, id string) error {
return p.SaveMessages([]*Message{{ return p.SaveMessages([]*common.Message{{
ID: id, ID: id,
LocalChatID: "chat-id", LocalChatID: testPublicChatID,
ChatMessage: protobuf.ChatMessage{Text: "some-text"}, ChatMessage: protobuf.ChatMessage{Text: "some-text"},
From: "me", From: "me",
}}) }})
@ -510,7 +513,7 @@ func TestMessagesAudioDurationMsNull(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Len(t, m, 1) require.Len(t, m, 1)
m, _, err = p.MessageByChatID("chat-id", "", 10) m, _, err = p.MessageByChatID(testPublicChatID, "", 10)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, m, 1) require.Len(t, m, 1)
} }
@ -521,7 +524,7 @@ func TestSaveChat(t *testing.T) {
p := sqlitePersistence{db: db} p := sqlitePersistence{db: db}
chat := CreatePublicChat("test-chat", &testTimeSource{}) chat := CreatePublicChat("test-chat", &testTimeSource{})
chat.LastMessage = &Message{} chat.LastMessage = &common.Message{}
err = p.SaveChat(chat) err = p.SaveChat(chat)
require.NoError(t, err) require.NoError(t, err)
@ -529,3 +532,33 @@ func TestSaveChat(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &chat, retrievedChat) require.Equal(t, &chat, retrievedChat)
} }
func TestSaveMentions(t *testing.T) {
chatID := testPublicChatID
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 := common.Message{
ID: "1",
LocalChatID: chatID,
ChatMessage: protobuf.ChatMessage{Text: "some-text"},
From: "me",
Mentions: []string{pkString},
}
err = p.SaveMessages([]*common.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)
}

View File

@ -174,9 +174,7 @@ func init() {
proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage") proto.RegisterType((*ApplicationMetadataMessage)(nil), "protobuf.ApplicationMetadataMessage")
} }
func init() { func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) }
proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7)
}
var fileDescriptor_ad09a6406fcf24c7 = []byte{ var fileDescriptor_ad09a6406fcf24c7 = []byte{
// 493 bytes of a gzipped FileDescriptorProto // 493 bytes of a gzipped FileDescriptorProto

View File

@ -123,9 +123,7 @@ func init() {
proto.RegisterType((*GroupChatInvitation)(nil), "protobuf.GroupChatInvitation") proto.RegisterType((*GroupChatInvitation)(nil), "protobuf.GroupChatInvitation")
} }
func init() { func init() { proto.RegisterFile("group_chat_invitation.proto", fileDescriptor_a6a73333de6a8ebe) }
proto.RegisterFile("group_chat_invitation.proto", fileDescriptor_a6a73333de6a8ebe)
}
var fileDescriptor_a6a73333de6a8ebe = []byte{ var fileDescriptor_a6a73333de6a8ebe = []byte{
// 234 bytes of a gzipped FileDescriptorProto // 234 bytes of a gzipped FileDescriptorProto

View File

@ -335,9 +335,7 @@ func init() {
proto.RegisterType((*SyncInstallation)(nil), "protobuf.SyncInstallation") proto.RegisterType((*SyncInstallation)(nil), "protobuf.SyncInstallation")
} }
func init() { func init() { proto.RegisterFile("pairing.proto", fileDescriptor_d61ab7221f0b5518) }
proto.RegisterFile("pairing.proto", fileDescriptor_d61ab7221f0b5518)
}
var fileDescriptor_d61ab7221f0b5518 = []byte{ var fileDescriptor_d61ab7221f0b5518 = []byte{
// 397 bytes of a gzipped FileDescriptorProto // 397 bytes of a gzipped FileDescriptorProto

View File

@ -142,21 +142,23 @@ func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) {
} }
type PushNotificationRegistration struct { type PushNotificationRegistration struct {
TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` 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"` 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"` 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"` 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"` Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"`
Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,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"` 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"` 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"` Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"`
Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,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"` 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"` ApnTopic string `protobuf:"bytes,12,opt,name=apn_topic,json=apnTopic,proto3" json:"apn_topic,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` BlockMentions bool `protobuf:"varint,13,opt,name=block_mentions,json=blockMentions,proto3" json:"block_mentions,omitempty"`
XXX_unrecognized []byte `json:"-"` AllowedMentionsChatList [][]byte `protobuf:"bytes,14,rep,name=allowed_mentions_chat_list,json=allowedMentionsChatList,proto3" json:"allowed_mentions_chat_list,omitempty"`
XXX_sizecache int32 `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
} }
func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} } func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} }
@ -268,6 +270,20 @@ func (m *PushNotificationRegistration) GetApnTopic() string {
return "" 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 { type PushNotificationRegistrationResponse struct {
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` 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"` Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"`
@ -550,6 +566,7 @@ type PushNotification struct {
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` 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"` 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"` 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_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -622,6 +639,13 @@ func (m *PushNotification) GetType() PushNotification_PushNotificationType {
return PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE return PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE
} }
func (m *PushNotification) GetAuthor() []byte {
if m != nil {
return m.Author
}
return nil
}
type PushNotificationRequest struct { type PushNotificationRequest struct {
Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"` 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"` MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
@ -799,65 +823,68 @@ func init() {
func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) }
var fileDescriptor_200acd86044eaa5d = []byte{ var fileDescriptor_200acd86044eaa5d = []byte{
// 952 bytes of a gzipped FileDescriptorProto // 1002 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x51, 0x6f, 0xe3, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdd, 0x6e, 0xe3, 0x44,
0x10, 0xc6, 0x4e, 0xda, 0x24, 0x93, 0x90, 0xba, 0xab, 0xb6, 0x67, 0x0e, 0x7a, 0x04, 0x83, 0x44, 0x14, 0xc6, 0x4e, 0xda, 0x24, 0x27, 0x69, 0x9a, 0x8e, 0xfa, 0x63, 0x0a, 0x5d, 0x82, 0x01, 0x11,
0xd4, 0x87, 0x80, 0x8a, 0xc4, 0x9d, 0x78, 0x22, 0xa4, 0x4e, 0xcf, 0x6a, 0x63, 0x87, 0x8d, 0xcb, 0xf5, 0xa2, 0xa0, 0x22, 0xb1, 0x2b, 0xb8, 0x21, 0xa4, 0x6e, 0xd7, 0x6a, 0x63, 0x87, 0x89, 0xcb,
0xe9, 0x24, 0x24, 0xcb, 0xb1, 0x37, 0xad, 0x55, 0xd7, 0x6b, 0xbc, 0x9b, 0xa2, 0xbc, 0xf1, 0x03, 0x6a, 0x25, 0xa4, 0x91, 0x9b, 0x4c, 0x5b, 0xab, 0xae, 0xc7, 0x78, 0xc6, 0x45, 0xb9, 0xe3, 0x01,
0x78, 0xe1, 0x95, 0x9f, 0xc1, 0x33, 0xff, 0x80, 0x3f, 0x85, 0xbc, 0xb6, 0xd3, 0xb4, 0x71, 0xd3, 0xb8, 0xe1, 0x96, 0x2b, 0x9e, 0x81, 0x57, 0xe2, 0x45, 0x90, 0xc7, 0x76, 0xd6, 0x6d, 0xdc, 0xb4,
0x22, 0xf1, 0x94, 0xcc, 0x37, 0x3b, 0x33, 0xbb, 0xf3, 0xcd, 0x37, 0x06, 0x35, 0x9e, 0xb3, 0x2b, 0x48, 0x7b, 0x95, 0x9c, 0x6f, 0xce, 0x39, 0x33, 0xe7, 0xe7, 0xfb, 0x0c, 0x5a, 0x18, 0xf3, 0x6b,
0x27, 0xa2, 0x3c, 0x98, 0x05, 0x9e, 0xcb, 0x03, 0x1a, 0xb1, 0x5e, 0x9c, 0x50, 0x4e, 0x51, 0x5d, 0x12, 0x30, 0xe1, 0x5d, 0x7a, 0x13, 0x57, 0x78, 0x2c, 0xe0, 0x07, 0x61, 0xc4, 0x04, 0x43, 0x75,
0xfc, 0x4c, 0xe7, 0x33, 0xed, 0xef, 0x2a, 0x7c, 0x32, 0x9e, 0xb3, 0x2b, 0x73, 0xe5, 0x14, 0x26, 0xf9, 0x73, 0x11, 0x5f, 0xea, 0x7f, 0xaf, 0xc0, 0xc7, 0xa3, 0x98, 0x5f, 0x5b, 0x05, 0x2f, 0x4c,
0x97, 0x01, 0xe3, 0x89, 0xf8, 0x8f, 0x2c, 0x00, 0x4e, 0xaf, 0x49, 0xe4, 0xf0, 0x45, 0x4c, 0x54, 0xaf, 0x3c, 0x2e, 0x22, 0xf9, 0x1f, 0xd9, 0x00, 0x82, 0xdd, 0xd0, 0x80, 0x88, 0x59, 0x48, 0x35,
0xa9, 0x23, 0x75, 0xdb, 0xc7, 0x5f, 0xf7, 0x8a, 0xf8, 0xde, 0xa6, 0xd8, 0x9e, 0x9d, 0x06, 0xda, 0xa5, 0xab, 0xf4, 0xda, 0x87, 0x5f, 0x1f, 0xe4, 0xf1, 0x07, 0xcb, 0x62, 0x0f, 0x9c, 0x24, 0xd0,
0x8b, 0x98, 0xe0, 0x06, 0x2f, 0xfe, 0xa2, 0xcf, 0xa0, 0xe5, 0x93, 0xdb, 0xc0, 0x23, 0x8e, 0xc0, 0x99, 0x85, 0x14, 0x37, 0x44, 0xfe, 0x17, 0x7d, 0x0a, 0xad, 0x29, 0xbd, 0xf3, 0x26, 0x94, 0x48,
0x54, 0xb9, 0x23, 0x75, 0x1b, 0xb8, 0x99, 0x61, 0x22, 0x02, 0x7d, 0x09, 0x3b, 0x41, 0xc4, 0xb8, 0x4c, 0x53, 0xbb, 0x4a, 0xaf, 0x81, 0x9b, 0x29, 0x26, 0x23, 0xd0, 0x97, 0xb0, 0xee, 0x05, 0x5c,
0x1b, 0x86, 0x22, 0x8f, 0x13, 0xf8, 0x6a, 0x45, 0x9c, 0x6a, 0xaf, 0xc2, 0x86, 0x9f, 0xe6, 0x72, 0xb8, 0xbe, 0x2f, 0xf3, 0x10, 0x6f, 0xaa, 0x55, 0xa4, 0x57, 0xbb, 0x08, 0x9b, 0xd3, 0x24, 0x97,
0x3d, 0x8f, 0x30, 0x96, 0xe7, 0xaa, 0x66, 0xb9, 0x32, 0x2c, 0xcb, 0xa5, 0x42, 0x8d, 0x44, 0xee, 0x3b, 0x99, 0x50, 0xce, 0xb3, 0x5c, 0xd5, 0x34, 0x57, 0x8a, 0xa5, 0xb9, 0x34, 0xa8, 0xd1, 0xc0,
0x34, 0x24, 0xbe, 0xba, 0xd5, 0x91, 0xba, 0x75, 0x5c, 0x98, 0xa9, 0xe7, 0x96, 0x24, 0x2c, 0xa0, 0xbd, 0xf0, 0xe9, 0x54, 0x5b, 0xe9, 0x2a, 0xbd, 0x3a, 0xce, 0xcd, 0xe4, 0xe4, 0x8e, 0x46, 0xdc,
0x91, 0xba, 0xdd, 0x91, 0xba, 0x55, 0x5c, 0x98, 0xa8, 0x0b, 0x8a, 0x1b, 0x86, 0xf4, 0x57, 0xe2, 0x63, 0x81, 0xb6, 0xda, 0x55, 0x7a, 0x55, 0x9c, 0x9b, 0xa8, 0x07, 0x1d, 0xd7, 0xf7, 0xd9, 0x6f,
0x3b, 0xd7, 0x64, 0xe1, 0x84, 0x01, 0xe3, 0x6a, 0xad, 0x53, 0xe9, 0xb6, 0x70, 0x3b, 0xc7, 0xcf, 0x74, 0x4a, 0x6e, 0xe8, 0x8c, 0xf8, 0x1e, 0x17, 0x5a, 0xad, 0x5b, 0xe9, 0xb5, 0x70, 0x3b, 0xc3,
0xc8, 0xe2, 0x3c, 0x60, 0x1c, 0x1d, 0xc1, 0xee, 0x34, 0xa4, 0xde, 0x35, 0xf1, 0x1d, 0xef, 0xca, 0x4f, 0xe9, 0xec, 0xcc, 0xe3, 0x02, 0xed, 0xc3, 0xc6, 0x85, 0xcf, 0x26, 0x37, 0x74, 0x4a, 0x26,
0xe5, 0xd9, 0xd1, 0xba, 0x38, 0xba, 0x93, 0x3b, 0x06, 0x57, 0x2e, 0x17, 0x67, 0x5f, 0x01, 0xcc, 0xd7, 0xae, 0x48, 0x5d, 0xeb, 0xd2, 0x75, 0x3d, 0x3b, 0x18, 0x5c, 0xbb, 0x42, 0xfa, 0xbe, 0x00,
0xa3, 0x44, 0xf4, 0x87, 0x24, 0x6a, 0x43, 0x5c, 0x66, 0x05, 0x41, 0x7b, 0xb0, 0x75, 0x99, 0xb8, 0x88, 0x83, 0x48, 0xf6, 0x87, 0x46, 0x5a, 0x43, 0x3e, 0xa6, 0x80, 0xa0, 0x4d, 0x58, 0xb9, 0x8a,
0x11, 0x57, 0xa1, 0x23, 0x75, 0x5b, 0x38, 0x33, 0xd0, 0x6b, 0x50, 0x45, 0x4d, 0x67, 0x96, 0xd0, 0xdc, 0x40, 0x68, 0xd0, 0x55, 0x7a, 0x2d, 0x9c, 0x1a, 0xe8, 0x25, 0x68, 0xf2, 0x4e, 0x72, 0x19,
0x1b, 0xc7, 0xa3, 0x11, 0x77, 0x3d, 0xce, 0x1c, 0x1a, 0x85, 0x0b, 0xb5, 0x29, 0x72, 0xec, 0x0b, 0xb1, 0x5b, 0x32, 0x61, 0x81, 0x70, 0x27, 0x82, 0x13, 0x16, 0xf8, 0x33, 0xad, 0x29, 0x73, 0x6c,
0xff, 0x30, 0xa1, 0x37, 0x83, 0xdc, 0x6b, 0x45, 0xe1, 0x02, 0x7d, 0x0c, 0x0d, 0x37, 0x8e, 0x1c, 0xc9, 0xf3, 0xe3, 0x88, 0xdd, 0x0e, 0xb2, 0x53, 0x3b, 0xf0, 0x67, 0xe8, 0x23, 0x68, 0xb8, 0x61,
0x4e, 0xe3, 0xc0, 0x53, 0x5b, 0xa2, 0x31, 0x75, 0x37, 0x8e, 0xec, 0xd4, 0xd6, 0x86, 0xd0, 0x58, 0x40, 0x04, 0x0b, 0xbd, 0x89, 0xd6, 0x92, 0x8d, 0xa9, 0xbb, 0x61, 0xe0, 0x24, 0x36, 0xfa, 0x02,
0x92, 0x83, 0x0e, 0x00, 0x5d, 0x98, 0x67, 0xa6, 0xf5, 0xce, 0x74, 0x6c, 0xeb, 0x4c, 0x37, 0x1d, 0xda, 0xf2, 0x79, 0xe4, 0x96, 0x06, 0x72, 0x31, 0xb4, 0x35, 0x99, 0x6b, 0x4d, 0xa2, 0xc3, 0x0c,
0xfb, 0xfd, 0x58, 0x57, 0x3e, 0x40, 0x1f, 0x42, 0xa3, 0x3f, 0xce, 0x31, 0x45, 0x42, 0x08, 0xda, 0x44, 0xdf, 0xc3, 0x6e, 0xde, 0x88, 0xdc, 0xb1, 0x50, 0x67, 0x5b, 0xd6, 0xb9, 0x93, 0x79, 0xe4,
0x43, 0x03, 0xeb, 0x3f, 0xf4, 0x27, 0x7a, 0x8e, 0xc9, 0xda, 0x5f, 0x32, 0x7c, 0xb1, 0x69, 0x04, 0x41, 0x79, 0xbd, 0xfa, 0x31, 0x34, 0xe6, 0x0b, 0x80, 0xb6, 0x01, 0x9d, 0x5b, 0xa7, 0x96, 0xfd,
0x30, 0x61, 0x31, 0x8d, 0x18, 0x49, 0x9b, 0xcd, 0xe6, 0x82, 0x16, 0x31, 0x43, 0x75, 0x5c, 0x98, 0xc6, 0x22, 0x8e, 0x7d, 0x6a, 0x58, 0xc4, 0x79, 0x3b, 0x32, 0x3a, 0x1f, 0xa0, 0x35, 0x68, 0xf4,
0xc8, 0x84, 0x2d, 0x92, 0x24, 0x34, 0x11, 0x83, 0xd0, 0x3e, 0x7e, 0xf3, 0xbc, 0xd9, 0x2a, 0x12, 0x47, 0x19, 0xd6, 0x51, 0x10, 0x82, 0xf6, 0xb1, 0x89, 0x8d, 0x1f, 0xfb, 0x63, 0x23, 0xc3, 0x54,
0xf7, 0xf4, 0x34, 0x56, 0xcc, 0x58, 0x96, 0x06, 0x1d, 0x02, 0x24, 0xe4, 0x97, 0x39, 0x61, 0xbc, 0xfd, 0x1f, 0x15, 0x3e, 0x5f, 0xb6, 0x66, 0x98, 0xf2, 0x90, 0x05, 0x9c, 0x26, 0x03, 0xe5, 0xb1,
0x98, 0x9b, 0x16, 0x6e, 0xe4, 0x88, 0xe1, 0x6b, 0xbf, 0x49, 0xd0, 0x58, 0xc6, 0xac, 0x3e, 0x5d, 0x1c, 0xbd, 0xdc, 0xd3, 0x3a, 0xce, 0x4d, 0x64, 0xc1, 0x0a, 0x8d, 0x22, 0x16, 0xc9, 0x65, 0x6b,
0xc7, 0xd8, 0xc2, 0xc5, 0xd3, 0xf7, 0x61, 0x77, 0xd4, 0x3f, 0x1f, 0x5a, 0x78, 0xa4, 0x9f, 0x38, 0x1f, 0xbe, 0x7a, 0xde, 0xfe, 0xe6, 0x89, 0x0f, 0x8c, 0x24, 0x56, 0xee, 0x71, 0x9a, 0x06, 0xed,
0x23, 0x7d, 0x32, 0xe9, 0x9f, 0xea, 0x8a, 0x84, 0xf6, 0x40, 0xf9, 0x49, 0xc7, 0x13, 0xc3, 0x32, 0x01, 0x44, 0xf4, 0xd7, 0x98, 0x72, 0x91, 0xef, 0x66, 0x0b, 0x37, 0x32, 0xc4, 0x9c, 0xea, 0xbf,
0x9d, 0x91, 0x31, 0x19, 0xf5, 0xed, 0xc1, 0x5b, 0x45, 0x46, 0x2f, 0xe1, 0xe0, 0xc2, 0x9c, 0x5c, 0x2b, 0xd0, 0x98, 0xc7, 0x14, 0x4b, 0x37, 0x30, 0xb6, 0x71, 0x5e, 0xfa, 0x16, 0x6c, 0x0c, 0xfb,
0x8c, 0xc7, 0x16, 0xb6, 0xf5, 0x93, 0xd5, 0x1e, 0x56, 0xd2, 0xa6, 0x19, 0xa6, 0xad, 0x63, 0xb3, 0x67, 0xc7, 0x36, 0x1e, 0x1a, 0x47, 0x64, 0x68, 0x8c, 0xc7, 0xfd, 0x13, 0xa3, 0xa3, 0xa0, 0x4d,
0x7f, 0x9e, 0x55, 0x50, 0xaa, 0xda, 0x1c, 0xd4, 0x9c, 0xa9, 0x01, 0xf5, 0x49, 0xdf, 0xbf, 0x25, 0xe8, 0xfc, 0x6c, 0xe0, 0xb1, 0x69, 0x5b, 0x64, 0x68, 0x8e, 0x87, 0x7d, 0x67, 0xf0, 0xba, 0xa3,
0x09, 0x0f, 0x18, 0xb9, 0x21, 0x11, 0x47, 0xef, 0xe1, 0x60, 0x4d, 0xb5, 0x4e, 0x10, 0xcd, 0xa8, 0xa2, 0x5d, 0xd8, 0x3e, 0xb7, 0xc6, 0xe7, 0xa3, 0x91, 0x8d, 0x1d, 0xe3, 0xa8, 0xd8, 0xc3, 0x4a,
0x2a, 0x75, 0x2a, 0xdd, 0xe6, 0xf1, 0xe7, 0x8f, 0xb7, 0xe7, 0xc7, 0x39, 0x49, 0x16, 0x46, 0x34, 0xd2, 0x34, 0xd3, 0x72, 0x0c, 0x6c, 0xf5, 0xcf, 0xd2, 0x1b, 0x3a, 0x55, 0x3d, 0x06, 0x2d, 0xdb,
0xa3, 0x78, 0x2f, 0x7e, 0xe0, 0x4a, 0x51, 0xed, 0x0d, 0xec, 0x97, 0x86, 0xa0, 0x4f, 0xa1, 0x19, 0x86, 0x01, 0x9b, 0xd2, 0xfe, 0xf4, 0x8e, 0x46, 0xc2, 0xe3, 0x34, 0x99, 0x22, 0x7a, 0x0b, 0xdb,
0xcf, 0xa7, 0x61, 0xe0, 0xa5, 0xd3, 0xce, 0x44, 0xa1, 0x16, 0x86, 0x0c, 0x3a, 0x23, 0x0b, 0xa6, 0x0b, 0xca, 0x40, 0xbc, 0xe0, 0x92, 0x69, 0x4a, 0xb7, 0xd2, 0x6b, 0x1e, 0x7e, 0xf6, 0x78, 0x7b,
0xfd, 0x2e, 0xc3, 0x47, 0x8f, 0x56, 0x5b, 0x13, 0xa1, 0xb4, 0x2e, 0xc2, 0x12, 0x41, 0xcb, 0xa5, 0x7e, 0x8a, 0x69, 0x34, 0x33, 0x83, 0x4b, 0x86, 0x37, 0xc3, 0x07, 0x47, 0x09, 0xaa, 0xbf, 0x82,
0x82, 0x3e, 0x04, 0xb8, 0xbb, 0x4a, 0x41, 0xde, 0xf2, 0x26, 0xa5, 0xc2, 0xac, 0x96, 0x0a, 0x73, 0xad, 0xd2, 0x10, 0xf4, 0x09, 0x34, 0xc3, 0xf8, 0xc2, 0xf7, 0x26, 0x09, 0xa3, 0xb8, 0xbc, 0xa8,
0x29, 0xa6, 0xad, 0x55, 0x31, 0x3d, 0x2e, 0xf9, 0x23, 0xd8, 0x65, 0x24, 0xb9, 0x25, 0x89, 0xb3, 0x85, 0x21, 0x85, 0x4e, 0xe9, 0x8c, 0xeb, 0x7f, 0xa8, 0xf0, 0xe1, 0xa3, 0xb7, 0x2d, 0x10, 0x5d,
0x52, 0xbf, 0x26, 0x62, 0x77, 0x32, 0xc7, 0xb8, 0xb8, 0x85, 0xf6, 0x87, 0x04, 0x87, 0xa5, 0xed, 0x59, 0x24, 0x7a, 0x89, 0x68, 0xa8, 0xa5, 0xa2, 0xb1, 0x07, 0xf0, 0xee, 0x29, 0xf9, 0xf0, 0xe6,
0x58, 0x4e, 0xfb, 0x6b, 0xa8, 0xfe, 0x57, 0xce, 0x44, 0x40, 0xfa, 0xfe, 0x1b, 0xc2, 0x98, 0x7b, 0x2f, 0x29, 0x25, 0x7f, 0xb5, 0x94, 0xfc, 0x73, 0xc2, 0xae, 0x14, 0x09, 0xfb, 0xb8, 0xac, 0xec,
0x49, 0x8a, 0x1e, 0xb5, 0x70, 0x23, 0x47, 0x0c, 0x7f, 0x55, 0x45, 0x95, 0x7b, 0x2a, 0xd2, 0xfe, 0xc3, 0x06, 0xa7, 0xd1, 0x1d, 0x8d, 0x48, 0xe1, 0xfe, 0x9a, 0x8c, 0x5d, 0x4f, 0x0f, 0x46, 0xf9,
0x91, 0x41, 0x79, 0x98, 0xfc, 0x39, 0xcc, 0xbc, 0x80, 0x9a, 0x58, 0x5c, 0xcb, 0x6a, 0xdb, 0xa9, 0x2b, 0xf4, 0x3f, 0x15, 0xd8, 0x2b, 0x6d, 0xc7, 0x7c, 0xdb, 0x5f, 0x42, 0xf5, 0xff, 0xce, 0x4c,
0xf9, 0x34, 0x13, 0x25, 0x8c, 0x56, 0x4b, 0x19, 0x55, 0xa1, 0x96, 0xdf, 0x3f, 0xa7, 0xa2, 0x30, 0x06, 0x24, 0xf5, 0xdf, 0x52, 0xce, 0xdd, 0x2b, 0x9a, 0xf7, 0xa8, 0x85, 0x1b, 0x19, 0x62, 0x4e,
0xd1, 0x00, 0xaa, 0xe2, 0x9b, 0xb2, 0x2d, 0x74, 0xff, 0xd5, 0xe3, 0x4d, 0x5a, 0x03, 0x84, 0xdc, 0x8b, 0x2c, 0xaa, 0xdc, 0x63, 0x91, 0xfe, 0xaf, 0x0a, 0x9d, 0x87, 0xc9, 0x9f, 0x33, 0x99, 0x1d,
0x45, 0xb0, 0x66, 0xc3, 0x5e, 0x99, 0x17, 0x69, 0xf0, 0xaa, 0x10, 0xf6, 0xf8, 0x62, 0xf2, 0xd6, 0xa8, 0x49, 0xd1, 0x98, 0xdf, 0xb6, 0x9a, 0x98, 0x4f, 0x4f, 0xa2, 0x64, 0xa2, 0xd5, 0xd2, 0x89,
0x31, 0x2d, 0xdb, 0x18, 0x1a, 0x83, 0xbe, 0x9d, 0x6a, 0x37, 0x17, 0x79, 0x13, 0x6a, 0x77, 0xd2, 0x6a, 0x50, 0xcb, 0xde, 0x9f, 0x8d, 0x22, 0x37, 0xd1, 0x00, 0xaa, 0xf2, 0xbb, 0xb5, 0x2a, 0x79,
0x16, 0x86, 0x99, 0xba, 0x15, 0x59, 0x8b, 0xe1, 0xc5, 0xfa, 0xf2, 0x11, 0x1b, 0x04, 0x7d, 0x0b, 0xff, 0xd5, 0xe3, 0x4d, 0x5a, 0x00, 0x24, 0xdd, 0x65, 0x30, 0xda, 0x86, 0x55, 0x37, 0x16, 0xd7,
0xf5, 0x7c, 0x99, 0xb0, 0x9c, 0xde, 0x97, 0x1b, 0x36, 0xd6, 0xf2, 0xec, 0x13, 0xcc, 0x6a, 0x7f, 0x2c, 0xca, 0x86, 0x95, 0x59, 0xba, 0x03, 0x9b, 0x65, 0x51, 0x48, 0x87, 0x17, 0x39, 0xe1, 0x47,
0xca, 0x70, 0xb0, 0x5e, 0x32, 0xa6, 0x09, 0xdf, 0xb0, 0x3a, 0xbf, 0xbf, 0xbf, 0x3a, 0x8f, 0x36, 0xe7, 0xe3, 0xd7, 0xc4, 0xb2, 0x1d, 0xf3, 0xd8, 0x1c, 0xf4, 0x9d, 0x84, 0xd3, 0x19, 0xf9, 0x9b,
0xad, 0xce, 0x34, 0x55, 0xe9, 0xb2, 0xfc, 0x3f, 0x58, 0xd6, 0x7e, 0x7e, 0xce, 0x52, 0xdd, 0x81, 0x50, 0x7b, 0x47, 0x79, 0x69, 0x58, 0xc9, 0x71, 0x47, 0xd5, 0x43, 0xd8, 0x59, 0x14, 0x25, 0xa9,
0xe6, 0x3b, 0x6c, 0x99, 0xa7, 0xab, 0x5f, 0x94, 0x07, 0xcb, 0x51, 0x4e, 0x31, 0xd3, 0xb2, 0x1d, 0x2c, 0xe8, 0x5b, 0xa8, 0x67, 0x22, 0xc3, 0xb3, 0xb1, 0xef, 0x2e, 0x51, 0xb2, 0xb9, 0xef, 0x13,
0xac, 0x9f, 0x1a, 0x13, 0x5b, 0xc7, 0xfa, 0x89, 0x52, 0x49, 0x17, 0xe6, 0xfa, 0x83, 0x72, 0xa9, 0x13, 0xd7, 0xff, 0x52, 0x61, 0x7b, 0xf1, 0xca, 0x90, 0x45, 0x62, 0x89, 0xa4, 0xfe, 0x70, 0x5f,
0xdd, 0xef, 0xab, 0xf4, 0x50, 0x31, 0xdf, 0x41, 0x2d, 0x11, 0x6f, 0x67, 0xaa, 0x2c, 0xd8, 0xea, 0x52, 0xf7, 0x97, 0x49, 0x6a, 0x92, 0xaa, 0x54, 0x44, 0xdf, 0xc7, 0xf4, 0xf5, 0x5f, 0x9e, 0x23,
0x3c, 0xd5, 0x24, 0x5c, 0x04, 0x4c, 0xb7, 0xc5, 0xc9, 0x6f, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xb6, 0xeb, 0xd0, 0x7c, 0x83, 0x6d, 0xeb, 0xa4, 0xf8, 0xa5, 0x79, 0x20, 0x9a, 0x6a, 0x82, 0x59,
0xff, 0x42, 0x7f, 0xee, 0x48, 0x09, 0x00, 0x00, 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,
} }

View File

@ -20,6 +20,8 @@ message PushNotificationRegistration {
bytes grant = 10; bytes grant = 10;
bool allow_from_contacts_only = 11; bool allow_from_contacts_only = 11;
string apn_topic = 12; string apn_topic = 12;
bool block_mentions = 13;
repeated bytes allowed_mentions_chat_list = 14;
} }
message PushNotificationRegistrationResponse { message PushNotificationRegistrationResponse {
@ -72,6 +74,7 @@ message PushNotification {
MESSAGE = 1; MESSAGE = 1;
MENTION = 2; MENTION = 2;
} }
bytes author = 7;
} }
message PushNotificationRequest { message PushNotificationRequest {

View File

@ -641,168 +641,6 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
s.Require().NoError(server.Shutdown()) 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() { func (s *MessengerPushNotificationSuite) TestContactCode() {
bob1 := s.m bob1 := s.m
@ -853,3 +691,133 @@ func (s *MessengerPushNotificationSuite) TestContactCode() {
s.Require().NoError(alice.Shutdown()) s.Require().NoError(alice.Shutdown())
s.Require().NoError(server.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())
}

View File

@ -7,6 +7,7 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"database/sql"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
@ -47,6 +48,8 @@ import (
const encryptedPayloadKeyLength = 16 const encryptedPayloadKeyLength = 16
const accessTokenKeyLength = 16 const accessTokenKeyLength = 16
const staleQueryTimeInSeconds = 86400 const staleQueryTimeInSeconds = 86400
const mentionInstallationID = "mention"
const oneToOneChatIDLength = 132
// maxRegistrationRetries is the maximum number of attempts we do before giving up registering with a server // maxRegistrationRetries is the maximum number of attempts we do before giving up registering with a server
const maxRegistrationRetries int64 = 12 const maxRegistrationRetries int64 = 12
@ -103,13 +106,21 @@ type PushNotificationInfo struct {
} }
type SentNotification struct { type SentNotification struct {
PublicKey *ecdsa.PublicKey PublicKey *ecdsa.PublicKey
InstallationID string InstallationID string
LastTriedAt int64 LastTriedAt int64
RetryCount int64 RetryCount int64
MessageID []byte MessageID []byte
Success bool ChatID string
Error protobuf.PushNotificationReport_ErrorType 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 { func (s *SentNotification) HashedPublicKey() []byte {
@ -128,6 +139,9 @@ type Config struct {
// only from contacts // only from contacts
AllowFromContactsOnly bool AllowFromContactsOnly bool
// BlockMentions indicates whether we should not receive notification for mentions
BlockMentions bool
// InstallationID is the installation-id for this device // InstallationID is the installation-id for this device
InstallationID string InstallationID string
@ -138,8 +152,13 @@ type Config struct {
DefaultServers []*ecdsa.PublicKey DefaultServers []*ecdsa.PublicKey
} }
type MessagePersistence interface {
MessageByID(string) (*common.Message, error)
}
type Client struct { type Client struct {
persistence *Persistence persistence *Persistence
messagePersistence MessagePersistence
config *Config config *Config
@ -176,13 +195,14 @@ type Client struct {
registrationSubscriptions []chan struct{} registrationSubscriptions []chan struct{}
} }
func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { func New(persistence *Persistence, config *Config, processor *common.MessageProcessor, messagePersistence MessagePersistence) *Client {
return &Client{ return &Client{
quit: make(chan struct{}), quit: make(chan struct{}),
config: config, config: config,
messageProcessor: processor, messageProcessor: processor,
persistence: persistence, messagePersistence: messagePersistence,
reader: rand.Reader, persistence: persistence,
reader: rand.Reader,
} }
} }
@ -191,15 +211,12 @@ func (c *Client) Start() error {
return errors.New("can't start, missing message processor") 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() err := c.loadLastPushNotificationRegistration()
if err != nil { if err != nil {
return err return err
} }
c.subscribeForSentMessages() c.subscribeForMessageEvents()
c.subscribeForScheduledMessages()
// We start even if push notifications are disabled, as we might // We start even if push notifications are disabled, as we might
// actually be sending an unregister message // actually be sending an unregister message
@ -299,7 +316,7 @@ func (c *Client) GetServers() ([]*PushNotificationServer, error) {
return c.persistence.GetServers() 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") c.config.Logger.Debug("re-registering")
if len(c.deviceToken) == 0 { if len(c.deviceToken) == 0 {
c.config.Logger.Info("no device token, not registering") c.config.Logger.Info("no device token, not registering")
@ -311,7 +328,7 @@ func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string
return nil 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 // pickDefaultServesr picks n servers at random
@ -333,7 +350,7 @@ func (c *Client) pickDefaultServers(servers []*ecdsa.PublicKey) []*ecdsa.PublicK
} }
// Register registers with all the servers // 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 // stop registration loop
c.stopRegistrationLoop() c.stopRegistrationLoop()
@ -364,12 +381,12 @@ func (c *Client) Register(deviceToken, apnTopic string, tokenType protobuf.PushN
c.apnTopic = apnTopic c.apnTopic = apnTopic
c.tokenType = tokenType c.tokenType = tokenType
registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) registration, err := c.buildPushNotificationRegistrationMessage(options)
if err != nil { if err != nil {
return err return err
} }
err = c.saveLastPushNotificationRegistration(registration, contactIDs) err = c.saveLastPushNotificationRegistration(registration, options.ContactIDs)
if err != nil { if err != nil {
return err return err
} }
@ -588,22 +605,42 @@ func (c *Client) DisableSending() {
c.config.SendEnabled = false 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.Logger.Debug("enabling push notification from contacts only")
c.config.AllowFromContactsOnly = true c.config.AllowFromContactsOnly = true
if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled { if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled {
c.config.Logger.Debug("re-registering after enabling push notifications from contacts only") 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 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.Logger.Debug("disabling push notification from contacts only")
c.config.AllowFromContactsOnly = false c.config.AllowFromContactsOnly = false
if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled { if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled {
c.config.Logger.Debug("re-registering after disabling push notifications from contacts only") 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
}
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 return nil
} }
@ -644,37 +681,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 // subscribeForMessageEvents subscribes for newly sent/scheduled messages so we can check if we need to send a push notification
func (c *Client) subscribeForSentMessages() { func (c *Client) subscribeForMessageEvents() {
go func() { go func() {
c.config.Logger.Debug("subscribing for sent messages") c.config.Logger.Debug("subscribing for message events")
subscription := c.messageProcessor.SubscribeToSentMessages() sentMessagesSubscription := c.messageProcessor.SubscribeToSentMessages()
scheduledMessagesSubscription := c.messageProcessor.SubscribeToScheduledMessages()
for { for {
select { select {
case m, more := <-subscription: // order is important, since both are asynchronous, we want to process
if !more { // first scheduled messages, and after sent messages, otherwise we might
c.config.Logger.Debug("no more sent messages, quitting") // have some race conditions.
return // This does not completely rules them out, but reduced the window
} // where it might happen, a single channel should be used
c.config.Logger.Debug("handling message sent") // if this actually happens.
if err := c.handleMessageSent(m); err != nil { case m, more := <-scheduledMessagesSubscription:
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:
if !more { if !more {
c.config.Logger.Debug("no more scheduled messages, quitting") c.config.Logger.Debug("no more scheduled messages, quitting")
return return
@ -683,6 +704,16 @@ func (c *Client) subscribeForScheduledMessages() {
if err := c.handleMessageScheduled(m); err != nil { if err := c.handleMessageScheduled(m); err != nil {
c.config.Logger.Error("failed to handle message", zap.Error(err)) 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: case <-c.quit:
return return
} }
@ -785,10 +816,9 @@ func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey, force bool) e
return nil return nil
} }
// handleMessageSent is called every time a message is sent. It will check if // handleMessageSent is called every time a message is sent
// 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) handleMessageSent(sentMessage *common.SentMessage) error { func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error {
c.config.Logger.Debug("sent messages", zap.Any("messageIDs", sentMessage.MessageIDs)) c.config.Logger.Debug("sent messages", zap.Any("messageIDs", sentMessage.MessageIDs))
// Ignore if we are not sending notifications // Ignore if we are not sending notifications
@ -797,6 +827,109 @@ func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error {
return nil 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 publicKey := sentMessage.PublicKey
// Collect the messageIDs we want to notify on // Collect the messageIDs we want to notify on
@ -849,8 +982,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)) 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(message.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 // 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 { if err != nil {
return err return err
} }
@ -860,7 +1012,7 @@ func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error {
for _, messageID := range trackedMessageIDs { for _, messageID := range trackedMessageIDs {
c.config.Logger.Debug("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) 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 return err
} }
@ -890,17 +1042,19 @@ func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID strin
return c.persistence.ShouldSendNotificationFor(publicKey, installationID, messageID) return c.persistence.ShouldSendNotificationFor(publicKey, installationID, messageID)
} }
// notifiedOn marks a combination of publickey/installationid/messageID as notified // notifiedOn marks a combination of publickey/installationid/messageID/chatID/type as notified
func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte, chatID string, notificationType protobuf.PushNotification_PushNotificationType) error {
return c.persistence.UpsertSentNotification(&SentNotification{ return c.persistence.UpsertSentNotification(&SentNotification{
PublicKey: publicKey, PublicKey: publicKey,
LastTriedAt: time.Now().Unix(), LastTriedAt: time.Now().Unix(),
InstallationID: installationID, InstallationID: installationID,
MessageID: messageID, MessageID: messageID,
ChatID: chatID,
NotificationType: notificationType,
}) })
} }
func (c *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { func (c *Client) chatIDsHashes(chatIDs []string) [][]byte {
var mutedChatListHashes [][]byte var mutedChatListHashes [][]byte
for _, chatID := range chatIDs { for _, chatID := range chatIDs {
@ -979,26 +1133,27 @@ func (c *Client) getVersion() uint64 {
return c.lastPushNotificationRegistration.Version + 1 return c.lastPushNotificationRegistration.Version + 1
} }
func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { func (c *Client) buildPushNotificationRegistrationMessage(options *RegistrationOptions) (*protobuf.PushNotificationRegistration, error) {
token := c.getToken(contactIDs) token := c.getToken(options.ContactIDs)
allowedKeyList, err := c.allowedKeyList([]byte(token), contactIDs) allowedKeyList, err := c.allowedKeyList([]byte(token), options.ContactIDs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
options := &protobuf.PushNotificationRegistration{ return &protobuf.PushNotificationRegistration{
AccessToken: token, AccessToken: token,
TokenType: c.tokenType, TokenType: c.tokenType,
ApnTopic: c.apnTopic, ApnTopic: c.apnTopic,
Version: c.getVersion(), Version: c.getVersion(),
InstallationId: c.config.InstallationID, InstallationId: c.config.InstallationID,
DeviceToken: c.deviceToken, DeviceToken: c.deviceToken,
AllowFromContactsOnly: c.config.AllowFromContactsOnly, AllowFromContactsOnly: c.config.AllowFromContactsOnly,
Enabled: c.config.RemoteNotificationsEnabled, Enabled: c.config.RemoteNotificationsEnabled,
BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), BlockedChatList: c.chatIDsHashes(options.MutedChatIDs),
AllowedKeyList: allowedKeyList, BlockMentions: c.config.BlockMentions,
} AllowedMentionsChatList: c.chatIDsHashes(options.PublicChatIDs),
return options, nil AllowedKeyList: allowedKeyList,
}, nil
} }
func (c *Client) buildPushNotificationUnregisterMessage() *protobuf.PushNotificationRegistration { func (c *Client) buildPushNotificationUnregisterMessage() *protobuf.PushNotificationRegistration {
@ -1128,7 +1283,8 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis
// sendNotification sends an actual notification to the push notification server. // 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 // 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 // get latest push notification infos
err := c.queryNotificationInfo(publicKey, false) err := c.queryNotificationInfo(publicKey, false)
if err != nil { if err != nil {
@ -1187,12 +1343,12 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []
for _, infos := range actionableInfos { for _, infos := range actionableInfos {
var pushNotifications []*protobuf.PushNotification var pushNotifications []*protobuf.PushNotification
for _, i := range infos { for _, i := range infos {
// TODO: Add group chat ChatID
pushNotifications = append(pushNotifications, &protobuf.PushNotification{ 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 // 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. // 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)),
Author: common.Shake256([]byte(types.EncodeHex(crypto.FromECDSAPub(&c.config.Identity.PublicKey)))),
AccessToken: i.AccessToken, AccessToken: i.AccessToken,
PublicKey: common.HashPublicKey(publicKey), PublicKey: common.HashPublicKey(publicKey),
InstallationId: i.InstallationID, InstallationId: i.InstallationID,
@ -1251,7 +1407,7 @@ func (c *Client) resendNotification(pn *SentNotification) error {
return err 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 return err
} }
@ -1304,6 +1460,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. // 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 { func (c *Client) registrationLoop() error {
if c.lastPushNotificationRegistration == nil {
return nil
}
for { for {
c.config.Logger.Debug("running registration loop") c.config.Logger.Debug("running registration loop")
servers, err := c.persistence.GetServers() servers, err := c.persistence.GetServers()

View File

@ -58,7 +58,7 @@ func (s *ClientSuite) SetupTest() {
InstallationID: s.installationID, InstallationID: s.installationID,
} }
s.client = New(s.persistence, config, nil) s.client = New(s.persistence, config, nil, nil)
} }
func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
@ -74,6 +74,11 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
s.Require().NoError(err) s.Require().NoError(err)
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
options := &RegistrationOptions{
ContactIDs: contactIDs,
MutedChatIDs: mutedChatList,
}
// Set random generator for uuid // Set random generator for uuid
var seed int64 = 1 var seed int64 = 1
uuid.SetRand(rand.New(rand.NewSource(seed))) uuid.SetRand(rand.New(rand.NewSource(seed)))
@ -88,7 +93,7 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
// Set reader // Set reader
s.client.reader = bytes.NewReader([]byte(expectedUUID)) s.client.reader = bytes.NewReader([]byte(expectedUUID))
options := &protobuf.PushNotificationRegistration{ registration := &protobuf.PushNotificationRegistration{
Version: 1, Version: 1,
AccessToken: expectedUUID, AccessToken: expectedUUID,
DeviceToken: testDeviceToken, DeviceToken: testDeviceToken,
@ -97,24 +102,36 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
BlockedChatList: mutedChatListHashes, BlockedChatList: mutedChatListHashes,
} }
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(options, actualMessage) s.Require().Equal(registration, actualMessage)
} }
func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() { func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() {
mutedChatList := []string{"a", "b"} mutedChatList := []string{"a", "b"}
publicChatList := []string{"c", "d"}
// build chat lish hashes // build muted chat lish hashes
var mutedChatListHashes [][]byte var mutedChatListHashes [][]byte
for _, chatID := range mutedChatList { for _, chatID := range mutedChatList {
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID))) 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() contactKey, err := crypto.GenerateKey()
s.Require().NoError(err) s.Require().NoError(err)
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
options := &RegistrationOptions{
ContactIDs: contactIDs,
MutedChatIDs: mutedChatList,
PublicChatIDs: publicChatList,
}
// Set random generator for uuid // Set random generator for uuid
var seed int64 = 1 var seed int64 = 1
@ -144,21 +161,22 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsO
// Set reader // Set reader
s.client.reader = bytes.NewReader([]byte(expectedUUID)) s.client.reader = bytes.NewReader([]byte(expectedUUID))
options := &protobuf.PushNotificationRegistration{ registration := &protobuf.PushNotificationRegistration{
Version: 1, Version: 1,
AccessToken: expectedUUID, AccessToken: expectedUUID,
DeviceToken: testDeviceToken, DeviceToken: testDeviceToken,
InstallationId: s.installationID, InstallationId: s.installationID,
AllowFromContactsOnly: true, AllowFromContactsOnly: true,
Enabled: true, Enabled: true,
BlockedChatList: mutedChatListHashes, BlockedChatList: mutedChatListHashes,
AllowedKeyList: [][]byte{encryptedToken}, AllowedKeyList: [][]byte{encryptedToken},
AllowedMentionsChatList: publicChatListHashes,
} }
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) actualMessage, err := s.client.buildPushNotificationRegistrationMessage(options)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal(options, actualMessage) s.Require().Equal(registration, actualMessage)
} }
func (s *ClientSuite) TestHandleMessageScheduled() { func (s *ClientSuite) TestHandleMessageScheduled() {
@ -183,7 +201,7 @@ func (s *ClientSuite) TestHandleMessageScheduled() {
s.Require().True(response) s.Require().True(response)
// Save notification // 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 // Second time, should not notify
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID) response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)

View File

@ -4,6 +4,8 @@
// 1593601729_initial_schema.up.sql (1.773kB) // 1593601729_initial_schema.up.sql (1.773kB)
// 1597909626_add_server_type.down.sql (0) // 1597909626_add_server_type.down.sql (0)
// 1597909626_add_server_type.up.sql (145B) // 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) // doc.go (382B)
package migrations package migrations
@ -128,7 +130,7 @@ func _1597909626_add_server_typeDownSql() (*asset, error) {
return nil, err 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}} 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 return a, nil
} }
@ -148,11 +150,51 @@ func _1597909626_add_server_typeUpSql() (*asset, error) {
return nil, err 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}} 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 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") 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) { 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, "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, "doc.go": docGo,
} }
@ -316,11 +362,13 @@ type bintree struct {
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"1593601729_initial_schema.down.sql": &bintree{_1593601729_initial_schemaDownSql, 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{}}, "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.down.sql": &bintree{_1597909626_add_server_typeDownSql, map[string]*bintree{}},
"1597909626_add_server_type.up.sql": &bintree{_1597909626_add_server_typeUpSql, map[string]*bintree{}}, "1597909626_add_server_type.up.sql": &bintree{_1597909626_add_server_typeUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, 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. // RestoreAsset restores an asset under the given directory.

View File

@ -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;

View File

@ -277,7 +277,7 @@ func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecds
} }
func (p *Persistence) UpsertSentNotification(n *SentNotification) error { 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 return err
} }
@ -287,7 +287,7 @@ func (p *Persistence) GetSentNotification(hashedPublicKey []byte, installationID
InstallationID: installationID, InstallationID: installationID,
MessageID: messageID, 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 { if err != nil {
return nil, err return nil, err
} }
@ -309,7 +309,7 @@ func (p *Persistence) UpdateNotificationResponse(messageID []byte, response *pro
func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, error) { func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, error) {
var notifications []*SentNotification 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 { if err != nil {
return nil, err return nil, err
} }
@ -318,7 +318,7 @@ func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, erro
for rows.Next() { for rows.Next() {
var publicKeyBytes []byte var publicKeyBytes []byte
notification := &SentNotification{} notification := &SentNotification{}
err = rows.Scan(&notification.RetryCount, &notification.LastTriedAt, &notification.Error, &notification.Success, &publicKeyBytes, &notification.InstallationID, &notification.MessageID) err = rows.Scan(&notification.RetryCount, &notification.LastTriedAt, &notification.Error, &notification.Success, &publicKeyBytes, &notification.InstallationID, &notification.MessageID, &notification.ChatID, &notification.NotificationType)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -12,7 +12,8 @@ import (
"github.com/status-im/status-go/protocol/protobuf" "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 { type GoRushRequestData struct {
EncryptedMessage string `json:"encryptedMessage"` EncryptedMessage string `json:"encryptedMessage"`
@ -52,11 +53,17 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ
for _, requestAndRegistration := range requestAndRegistrations { for _, requestAndRegistration := range requestAndRegistrations {
request := requestAndRegistration.Request request := requestAndRegistration.Request
registration := requestAndRegistration.Registration registration := requestAndRegistration.Registration
var text string
if request.Type == protobuf.PushNotification_MESSAGE {
text = defaultNewMessageNotificationText
} else {
text = defaultMentionNotificationText
}
goRushRequests.Notifications = append(goRushRequests.Notifications, goRushRequests.Notifications = append(goRushRequests.Notifications,
&GoRushRequestNotification{ &GoRushRequestNotification{
Tokens: []string{registration.DeviceToken}, Tokens: []string{registration.DeviceToken},
Platform: tokenTypeToGoRushPlatform(registration.TokenType), Platform: tokenTypeToGoRushPlatform(registration.TokenType),
Message: defaultNotificationMessage, Message: text,
Topic: registration.ApnTopic, Topic: registration.ApnTopic,
Data: &GoRushRequestData{ Data: &GoRushRequestData{
EncryptedMessage: types.EncodeHex(request.Message), EncryptedMessage: types.EncodeHex(request.Message),

View File

@ -33,6 +33,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
{ {
Request: &protobuf.PushNotification{ Request: &protobuf.PushNotification{
ChatId: chatID, ChatId: chatID,
Type: protobuf.PushNotification_MESSAGE,
PublicKey: publicKey1, PublicKey: publicKey1,
InstallationId: installationID1, InstallationId: installationID1,
Message: message1, Message: message1,
@ -45,6 +46,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
{ {
Request: &protobuf.PushNotification{ Request: &protobuf.PushNotification{
ChatId: chatID, ChatId: chatID,
Type: protobuf.PushNotification_MESSAGE,
PublicKey: publicKey1, PublicKey: publicKey1,
InstallationId: installationID2, InstallationId: installationID2,
Message: message2, Message: message2,
@ -57,6 +59,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
{ {
Request: &protobuf.PushNotification{ Request: &protobuf.PushNotification{
ChatId: chatID, ChatId: chatID,
Type: protobuf.PushNotification_MENTION,
PublicKey: publicKey2, PublicKey: publicKey2,
InstallationId: installationID3, InstallationId: installationID3,
Message: message3, Message: message3,
@ -73,7 +76,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
{ {
Tokens: []string{token1}, Tokens: []string{token1},
Platform: platform1, Platform: platform1,
Message: defaultNotificationMessage, Message: defaultNewMessageNotificationText,
Data: &GoRushRequestData{ Data: &GoRushRequestData{
EncryptedMessage: hexMessage1, EncryptedMessage: hexMessage1,
ChatID: types.EncodeHex(chatID), ChatID: types.EncodeHex(chatID),
@ -83,7 +86,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
{ {
Tokens: []string{token2}, Tokens: []string{token2},
Platform: platform2, Platform: platform2,
Message: defaultNotificationMessage, Message: defaultNewMessageNotificationText,
Data: &GoRushRequestData{ Data: &GoRushRequestData{
EncryptedMessage: hexMessage2, EncryptedMessage: hexMessage2,
ChatID: types.EncodeHex(chatID), ChatID: types.EncodeHex(chatID),
@ -93,7 +96,7 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
{ {
Tokens: []string{token3}, Tokens: []string{token3},
Platform: platform3, Platform: platform3,
Message: defaultNotificationMessage, Message: defaultMentionNotificationText,
Data: &GoRushRequestData{ Data: &GoRushRequestData{
EncryptedMessage: hexMessage3, EncryptedMessage: hexMessage3,
ChatID: types.EncodeHex(chatID), ChatID: types.EncodeHex(chatID),

View File

@ -21,6 +21,8 @@ import (
const encryptedPayloadKeyLength = 16 const encryptedPayloadKeyLength = 16
const defaultGorushURL = "https://gorush.status.im" const defaultGorushURL = "https://gorush.status.im"
var errUnhandledPushNotificationType = errors.New("unhandled push notification type")
type Config struct { type Config struct {
Enabled bool Enabled bool
// Identity is our identity key // Identity is our identity key
@ -56,7 +58,7 @@ func (s *Server) Start() error {
s.config.Logger.Info("starting push notification server") s.config.Logger.Info("starting push notification server")
if s.config.Identity == nil { if s.config.Identity == nil {
s.config.Logger.Info("Identity nil") s.config.Logger.Debug("Identity nil")
// Pull identity from database // Pull identity from database
identity, err := s.persistence.GetIdentity() identity, err := s.persistence.GetIdentity()
if err != nil { if err != nil {
@ -139,7 +141,7 @@ func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, message
func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey,
messageID []byte, messageID []byte,
request protobuf.PushNotificationRequest) error { 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 // This is at-most-once semantic for now
exists, err := s.persistence.PushNotificationExists(messageID) exists, err := s.persistence.PushNotificationExists(messageID)
@ -148,14 +150,20 @@ func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey,
} }
if exists { if exists {
s.config.Logger.Info("already handled") s.config.Logger.Debug("already handled")
return nil return nil
} }
response := s.buildPushNotificationRequestResponseAndSendNotification(&request) response, requestsAndRegistrations := s.buildPushNotificationRequestResponse(&request)
//AndSendNotification(&request)
if response == nil { if response == nil {
return 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) encodedMessage, err := proto.Marshal(response)
if err != nil { if err != nil {
return err return err
@ -298,7 +306,7 @@ func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte
// buildPushNotificationQueryResponse check if we have the client information and send them back // buildPushNotificationQueryResponse check if we have the client information and send them back
func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { 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{} response := &protobuf.PushNotificationQueryResponse{}
if query == nil || len(query.PublicKeys) == 0 { if query == nil || len(query.PublicKeys) == 0 {
return response return response
@ -334,23 +342,65 @@ func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotifica
return response return response
} }
func (s *Server) blockedChatID(blockedChatIDs [][]byte, chatID []byte) bool { func (s *Server) contains(list [][]byte, chatID []byte) bool {
for _, blockedChatID := range blockedChatIDs { for _, list := range list {
if bytes.Equal(blockedChatID, chatID) { if bytes.Equal(list, chatID) {
return true return true
} }
} }
return false return false
} }
// buildPushNotificationRequestResponseAndSendNotification will build a response type reportResult struct {
// and fire-and-forget send a query to the gorush instance sendNotification bool
func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { 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{} response := &protobuf.PushNotificationResponse{}
// We don't even send a response in this case // We don't even send a response in this case
if request == nil || len(request.MessageId) == 0 { if request == nil || len(request.MessageId) == 0 {
s.config.Logger.Warn("empty message id") s.config.Logger.Warn("empty message id")
return nil return nil, nil
} }
response.MessageId = request.MessageId response.MessageId = request.MessageId
@ -360,56 +410,48 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
for _, pn := range request.Requests { for _, pn := range request.Requests {
registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId)
report := &protobuf.PushNotificationReport{ var report *protobuf.PushNotificationReport
PublicKey: pn.PublicKey,
InstallationId: pn.InstallationId,
}
if pn.Type != protobuf.PushNotification_MESSAGE {
s.config.Logger.Warn("unhandled type")
continue
}
if err != nil { if err != nil {
s.config.Logger.Error("failed to retrieve registration", zap.Error(err)) report = &protobuf.PushNotificationReport{
report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE PublicKey: pn.PublicKey,
} else if registration == nil { Error: protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE,
s.config.Logger.Warn("empty registration") InstallationId: pn.InstallationId,
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) {
// We report as successful but don't send the notification
report.Success = true
} else { } else {
// For now we just assume that the notification will be successful response, err := s.buildPushNotificationReport(pn, registration)
requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ if err != nil {
Request: pn, s.config.Logger.Warn("unhandled type")
Registration: registration, continue
}) }
report.Success = true
if response.sendNotification {
requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{
Request: pn,
Registration: registration,
})
}
report = response.report
} }
response.Reports = append(response.Reports, 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 { if len(requestAndRegistrations) == 0 {
s.config.Logger.Warn("no request and registration") 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) goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
err := sendGoRushNotification(goRushRequest, s.config.GorushURL, s.config.Logger) return 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
} }
// listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key // listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key
@ -424,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 // 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 { 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{ response := &protobuf.PushNotificationRegistrationResponse{
RequestId: common.Shake256(payload), RequestId: common.Shake256(payload),
} }
@ -442,7 +484,7 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ
} }
if registration.Unregister { 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 // 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 { if err := s.persistence.UnregisterPushNotificationRegistration(common.HashPublicKey(publicKey), registration.InstallationId, registration.Version); err != nil {
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
@ -464,7 +506,32 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ
} }
response.Success = true response.Success = true
s.config.Logger.Info("handled push notification registration successfully") s.config.Logger.Debug("handled push notification registration successfully")
return response return response
} }
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) && !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)
}

View File

@ -582,3 +582,298 @@ func (s *ServerSuite) TestbuildPushNotificationQueryResponseWithFiltering() {
s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId)
s.Require().Equal(allowedKeyList, queryResponse.Info[0].AllowedKeyList) 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)
}
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)
})
}
}

View File

@ -16,6 +16,7 @@ import (
coretypes "github.com/status-im/status-go/eth-node/core/types" 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/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
) )
const ( const (
@ -92,7 +93,7 @@ func (t *TransactionValidator) verifyTransactionSignature(ctx context.Context, f
return nil 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() data := transaction.Data()
if len(data) != tokenTransferDataLength { if len(data) != tokenTransferDataLength {
@ -153,7 +154,7 @@ func (t *TransactionValidator) validateToAddress(specifiedTo, actualTo string) b
return t.addresses[actualTo] 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()) toAddress := strings.ToLower(transaction.To().Hex())
if !t.validateToAddress(parameters.Address, toAddress) { if !t.validateToAddress(parameters.Address, toAddress) {
@ -197,7 +198,7 @@ type VerifyTransactionResponse struct {
// The address the transaction was actually sent // The address the transaction was actually sent
Address string Address string
Message *Message Message *common.Message
Transaction *TransactionToValidate Transaction *TransactionToValidate
} }
@ -205,7 +206,7 @@ type VerifyTransactionResponse struct {
// If a negative response is returned, i.e `Valid` is false, it should // If a negative response is returned, i.e `Valid` is false, it should
// not be retried. // not be retried.
// If an error is returned, validation can 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()) fromAddress := types.BytesToAddress(message.From().Bytes())
err := t.verifyTransactionSignature(ctx, from, fromAddress, parameters.TransactionHash, parameters.Signature) 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 validationResult.Message = message
} else { } else {
commandParameters := &CommandParameters{} commandParameters := &common.CommandParameters{}
commandParameters.TransactionHash = transaction.TransactionHash commandParameters.TransactionHash = transaction.TransactionHash
commandParameters.Signature = transaction.Signature commandParameters.Signature = transaction.Signature
@ -304,7 +305,7 @@ func (t *TransactionValidator) ValidateTransactions(ctx context.Context) ([]*Ver
return response, nil 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)) t.logger.Debug("validating transaction", zap.Any("transaction", parameters), zap.Any("from", from))
hash := parameters.TransactionHash hash := parameters.TransactionHash
c, cancel := context.WithTimeout(ctx, 10*time.Second) c, cancel := context.WithTimeout(ctx, 10*time.Second)

View File

@ -14,6 +14,7 @@ import (
coretypes "github.com/status-im/status-go/eth-node/core/types" 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/crypto"
"github.com/status-im/status-go/eth-node/types" "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" "github.com/status-im/status-go/protocol/tt"
) )
@ -105,7 +106,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
Transaction coretypes.Message Transaction coretypes.Message
OverrideSignatureChatKey *ecdsa.PublicKey OverrideSignatureChatKey *ecdsa.PublicKey
OverrideTransactionHash string OverrideTransactionHash string
Parameters *CommandParameters Parameters *common.CommandParameters
WalletKey *ecdsa.PrivateKey WalletKey *ecdsa.PrivateKey
From *ecdsa.PublicKey From *ecdsa.PublicKey
}{ }{
@ -123,7 +124,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
}, },
WalletKey: senderWalletKey, WalletKey: senderWalletKey,
@ -143,7 +144,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
Address: strings.ToLower(myAddress1.Hex()), Address: strings.ToLower(myAddress1.Hex()),
}, },
@ -162,7 +163,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
Address: strings.ToLower(myAddress1.Hex()), Address: strings.ToLower(myAddress1.Hex()),
}, },
@ -182,7 +183,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
Address: strings.ToLower(myAddress1.Hex()), Address: strings.ToLower(myAddress1.Hex()),
}, },
@ -202,7 +203,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
false, false,
), ),
OverrideTransactionHash: "0xdd9202df5e2f3611b5b6b716aef2a3543cc0bdd7506f50926e0869b83c8383b9", OverrideTransactionHash: "0xdd9202df5e2f3611b5b6b716aef2a3543cc0bdd7506f50926e0869b83c8383b9",
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
}, },
WalletKey: senderWalletKey, WalletKey: senderWalletKey,
@ -221,7 +222,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
Address: strings.ToLower(myAddress2.Hex()), Address: strings.ToLower(myAddress2.Hex()),
}, },
@ -240,7 +241,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
}, },
WalletKey: senderWalletKey, WalletKey: senderWalletKey,
@ -259,7 +260,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
nil, nil,
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Value: "23", Value: "23",
}, },
WalletKey: senderWalletKey, WalletKey: senderWalletKey,
@ -279,7 +280,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(transferFunction, myAddress1, big.NewInt(int64(23))), buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Value: "23", Value: "23",
}, },
@ -300,7 +301,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(transferFunction, myAddress1, big.NewInt(int64(23))), buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Address: strings.ToLower(myAddress1.Hex()), Address: strings.ToLower(myAddress1.Hex()),
Value: "23", Value: "23",
@ -322,7 +323,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(transferFunction, myAddress1, big.NewInt(int64(13))), buildData(transferFunction, myAddress1, big.NewInt(int64(13))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Value: "23", Value: "23",
}, },
@ -341,7 +342,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(transferFunction, myAddress1, big.NewInt(int64(23))), buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Address: strings.ToLower(myAddress1.Hex()), Address: strings.ToLower(myAddress1.Hex()),
Value: "23", Value: "23",
@ -362,7 +363,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(transferFunction, myAddress1, big.NewInt(int64(23))), buildData(transferFunction, myAddress1, big.NewInt(int64(23))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Address: strings.ToLower(senderAddress.Hex()), Address: strings.ToLower(senderAddress.Hex()),
Value: "23", Value: "23",
@ -383,7 +384,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(transferFunction, myAddress2, big.NewInt(int64(23))), buildData(transferFunction, myAddress2, big.NewInt(int64(23))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Address: strings.ToLower(myAddress1.Hex()), Address: strings.ToLower(myAddress1.Hex()),
Value: "23", Value: "23",
@ -403,7 +404,7 @@ func (s *TransactionValidatorSuite) TestValidateTransactions() {
buildData(notTransferFunction, myAddress1, big.NewInt(int64(23))), buildData(notTransferFunction, myAddress1, big.NewInt(int64(23))),
false, false,
), ),
Parameters: &CommandParameters{ Parameters: &common.CommandParameters{
Contract: contractString, Contract: contractString,
Value: "23", Value: "23",
}, },

View File

@ -16,6 +16,7 @@ import (
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/mailserver" "github.com/status-im/status-go/mailserver"
"github.com/status-im/status-go/protocol" "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/encryption/multidevice"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/pushnotificationclient" "github.com/status-im/status-go/protocol/pushnotificationclient"
@ -315,8 +316,8 @@ func (api *PublicAPI) SetInstallationMetadata(installationID string, data *multi
} }
type ApplicationMessagesResponse struct { type ApplicationMessagesResponse struct {
Messages []*protocol.Message `json:"messages"` Messages []*common.Message `json:"messages"`
Cursor string `json:"cursor"` Cursor string `json:"cursor"`
} }
func (api *PublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) { 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) 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) return api.service.messenger.SendChatMessage(ctx, message)
} }
@ -505,6 +506,22 @@ func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Conte
return api.service.messenger.DisablePushNotificationsFromContactsOnly() 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 { func (api *PublicAPI) AddPushNotificationsServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
if err != nil { if err != nil {

View File

@ -484,6 +484,7 @@ func buildMessengerOptions(
options = append(options, protocol.WithPushNotificationClientConfig(&pushnotificationclient.Config{ options = append(options, protocol.WithPushNotificationClientConfig(&pushnotificationclient.Config{
DefaultServers: config.DefaultPushNotificationsServers, DefaultServers: config.DefaultPushNotificationsServers,
BlockMentions: settings.PushNotificationsBlockMentions,
SendEnabled: settings.SendPushNotifications, SendEnabled: settings.SendPushNotifications,
AllowFromContactsOnly: settings.PushNotificationsFromContactsOnly, AllowFromContactsOnly: settings.PushNotificationsFromContactsOnly,
RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled, RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled,

View File

@ -4,6 +4,7 @@ package shhext
import ( import (
"context" "context"
"github.com/status-im/status-go/protocol/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -465,8 +466,8 @@ func (api *NimbusPublicAPI) VerifyENSNames(details []enstypes.ENSDetails) (map[s
} }
type ApplicationMessagesResponse struct { type ApplicationMessagesResponse struct {
Messages []*protocol.Message `json:"messages"` Messages []*common.Message `json:"messages"`
Cursor string `json:"cursor"` Cursor string `json:"cursor"`
} }
func (api *NimbusPublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) { func (api *NimbusPublicAPI) ChatMessages(chatID, cursor string, limit int) (*ApplicationMessagesResponse, error) {
@ -501,7 +502,7 @@ func (api *PublicAPI) StartMessenger() error {
return api.service.StartMessenger() 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) return api.service.messenger.SendChatMessage(ctx, message)
} }