feature: view only channel reactions (#4820)
* CommunityMember channel role * make generate
This commit is contained in:
parent
84713384bb
commit
bdb2b261a6
|
@ -177,7 +177,9 @@ func (o *Community) MarshalPublicAPIJSON() ([]byte, error) {
|
|||
communityItem.Encrypted = o.Encrypted()
|
||||
}
|
||||
for id, c := range o.config.CommunityDescription.Chats {
|
||||
canPost, err := o.CanPost(o.config.MemberIdentity, id)
|
||||
// NOTE: Here `CanPost` is only set for ChatMessage. But it can be different for reactions/pin/etc.
|
||||
// Consider adding more properties to `CommunityChat` to reflect that.
|
||||
canPost, err := o.CanPost(o.config.MemberIdentity, id, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -311,7 +313,9 @@ func (o *Community) MarshalJSON() ([]byte, error) {
|
|||
communityItem.Categories[id] = category
|
||||
}
|
||||
for id, c := range o.config.CommunityDescription.Chats {
|
||||
canPost, err := o.CanPost(o.config.MemberIdentity, id)
|
||||
// NOTE: Here `CanPost` is only set for ChatMessage. But it can be different for reactions/pin/etc.
|
||||
// Consider adding more properties to `CommunityChat` to reflect that.
|
||||
canPost, err := o.CanPost(o.config.MemberIdentity, id, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1847,7 +1851,7 @@ func (o *Community) VerifyGrantSignature(data []byte) (*protobuf.Grant, error) {
|
|||
return grant, nil
|
||||
}
|
||||
|
||||
func (o *Community) CanPost(pk *ecdsa.PublicKey, chatID string) (bool, error) {
|
||||
func (o *Community) CanPost(pk *ecdsa.PublicKey, chatID string, messageType protobuf.ApplicationMetadataMessage_Type) (bool, error) {
|
||||
if o.config.CommunityDescription.Chats == nil {
|
||||
o.config.Logger.Debug("Community.CanPost: no-chats")
|
||||
return false, nil
|
||||
|
@ -1886,14 +1890,24 @@ func (o *Community) CanPost(pk *ecdsa.PublicKey, chatID string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
// Need to also be a chat member to post
|
||||
if !o.IsMemberInChat(pk, chatID) {
|
||||
o.config.Logger.Debug("Community.CanPost: not a chat member", zap.String("chat-id", chatID))
|
||||
return false, nil
|
||||
}
|
||||
member, isChatMember := chat.Members[common.PubkeyToHex(pk)]
|
||||
|
||||
// all conditions satisfied, user can post after all
|
||||
return true, nil
|
||||
switch messageType {
|
||||
case protobuf.ApplicationMetadataMessage_PIN_MESSAGE:
|
||||
pinAllowed := o.IsPrivilegedMember(pk) || o.AllowsAllMembersToPinMessage()
|
||||
return isChatMember && pinAllowed, nil
|
||||
|
||||
case protobuf.ApplicationMetadataMessage_EMOJI_REACTION:
|
||||
if !isChatMember {
|
||||
return false, nil
|
||||
}
|
||||
isPoster := member.ChannelRole == protobuf.CommunityMember_CHANNEL_ROLE_POSTER
|
||||
isViewer := member.ChannelRole == protobuf.CommunityMember_CHANNEL_ROLE_VIEWER
|
||||
return isPoster || (isViewer && chat.ViewersCanPostReactions), nil
|
||||
|
||||
default:
|
||||
return isChatMember, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Community) BuildGrant(key *ecdsa.PublicKey, chatID string) ([]byte, error) {
|
||||
|
@ -1968,8 +1982,8 @@ func (o *Community) isMember() bool {
|
|||
return o.hasMember(o.config.MemberIdentity)
|
||||
}
|
||||
|
||||
func (o *Community) CanMemberIdentityPost(chatID string) (bool, error) {
|
||||
return o.CanPost(o.config.MemberIdentity, chatID)
|
||||
func (o *Community) CanMemberIdentityPost(chatID string, messageType protobuf.ApplicationMetadataMessage_Type) (bool, error) {
|
||||
return o.CanPost(o.config.MemberIdentity, chatID, messageType)
|
||||
}
|
||||
|
||||
// CanJoin returns whether a user can join the community, only if it's
|
||||
|
@ -2052,7 +2066,9 @@ func (o *Community) AddMember(publicKey *ecdsa.PublicKey, roles []protobuf.Commu
|
|||
return changes, nil
|
||||
}
|
||||
|
||||
func (o *Community) AddMemberToChat(chatID string, publicKey *ecdsa.PublicKey, roles []protobuf.CommunityMember_Roles) (*CommunityChanges, error) {
|
||||
func (o *Community) AddMemberToChat(chatID string, publicKey *ecdsa.PublicKey,
|
||||
roles []protobuf.CommunityMember_Roles, channelRole protobuf.CommunityMember_ChannelRole) (*CommunityChanges, error) {
|
||||
|
||||
o.mutex.Lock()
|
||||
defer o.mutex.Unlock()
|
||||
|
||||
|
@ -2072,7 +2088,8 @@ func (o *Community) AddMemberToChat(chatID string, publicKey *ecdsa.PublicKey, r
|
|||
chat.Members = make(map[string]*protobuf.CommunityMember)
|
||||
}
|
||||
chat.Members[memberKey] = &protobuf.CommunityMember{
|
||||
Roles: roles,
|
||||
Roles: roles,
|
||||
ChannelRole: channelRole,
|
||||
}
|
||||
changes.ChatsModified[chatID] = &CommunityChatChanges{
|
||||
ChatModified: chat,
|
||||
|
|
|
@ -761,7 +761,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestChannelLevelKeyActions() {
|
|||
for _, member := range tc.originMembers {
|
||||
_, err := origin.AddMember(member, []protobuf.CommunityMember_Roles{})
|
||||
s.Require().NoError(err)
|
||||
_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{})
|
||||
_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
|
@ -772,7 +772,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestChannelLevelKeyActions() {
|
|||
for _, member := range tc.modifiedMembers {
|
||||
_, err := modified.AddMember(member, []protobuf.CommunityMember_Roles{})
|
||||
s.Require().NoError(err)
|
||||
_, err = modified.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{})
|
||||
_, err = modified.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
|
@ -972,7 +972,7 @@ func (s *CommunityEncryptionKeyActionSuite) TestControlNodeChange() {
|
|||
s.Require().NoError(err)
|
||||
}
|
||||
for _, member := range tc.channelMembers {
|
||||
_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{})
|
||||
_, err = origin.AddMemberToChat(channelID, member, []protobuf.CommunityMember_Roles{}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -452,9 +452,6 @@ func (s *CommunitySuite) TestCanPost() {
|
|||
notMember := &s.member3.PublicKey
|
||||
member := &s.member1.PublicKey
|
||||
|
||||
// MEMBERSHIP-NO-MEMBERSHIP-Member-> User can post
|
||||
// MEMBERSHIP-NO-MEMEBRESHIP->NON member -> User can't post
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
config Config
|
||||
|
@ -463,7 +460,7 @@ func (s *CommunitySuite) TestCanPost() {
|
|||
canPost bool
|
||||
}{
|
||||
{
|
||||
name: "no-membership org with no-membeship chat",
|
||||
name: "no-membership org with no-membership chat",
|
||||
config: s.configNoMembershipOrgNoMembershipChat(),
|
||||
member: notMember,
|
||||
canPost: false,
|
||||
|
@ -481,7 +478,7 @@ func (s *CommunitySuite) TestCanPost() {
|
|||
canPost: true,
|
||||
},
|
||||
{
|
||||
name: "monsier creator can always post of course",
|
||||
name: "creator can always post of course",
|
||||
config: s.configOnRequestOrgNoMembershipChat(),
|
||||
member: &s.identity.PublicKey,
|
||||
canPost: true,
|
||||
|
@ -494,7 +491,7 @@ func (s *CommunitySuite) TestCanPost() {
|
|||
org, err := New(tc.config, &TimeSourceStub{}, &DescriptionEncryptorMock{})
|
||||
s.Require().NoError(err)
|
||||
|
||||
canPost, err := org.CanPost(tc.member, testChatID1)
|
||||
canPost, err := org.CanPost(tc.member, testChatID1, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
||||
s.Require().Equal(tc.err, err)
|
||||
s.Require().Equal(tc.canPost, canPost)
|
||||
})
|
||||
|
|
|
@ -1046,8 +1046,12 @@ func (m *Manager) ReevaluateMembers(community *Community) (map[protobuf.Communit
|
|||
isMemberAlreadyInChannel := community.IsMemberInChat(memberPubKey, channelID)
|
||||
|
||||
if response.ViewOnlyPermissions.Satisfied || response.ViewAndPostPermissions.Satisfied {
|
||||
channelRole := protobuf.CommunityMember_CHANNEL_ROLE_VIEWER
|
||||
if response.ViewAndPostPermissions.Satisfied {
|
||||
channelRole = protobuf.CommunityMember_CHANNEL_ROLE_POSTER
|
||||
}
|
||||
if !isMemberAlreadyInChannel {
|
||||
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{})
|
||||
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{}, channelRole)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2315,27 +2319,37 @@ func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, account
|
|||
return true, protobuf.CommunityMember_ROLE_NONE, nil
|
||||
}
|
||||
|
||||
func (m *Manager) accountsSatisfyPermissionsToJoinChannels(community *Community, accounts []*protobuf.RevealedAccount) (map[string]*protobuf.CommunityChat, error) {
|
||||
result := make(map[string]*protobuf.CommunityChat)
|
||||
|
||||
func (m *Manager) accountsSatisfyPermissionsToJoinChannels(community *Community, accounts []*protobuf.RevealedAccount) (map[string]*protobuf.CommunityChat, map[string]*protobuf.CommunityChat, error) {
|
||||
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts)
|
||||
|
||||
for channelID, channel := range community.config.CommunityDescription.Chats {
|
||||
channelViewOnlyPermissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
|
||||
channelViewAndPostPermissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
|
||||
channelPermissions := append(channelViewOnlyPermissions, channelViewAndPostPermissions...)
|
||||
viewChats, err := m.accountsSatisfyPermissionTypeToJoinChannels(community, accountsAndChainIDs, protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(channelPermissions) > 0 {
|
||||
permissionResponse, err := m.PermissionChecker.CheckPermissions(channelPermissions, accountsAndChainIDs, true)
|
||||
postChats, err := m.accountsSatisfyPermissionTypeToJoinChannels(community, accountsAndChainIDs, protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return viewChats, postChats, nil
|
||||
}
|
||||
|
||||
func (m *Manager) accountsSatisfyPermissionTypeToJoinChannels(community *Community, accounts []*AccountChainIDsCombination, permissionType protobuf.CommunityTokenPermission_Type) (map[string]*protobuf.CommunityChat, error) {
|
||||
result := make(map[string]*protobuf.CommunityChat)
|
||||
|
||||
for channelID, channel := range community.config.CommunityDescription.Chats {
|
||||
permissions := community.ChannelTokenPermissionsByType(community.IDString()+channelID, permissionType)
|
||||
if len(permissions) > 0 {
|
||||
permissionResponse, err := m.PermissionChecker.CheckPermissions(permissions, accounts, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if permissionResponse.Satisfied {
|
||||
result[channelID] = channel
|
||||
if !permissionResponse.Satisfied {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
result[channelID] = channel
|
||||
}
|
||||
result[channelID] = channel
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
@ -2377,13 +2391,20 @@ func (m *Manager) AcceptRequestToJoin(dbRequest *RequestToJoin) (*Community, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
channels, err := m.accountsSatisfyPermissionsToJoinChannels(community, revealedAccounts)
|
||||
viewChannels, postChannels, err := m.accountsSatisfyPermissionsToJoinChannels(community, revealedAccounts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for channelID := range channels {
|
||||
_, err = community.AddMemberToChat(channelID, pk, memberRoles)
|
||||
for channelID := range viewChannels {
|
||||
_, err = community.AddMemberToChat(channelID, pk, memberRoles, protobuf.CommunityMember_CHANNEL_ROLE_VIEWER)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for channelID := range postChannels {
|
||||
_, err = community.AddMemberToChat(channelID, pk, memberRoles, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3460,12 +3481,12 @@ func (m *Manager) RequestsToJoinForCommunityAwaitingAddresses(id types.HexBytes)
|
|||
return m.persistence.RequestsToJoinForCommunityAwaitingAddresses(id)
|
||||
}
|
||||
|
||||
func (m *Manager) CanPost(pk *ecdsa.PublicKey, communityID string, chatID string) (bool, error) {
|
||||
func (m *Manager) CanPost(pk *ecdsa.PublicKey, communityID string, chatID string, messageType protobuf.ApplicationMetadataMessage_Type) (bool, error) {
|
||||
community, err := m.GetByIDString(communityID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return community.CanPost(pk, chatID)
|
||||
return community.CanPost(pk, chatID, messageType)
|
||||
}
|
||||
|
||||
func (m *Manager) IsEncrypted(communityID string) (bool, error) {
|
||||
|
@ -4794,7 +4815,7 @@ func (m *Manager) ReevaluatePrivilegedMember(community *Community, tokenPermissi
|
|||
// Make sure privileged user is added to every channel
|
||||
for channelID := range community.Chats() {
|
||||
if !community.IsMemberInChat(memberPubKey, channelID) {
|
||||
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{privilegedRole})
|
||||
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{privilegedRole}, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
|
||||
if err != nil {
|
||||
return alreadyHasPrivilegedRole, err
|
||||
}
|
||||
|
@ -5002,7 +5023,7 @@ func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) (
|
|||
}
|
||||
|
||||
for channelID := range community.Chats() {
|
||||
_, err = community.AddMemberToChat(channelID, &m.identity.PublicKey, ownerRole)
|
||||
_, err = community.AddMemberToChat(channelID, &m.identity.PublicKey, ownerRole, protobuf.CommunityMember_CHANNEL_ROLE_POSTER)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -568,15 +568,15 @@ func waitOnCommunitiesEvent(user *Messenger, condition func(*communities.Subscri
|
|||
|
||||
go func() {
|
||||
defer close(errCh)
|
||||
subscription := user.communitiesManager.Subscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case sub, more := <-user.communitiesManager.Subscribe():
|
||||
case sub, more := <-subscription:
|
||||
if !more {
|
||||
errCh <- errors.New("channel closed when waiting for communities event")
|
||||
return
|
||||
}
|
||||
|
||||
if condition(sub) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package protocol
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
@ -159,11 +160,7 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) SetupTest() {
|
|||
_, err = s.alice.Start()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.mockedBalances = make(map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1][gethcommon.HexToAddress(aliceAddress1)] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1][gethcommon.HexToAddress(aliceAddress2)] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1][gethcommon.HexToAddress(bobAddress)] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
s.resetMockedBalances()
|
||||
|
||||
}
|
||||
|
||||
|
@ -231,6 +228,14 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) makeAddressSatisfyTheCriteri
|
|||
s.mockedBalances[chainID][walletAddress][contractAddress] = (*hexutil.Big)(balance)
|
||||
}
|
||||
|
||||
func (s *MessengerCommunitiesTokenPermissionsSuite) resetMockedBalances() {
|
||||
s.mockedBalances = make(map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1][gethcommon.HexToAddress(aliceAddress1)] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1][gethcommon.HexToAddress(aliceAddress2)] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
s.mockedBalances[testChainID1][gethcommon.HexToAddress(bobAddress)] = make(map[gethcommon.Address]*hexutil.Big)
|
||||
}
|
||||
|
||||
func (s *MessengerCommunitiesTokenPermissionsSuite) waitOnKeyDistribution(condition func(*CommunityAndKeyActions) bool) <-chan error {
|
||||
testCommunitiesKeyDistributor, ok := s.owner.communitiesKeyDistributor.(*TestCommunitiesKeyDistributor)
|
||||
s.Require().True(ok)
|
||||
|
@ -1002,9 +1007,26 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinCommunityAsAdminWith
|
|||
s.Require().Equal(bobAddress, revealedAccounts[0].Address)
|
||||
}
|
||||
|
||||
func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions() {
|
||||
func (s *MessengerCommunitiesTokenPermissionsSuite) testViewChannelPermissions(viewersCanAddReactions bool) {
|
||||
community, chat := s.createCommunity()
|
||||
|
||||
// setup channel reactions permissions
|
||||
editedChat := &protobuf.CommunityChat{
|
||||
Identity: &protobuf.ChatIdentity{
|
||||
DisplayName: chat.Name,
|
||||
Description: chat.Description,
|
||||
Emoji: chat.Emoji,
|
||||
Color: chat.Color,
|
||||
},
|
||||
Permissions: &protobuf.CommunityPermissions{
|
||||
Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
||||
},
|
||||
ViewersCanPostReactions: viewersCanAddReactions,
|
||||
}
|
||||
|
||||
_, err := s.owner.EditCommunityChat(community.ID(), chat.ID, editedChat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// bob joins the community
|
||||
s.advertiseCommunityTo(community, s.bob)
|
||||
s.joinCommunity(community, s.bob, bobPassword, []string{})
|
||||
|
@ -1016,12 +1038,8 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
|
|||
response, err := WaitOnMessengerResponse(
|
||||
s.bob,
|
||||
func(r *MessengerResponse) bool {
|
||||
for _, message := range r.messages {
|
||||
if message.Text == msg.Text {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
_, ok := r.messages[msg.ID]
|
||||
return ok
|
||||
},
|
||||
"no messages",
|
||||
)
|
||||
|
@ -1029,6 +1047,15 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
|
|||
s.Require().Len(response.Messages(), 1)
|
||||
s.Require().Equal(msg.Text, response.Messages()[0].Text)
|
||||
|
||||
waitOnBobToBeKickedFromChannel := waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool {
|
||||
channel, ok := sub.Community.Chats()[chat.CommunityChatID()]
|
||||
return ok && len(channel.Members) == 1
|
||||
})
|
||||
waitOnChannelToBeRekeyedOnceBobIsKicked := s.waitOnKeyDistribution(func(sub *CommunityAndKeyActions) bool {
|
||||
action, ok := sub.keyActions.ChannelKeysActions[chat.CommunityChatID()]
|
||||
return ok && (action.ActionType == communities.EncryptionKeyRekey || action.ActionType == communities.EncryptionKeyAdd)
|
||||
})
|
||||
|
||||
// setup view channel permission
|
||||
channelPermissionRequest := requests.CreateCommunityTokenPermission{
|
||||
CommunityID: community.ID(),
|
||||
|
@ -1045,24 +1072,6 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
|
|||
ChatIds: []string{chat.ID},
|
||||
}
|
||||
|
||||
waitOnBobToBeKickedFromChannel := waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool {
|
||||
for channelID, channel := range sub.Community.Chats() {
|
||||
if channelID == chat.CommunityChatID() && len(channel.Members) == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
waitOnChannelToBeRekeyedOnceBobIsKicked := s.waitOnKeyDistribution(func(sub *CommunityAndKeyActions) bool {
|
||||
for channelID, action := range sub.keyActions.ChannelKeysActions {
|
||||
// We both listen for Rekey or Add, since the first time around the community goes from non-encrypted to encrypted, and that's an Add
|
||||
if channelID == chat.CommunityChatID() && (action.ActionType == communities.EncryptionKeyRekey || action.ActionType == communities.EncryptionKeyAdd) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
response, err = s.owner.CreateCommunityTokenPermission(&channelPermissionRequest)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.Communities(), 1)
|
||||
|
@ -1080,15 +1089,14 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
|
|||
_, err = WaitOnMessengerResponse(
|
||||
s.bob,
|
||||
func(r *MessengerResponse) bool {
|
||||
community, err := s.bob.GetCommunityByID(community.ID())
|
||||
c, err := s.bob.GetCommunityByID(community.ID())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if community == nil {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
channel := community.Chats()[chat.CommunityChatID()]
|
||||
channel := c.Chats()[chat.CommunityChatID()]
|
||||
return channel != nil && len(channel.Members) == 0
|
||||
},
|
||||
"no community that satisfies criteria",
|
||||
|
@ -1097,19 +1105,15 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
|
|||
|
||||
// make bob satisfy channel criteria
|
||||
s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, channelPermissionRequest.TokenCriteria[0])
|
||||
defer s.resetMockedBalances() // reset mocked balances, this test in run with different test cases
|
||||
|
||||
waitOnChannelKeyToBeDistributedToBob := s.waitOnKeyDistribution(func(sub *CommunityAndKeyActions) bool {
|
||||
for channelID, action := range sub.keyActions.ChannelKeysActions {
|
||||
if channelID == chat.CommunityChatID() && action.ActionType == communities.EncryptionKeySendToMembers {
|
||||
for memberPubKey := range action.Members {
|
||||
if memberPubKey == common.PubkeyToHex(&s.bob.identity.PublicKey) {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
action, ok := sub.keyActions.ChannelKeysActions[chat.CommunityChatID()]
|
||||
if !ok || action.ActionType != communities.EncryptionKeySendToMembers {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
_, ok = action.Members[common.PubkeyToHex(&s.bob.identity.PublicKey)]
|
||||
return ok
|
||||
})
|
||||
|
||||
// force owner to reevaluate channel members
|
||||
|
@ -1129,18 +1133,64 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
|
|||
response, err = WaitOnMessengerResponse(
|
||||
s.bob,
|
||||
func(r *MessengerResponse) bool {
|
||||
for _, message := range r.messages {
|
||||
if message.Text == msg.Text {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
_, ok := r.messages[msg.ID]
|
||||
return ok
|
||||
},
|
||||
"no messages",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.Messages(), 1)
|
||||
s.Require().Equal(msg.Text, response.Messages()[0].Text)
|
||||
|
||||
// bob can/can't post reactions
|
||||
response, err = s.bob.SendEmojiReaction(context.Background(), chat.ID, msg.ID, protobuf.EmojiReaction_THUMBS_UP)
|
||||
if !viewersCanAddReactions {
|
||||
s.Require().Error(err)
|
||||
} else {
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.emojiReactions, 1)
|
||||
reactionMessage := response.EmojiReactions()[0]
|
||||
|
||||
response, err = WaitOnMessengerResponse(
|
||||
s.owner,
|
||||
func(r *MessengerResponse) bool {
|
||||
_, ok := r.emojiReactions[reactionMessage.ID()]
|
||||
return ok
|
||||
},
|
||||
"no reactions received",
|
||||
)
|
||||
|
||||
if viewersCanAddReactions {
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.EmojiReactions(), 1)
|
||||
s.Require().Equal(response.EmojiReactions()[0].Type, protobuf.EmojiReaction_THUMBS_UP)
|
||||
} else {
|
||||
s.Require().Error(err)
|
||||
s.Require().Len(response.EmojiReactions(), 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions() {
|
||||
testCases := []struct {
|
||||
name string
|
||||
viewersCanPostReactions bool
|
||||
}{
|
||||
{
|
||||
name: "viewers are allowed to post reactions",
|
||||
viewersCanPostReactions: true,
|
||||
},
|
||||
{
|
||||
name: "viewers are forbidden to post reactions",
|
||||
viewersCanPostReactions: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.T().Run(tc.name, func(*testing.T) {
|
||||
s.testViewChannelPermissions(tc.viewersCanPostReactions)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MessengerCommunitiesTokenPermissionsSuite) testReevaluateMemberPrivilegedRoleInOpenCommunity(permissionType protobuf.CommunityTokenPermission_Type) {
|
||||
|
|
|
@ -2227,6 +2227,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMe
|
|||
if err != nil {
|
||||
return rawMessage, err
|
||||
}
|
||||
|
||||
case ChatTypeCommunityChat:
|
||||
community, err := m.communitiesManager.GetByIDString(chat.CommunityID)
|
||||
if err != nil {
|
||||
|
@ -2234,14 +2235,18 @@ func (m *Messenger) dispatchMessage(ctx context.Context, rawMessage common.RawMe
|
|||
}
|
||||
rawMessage.PubsubTopic = community.PubsubTopic()
|
||||
|
||||
canPost, err := m.communitiesManager.CanPost(&m.identity.PublicKey, chat.CommunityID, chat.CommunityChatID())
|
||||
canPost, err := m.communitiesManager.CanPost(&m.identity.PublicKey, chat.CommunityID, chat.CommunityChatID(), rawMessage.MessageType)
|
||||
if err != nil {
|
||||
return rawMessage, err
|
||||
}
|
||||
|
||||
if !canPost {
|
||||
m.logger.Error("can't post on chat", zap.String("chat-id", chat.ID), zap.String("chat-name", chat.Name))
|
||||
return rawMessage, errors.New("can't post on chat")
|
||||
m.logger.Error("can't post on chat",
|
||||
zap.String("chatID", chat.ID),
|
||||
zap.String("chatName", chat.Name),
|
||||
zap.Any("messageType", rawMessage.MessageType),
|
||||
)
|
||||
return rawMessage, fmt.Errorf("can't post message type '%d' on chat '%s'", rawMessage.MessageType, chat.ID)
|
||||
}
|
||||
|
||||
logger.Debug("sending community chat message", zap.String("chatName", chat.Name))
|
||||
|
@ -5688,143 +5693,6 @@ func generateAliasAndIdenticon(pk string) (string, string, error) {
|
|||
|
||||
}
|
||||
|
||||
func (m *Messenger) SendEmojiReaction(ctx context.Context, chatID, messageID string, emojiID protobuf.EmojiReaction_Type) (*MessengerResponse, error) {
|
||||
var response MessengerResponse
|
||||
|
||||
chat, ok := m.allChats.Load(chatID)
|
||||
if !ok {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
||||
|
||||
emojiR := &EmojiReaction{
|
||||
EmojiReaction: &protobuf.EmojiReaction{
|
||||
Clock: clock,
|
||||
MessageId: messageID,
|
||||
ChatId: chatID,
|
||||
Type: emojiID,
|
||||
},
|
||||
LocalChatID: chatID,
|
||||
From: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)),
|
||||
}
|
||||
encodedMessage, err := m.encodeChatEntity(chat, emojiR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
SkipGroupMessageWrap: true,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
|
||||
// Don't resend using datasync, that would create quite a lot
|
||||
// of traffic if clicking too eagelry
|
||||
ResendAutomatically: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.AddEmojiReaction(emojiR)
|
||||
response.AddChat(chat)
|
||||
|
||||
err = m.persistence.SaveEmojiReaction(emojiR)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Can't save emoji reaction in db")
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) EmojiReactionsByChatID(chatID string, cursor string, limit int) ([]*EmojiReaction, error) {
|
||||
chat, err := m.persistence.Chat(chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if chat.Timeline() {
|
||||
var chatIDs = []string{"@" + contactIDFromPublicKey(&m.identity.PublicKey)}
|
||||
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
||||
if contact.added() {
|
||||
chatIDs = append(chatIDs, "@"+contact.ID)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return m.persistence.EmojiReactionsByChatIDs(chatIDs, cursor, limit)
|
||||
}
|
||||
return m.persistence.EmojiReactionsByChatID(chatID, cursor, limit)
|
||||
}
|
||||
|
||||
func (m *Messenger) EmojiReactionsByChatIDMessageID(chatID string, messageID string) ([]*EmojiReaction, error) {
|
||||
_, err := m.persistence.Chat(chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.persistence.EmojiReactionsByChatIDMessageID(chatID, messageID)
|
||||
}
|
||||
|
||||
func (m *Messenger) SendEmojiReactionRetraction(ctx context.Context, emojiReactionID string) (*MessengerResponse, error) {
|
||||
emojiR, err := m.persistence.EmojiReactionByID(emojiReactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that the sender is the key owner
|
||||
pk := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))
|
||||
if emojiR.From != pk {
|
||||
return nil, errors.Errorf("identity mismatch, "+
|
||||
"emoji reactions can only be retracted by the reaction sender, "+
|
||||
"emoji reaction sent by '%s', current identity '%s'",
|
||||
emojiR.From, pk,
|
||||
)
|
||||
}
|
||||
|
||||
// Get chat and clock
|
||||
chat, ok := m.allChats.Load(emojiR.GetChatId())
|
||||
if !ok {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
||||
|
||||
// Update the relevant fields
|
||||
emojiR.Clock = clock
|
||||
emojiR.Retracted = true
|
||||
|
||||
encodedMessage, err := m.encodeChatEntity(chat, emojiR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send the marshalled EmojiReactionRetraction protobuf
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
LocalChatID: emojiR.GetChatId(),
|
||||
Payload: encodedMessage,
|
||||
SkipGroupMessageWrap: true,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
|
||||
// Don't resend using datasync, that would create quite a lot
|
||||
// of traffic if clicking too eagelry
|
||||
ResendAutomatically: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update MessengerResponse
|
||||
response := MessengerResponse{}
|
||||
emojiR.Retracted = true
|
||||
response.AddEmojiReaction(emojiR)
|
||||
response.AddChat(chat)
|
||||
|
||||
// Persist retraction state for emoji reaction
|
||||
err = m.persistence.SaveEmojiReaction(emojiR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) encodeChatEntity(chat *Chat, message common.ChatEntity) ([]byte, error) {
|
||||
var encodedMessage []byte
|
||||
var err error
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func (m *Messenger) SendEmojiReaction(ctx context.Context, chatID, messageID string, emojiID protobuf.EmojiReaction_Type) (*MessengerResponse, error) {
|
||||
var response MessengerResponse
|
||||
|
||||
chat, ok := m.allChats.Load(chatID)
|
||||
if !ok {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
||||
|
||||
emojiR := &EmojiReaction{
|
||||
EmojiReaction: &protobuf.EmojiReaction{
|
||||
Clock: clock,
|
||||
MessageId: messageID,
|
||||
ChatId: chatID,
|
||||
Type: emojiID,
|
||||
},
|
||||
LocalChatID: chatID,
|
||||
From: types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)),
|
||||
}
|
||||
encodedMessage, err := m.encodeChatEntity(chat, emojiR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
SkipGroupMessageWrap: true,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
|
||||
// Don't resend using datasync, that would create quite a lot
|
||||
// of traffic if clicking too eagelry
|
||||
ResendAutomatically: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.AddEmojiReaction(emojiR)
|
||||
response.AddChat(chat)
|
||||
|
||||
err = m.persistence.SaveEmojiReaction(emojiR)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Can't save emoji reaction in db")
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) EmojiReactionsByChatID(chatID string, cursor string, limit int) ([]*EmojiReaction, error) {
|
||||
chat, err := m.persistence.Chat(chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if chat.Timeline() {
|
||||
var chatIDs = []string{"@" + contactIDFromPublicKey(&m.identity.PublicKey)}
|
||||
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
||||
if contact.added() {
|
||||
chatIDs = append(chatIDs, "@"+contact.ID)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return m.persistence.EmojiReactionsByChatIDs(chatIDs, cursor, limit)
|
||||
}
|
||||
return m.persistence.EmojiReactionsByChatID(chatID, cursor, limit)
|
||||
}
|
||||
|
||||
func (m *Messenger) EmojiReactionsByChatIDMessageID(chatID string, messageID string) ([]*EmojiReaction, error) {
|
||||
_, err := m.persistence.Chat(chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m.persistence.EmojiReactionsByChatIDMessageID(chatID, messageID)
|
||||
}
|
||||
|
||||
func (m *Messenger) SendEmojiReactionRetraction(ctx context.Context, emojiReactionID string) (*MessengerResponse, error) {
|
||||
emojiR, err := m.persistence.EmojiReactionByID(emojiReactionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check that the sender is the key owner
|
||||
pk := types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))
|
||||
if emojiR.From != pk {
|
||||
return nil, errors.Errorf("identity mismatch, "+
|
||||
"emoji reactions can only be retracted by the reaction sender, "+
|
||||
"emoji reaction sent by '%s', current identity '%s'",
|
||||
emojiR.From, pk,
|
||||
)
|
||||
}
|
||||
|
||||
// Get chat and clock
|
||||
chat, ok := m.allChats.Load(emojiR.GetChatId())
|
||||
if !ok {
|
||||
return nil, ErrChatNotFound
|
||||
}
|
||||
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
|
||||
|
||||
// Update the relevant fields
|
||||
emojiR.Clock = clock
|
||||
emojiR.Retracted = true
|
||||
|
||||
encodedMessage, err := m.encodeChatEntity(chat, emojiR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Send the marshalled EmojiReactionRetraction protobuf
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
LocalChatID: emojiR.GetChatId(),
|
||||
Payload: encodedMessage,
|
||||
SkipGroupMessageWrap: true,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_EMOJI_REACTION,
|
||||
// Don't resend using datasync, that would create quite a lot
|
||||
// of traffic if clicking too eagelry
|
||||
ResendAutomatically: false,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update MessengerResponse
|
||||
response := MessengerResponse{}
|
||||
emojiR.Retracted = true
|
||||
response.AddEmojiReaction(emojiR)
|
||||
response.AddChat(chat)
|
||||
|
||||
// Persist retraction state for emoji reaction
|
||||
err = m.persistence.SaveEmojiReaction(emojiR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
|
@ -393,7 +393,26 @@ func (m *Messenger) handleCommandMessage(state *ReceivedMessageState, message *c
|
|||
if err := message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey)); err != nil {
|
||||
return fmt.Errorf("failed to prepare content: %v", err)
|
||||
}
|
||||
chat, err := m.matchChatEntity(message)
|
||||
|
||||
// Get Application layer messageType from commandState
|
||||
// Currently this is not really used in `matchChatEntity`, but I did want to pass UNKNOWN there.
|
||||
var messageType protobuf.ApplicationMetadataMessage_Type
|
||||
switch message.CommandParameters.CommandState {
|
||||
case common.CommandStateRequestAddressForTransaction:
|
||||
messageType = protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION
|
||||
case common.CommandStateRequestAddressForTransactionAccepted:
|
||||
messageType = protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION
|
||||
case common.CommandStateRequestAddressForTransactionDeclined:
|
||||
messageType = protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION
|
||||
case common.CommandStateRequestTransaction:
|
||||
messageType = protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION
|
||||
case common.CommandStateRequestTransactionDeclined:
|
||||
messageType = protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION
|
||||
default:
|
||||
messageType = protobuf.ApplicationMetadataMessage_UNKNOWN
|
||||
}
|
||||
|
||||
chat, err := m.matchChatEntity(message, messageType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -862,7 +881,7 @@ func (m *Messenger) handlePinMessage(pinner *Contact, whisperTimestamp uint64, r
|
|||
Alias: pinner.Alias,
|
||||
}
|
||||
|
||||
chat, err := m.matchChatEntity(pinMessage)
|
||||
chat, err := m.matchChatEntity(pinMessage, protobuf.ApplicationMetadataMessage_PIN_MESSAGE)
|
||||
if err != nil {
|
||||
return err // matchChatEntity returns a descriptive error message
|
||||
}
|
||||
|
@ -2127,7 +2146,7 @@ func (m *Messenger) handleChatMessage(state *ReceivedMessageState, forceSeen boo
|
|||
}
|
||||
}
|
||||
|
||||
chat, err := m.matchChatEntity(receivedMessage)
|
||||
chat, err := m.matchChatEntity(receivedMessage, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
||||
if err != nil {
|
||||
return err // matchChatEntity returns a descriptive error message
|
||||
}
|
||||
|
@ -2684,7 +2703,7 @@ func (m *Messenger) HandleDeclineRequestTransaction(messageState *ReceivedMessag
|
|||
return m.handleCommandMessage(messageState, oldMessage)
|
||||
}
|
||||
|
||||
func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity) (*Chat, error) {
|
||||
func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity, messageType protobuf.ApplicationMetadataMessage_Type) (*Chat, error) {
|
||||
if chatEntity.GetSigPubKey() == nil {
|
||||
m.logger.Error("public key can't be empty")
|
||||
return nil, errors.New("received a chatEntity with empty public key")
|
||||
|
@ -2753,7 +2772,7 @@ func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity) (*Chat, error)
|
|||
return nil, errors.New("not an community chat")
|
||||
}
|
||||
|
||||
canPost, err := m.communitiesManager.CanPost(chatEntity.GetSigPubKey(), chat.CommunityID, chat.CommunityChatID())
|
||||
canPost, err := m.communitiesManager.CanPost(chatEntity.GetSigPubKey(), chat.CommunityID, chat.CommunityChatID(), messageType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2762,21 +2781,8 @@ func (m *Messenger) matchChatEntity(chatEntity common.ChatEntity) (*Chat, error)
|
|||
return nil, errors.New("user can't post in community")
|
||||
}
|
||||
|
||||
_, isPinMessage := chatEntity.(*common.PinMessage)
|
||||
if isPinMessage {
|
||||
community, err := m.communitiesManager.GetByIDString(chat.CommunityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hasPermission := community.IsPrivilegedMember(chatEntity.GetSigPubKey())
|
||||
pinMessageAllowed := community.AllowsAllMembersToPinMessage()
|
||||
if !hasPermission && !pinMessageAllowed {
|
||||
return nil, errors.New("user can't pin message")
|
||||
}
|
||||
}
|
||||
|
||||
return chat, nil
|
||||
|
||||
case chatEntity.GetMessageType() == protobuf.MessageType_PRIVATE_GROUP:
|
||||
// In the case of a group chatEntity, ChatID is the same for all messages belonging to a group.
|
||||
// It needs to be verified if the signature public key belongs to the chat.
|
||||
|
@ -2853,7 +2859,7 @@ func (m *Messenger) HandleEmojiReaction(state *ReceivedMessageState, pbEmojiR *p
|
|||
return nil
|
||||
}
|
||||
|
||||
chat, err := m.matchChatEntity(emojiReaction)
|
||||
chat, err := m.matchChatEntity(emojiReaction, protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
||||
if err != nil {
|
||||
return err // matchChatEntity returns a descriptive error message
|
||||
}
|
||||
|
|
|
@ -31,24 +31,6 @@ func (m *Messenger) sendPinMessage(ctx context.Context, message *common.PinMessa
|
|||
return nil, errors.New("chat not found")
|
||||
}
|
||||
|
||||
if chat.CommunityChat() {
|
||||
community, err := m.communitiesManager.GetByIDString(chat.CommunityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hasPermission := community.IsPrivilegedMember(&m.identity.PublicKey)
|
||||
pinMessageAllowed := community.AllowsAllMembersToPinMessage()
|
||||
canPost, err := community.CanPost(&m.identity.PublicKey, chat.CommunityChatID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !canPost && !pinMessageAllowed && !hasPermission {
|
||||
return nil, errors.New("can't pin message")
|
||||
}
|
||||
}
|
||||
|
||||
err := m.handleStandaloneChatIdentity(chat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,9 +23,16 @@ message CommunityMember {
|
|||
ROLE_ADMIN = 4;
|
||||
ROLE_TOKEN_MASTER = 5;
|
||||
}
|
||||
enum ChannelRole {
|
||||
// We make POSTER the first role to be the default one.
|
||||
// This is for backwards compatibility. Older protobufs won't have this field and will default to 0.
|
||||
CHANNEL_ROLE_POSTER = 0;
|
||||
CHANNEL_ROLE_VIEWER = 1;
|
||||
}
|
||||
repeated Roles roles = 1;
|
||||
repeated RevealedAccount revealed_accounts = 2 [deprecated = true];
|
||||
uint64 last_update_clock = 3;
|
||||
ChannelRole channel_role = 4;
|
||||
}
|
||||
|
||||
message CommunityTokenMetadata {
|
||||
|
@ -121,6 +128,7 @@ message CommunityChat {
|
|||
ChatIdentity identity = 3;
|
||||
string category_id = 4;
|
||||
int32 position = 5;
|
||||
bool viewers_can_post_reactions = 6;
|
||||
}
|
||||
|
||||
message CommunityCategory {
|
||||
|
|
|
@ -77,8 +77,12 @@ type Chat struct {
|
|||
FirstMessageTimestamp uint32 `json:"firstMessageTimestamp,omitempty"`
|
||||
Highlight bool `json:"highlight,omitempty"`
|
||||
PinnedMessages *PinnedMessages `json:"pinnedMessages,omitempty"`
|
||||
CanPost bool `json:"canPost"`
|
||||
Base64Image string `json:"image,omitempty"`
|
||||
// Deprecated: CanPost is deprecated in favor of CanPostMessages/CanPostReactions/etc.
|
||||
// For now CanPost will equal to CanPostMessages.
|
||||
CanPost bool `json:"canPost"`
|
||||
CanPostMessages bool `json:"canPostMessages"`
|
||||
CanPostReactions bool `json:"canPostReactions"`
|
||||
Base64Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
type ChannelGroup struct {
|
||||
|
@ -482,7 +486,12 @@ func (chat *Chat) populateCommunityFields(community *communities.Community) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
canPost, err := community.CanMemberIdentityPost(chat.ID)
|
||||
canPostMessages, err := community.CanMemberIdentityPost(chat.ID, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
canPostReactions, err := community.CanMemberIdentityPost(chat.ID, protobuf.ApplicationMetadataMessage_EMOJI_REACTION)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -493,7 +502,9 @@ func (chat *Chat) populateCommunityFields(community *communities.Community) erro
|
|||
chat.Emoji = commChat.Identity.Emoji
|
||||
chat.Name = commChat.Identity.DisplayName
|
||||
chat.Description = commChat.Identity.Description
|
||||
chat.CanPost = canPost
|
||||
chat.CanPost = canPostMessages
|
||||
chat.CanPostMessages = canPostMessages
|
||||
chat.CanPostReactions = canPostReactions
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue