status-go/protocol/activity_center.go

142 lines
5.3 KiB
Go
Raw Normal View History

package protocol
import (
"crypto/ecdsa"
"errors"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/verification"
)
// The activity center is a place where we store incoming notifications before
// they are shown to the users as new chats, in order to mitigate the impact of spam
// on the messenger
type ActivityCenterType int
const (
ActivityCenterNotificationNoType ActivityCenterType = iota
ActivityCenterNotificationTypeNewOneToOne
ActivityCenterNotificationTypeNewPrivateGroupChat
ActivityCenterNotificationTypeMention
ActivityCenterNotificationTypeReply
ActivityCenterNotificationTypeContactRequest
ActivityCenterNotificationTypeCommunityInvitation
ActivityCenterNotificationTypeCommunityRequest
ActivityCenterNotificationTypeCommunityMembershipRequest
ActivityCenterNotificationTypeCommunityKicked
ActivityCenterNotificationTypeContactVerification
ActivityCenterNotificationTypeContactRemoved
ActivityCenterNotificationTypeNewKeypairAddedToPairedDevice
ActivityCenterNotificationTypeOwnerTokenReceived
ActivityCenterNotificationTypeOwnershipReceived
ActivityCenterNotificationTypeOwnershipLost
ActivityCenterNotificationTypeSetSignerFailed
ActivityCenterNotificationTypeSetSignerDeclined
ActivityCenterNotificationTypeShareAccounts
)
type ActivityCenterMembershipStatus int
const (
ActivityCenterMembershipStatusIdle ActivityCenterMembershipStatus = iota
ActivityCenterMembershipStatusPending
ActivityCenterMembershipStatusAccepted
ActivityCenterMembershipStatusDeclined
refactor: EventSenders forward RequestToJoin decision to control node This is a bigger change in how community membership requests are handled among admins, token masters, owners, and control nodes. Prior to this commit, all privileged users, also known as `EventSenders`, were able to accept and reject community membership requests and those changes would be applied by all users. This commit changes this behaviour such that: 1. EventSenders can make a decision (accept, reject), but merely forward their decision to the control node, which ultimately has to confirm it 2. EventSenders are no longer removing or adding members to and from communities 3. When an eventsender signaled a decision, the membership request will enter a pending state (acceptedPending or rejectedPending) 4. Once a decision was made by one eventsender, no other eventsender can override that decision This implementation is covered with a bunch of tests: - Ensure that decision made by event sender is shared with other event senders - `testAcceptMemberRequestToJoinResponseSharedWithOtherEventSenders()` - `testRejectMemberRequestToJoinResponseSharedWithOtherEventSenders()` - Ensure memebrship request stays pending, until control node has confirmed decision by event senders - `testAcceptMemberRequestToJoinNotConfirmedByControlNode()` - `testRejectMemberRequestToJoinNotConfirmedByControlNode()` - Ensure that decision made by event sender cannot be overriden by other event senders - `testEventSenderCannotOverrideRequestToJoinState()` These test cases live in three test suites for different event sender types respectively - `OwnerWithoutCommunityKeyCommunityEventsSuite` - `TokenMasterCommunityEventsSuite` - `AdminCommunityEventsSuite` In addition to the changes mentioned above, there's also a smaller changes that ensures membership requests to *not* attached revealed wallet addresses when the requests are sent to event senders (in addition to control nodes). Requests send to a control node will still include revealed addresses as the control node needs them to verify token permissions. This commit does not yet handle the case of event senders attempting to kick and ban members. Similar to accepting and rejecting membership requests, kicking and banning need a new pending state. However, we don't track such state in local databases yet so those two cases will be handled in future commit to not have this commit grow larger.
2023-08-02 12:04:47 +00:00
ActivityCenterMembershipStatusAcceptedPending
ActivityCenterMembershipStatusDeclinedPending
ActivityCenterMembershipOwnershipChanged
)
type ActivityCenterQueryParamsRead uint
const (
ActivityCenterQueryParamsReadRead = iota + 1
ActivityCenterQueryParamsReadUnread
ActivityCenterQueryParamsReadAll
)
var ErrInvalidActivityCenterNotification = errors.New("invalid activity center notification")
type ActivityCenterNotification struct {
2022-10-24 11:33:47 +00:00
ID types.HexBytes `json:"id"`
ChatID string `json:"chatId"`
CommunityID string `json:"communityId"`
MembershipStatus ActivityCenterMembershipStatus `json:"membershipStatus"`
Name string `json:"name"`
Author string `json:"author"`
Type ActivityCenterType `json:"type"`
LastMessage *common.Message `json:"lastMessage"`
Message *common.Message `json:"message"`
ReplyMessage *common.Message `json:"replyMessage"`
Timestamp uint64 `json:"timestamp"`
Read bool `json:"read"`
Dismissed bool `json:"dismissed"`
Deleted bool `json:"deleted"`
2022-10-24 11:33:47 +00:00
Accepted bool `json:"accepted"`
ContactVerificationStatus verification.RequestStatus `json:"contactVerificationStatus"`
//Used for synchronization. Each update should increment the UpdatedAt.
//The value should represent the time when the update occurred.
UpdatedAt uint64 `json:"updatedAt"`
AlbumMessages []*common.Message `json:"albumMessages"`
}
func (n *ActivityCenterNotification) IncrementUpdatedAt(timesource common.TimeSource) {
tNow := timesource.GetCurrentTime()
// If updatead at is greater or equal than time now, we bump it
if n.UpdatedAt >= tNow {
n.UpdatedAt++
} else {
n.UpdatedAt = tNow
}
}
type ActivityCenterNotificationsRequest struct {
Cursor string `json:"cursor"`
Limit uint64 `json:"limit"`
ActivityTypes []ActivityCenterType `json:"activityTypes"`
ReadType ActivityCenterQueryParamsRead `json:"readType"`
}
type ActivityCenterCountRequest struct {
ActivityTypes []ActivityCenterType `json:"activityTypes"`
ReadType ActivityCenterQueryParamsRead `json:"readType"`
}
type ActivityCenterPaginationResponse struct {
Cursor string `json:"cursor"`
Notifications []*ActivityCenterNotification `json:"notifications"`
}
type ActivityCenterCountResponse = map[ActivityCenterType]uint64
type ActivityCenterState struct {
HasSeen bool `json:"hasSeen"`
UpdatedAt uint64 `json:"updatedAt"`
}
func (n *ActivityCenterNotification) Valid() error {
if len(n.ID) == 0 || n.Type == 0 || n.Timestamp == 0 {
return ErrInvalidActivityCenterNotification
}
return nil
}
func showMentionOrReplyActivityCenterNotification(publicKey ecdsa.PublicKey, message *common.Message, chat *Chat, responseTo *common.Message) (bool, ActivityCenterType) {
2023-04-16 15:06:00 +00:00
if chat == nil || !chat.Active || (!chat.CommunityChat() && !chat.PrivateGroupChat()) || chat.Muted {
return false, ActivityCenterNotificationNoType
}
if message.Mentioned {
return true, ActivityCenterNotificationTypeMention
}
publicKeyString := common.PubkeyToHex(&publicKey)
if responseTo != nil && responseTo.From == publicKeyString {
return true, ActivityCenterNotificationTypeReply
}
return false, ActivityCenterNotificationNoType
}