Allow owner/admin to delete messages of a community (#2958)

This commit is contained in:
frank 2022-12-02 19:34:02 +08:00 committed by GitHub
parent 8865a0e4b0
commit 684e9654de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 620 additions and 144 deletions

View file

@ -1 +1 @@
0.115.4
0.115.5

View file

@ -183,48 +183,50 @@ func (o *Community) MarshalJSON() ([]byte, error) {
return nil, errors.New("member identity not set")
}
communityItem := struct {
ID types.HexBytes `json:"id"`
Admin bool `json:"admin"`
Verified bool `json:"verified"`
Joined bool `json:"joined"`
Spectated bool `json:"spectated"`
RequestedAccessAt int `json:"requestedAccessAt"`
Name string `json:"name"`
Description string `json:"description"`
IntroMessage string `json:"introMessage"`
OutroMessage string `json:"outroMessage"`
Tags []CommunityTag `json:"tags"`
Chats map[string]CommunityChat `json:"chats"`
Categories map[string]CommunityCategory `json:"categories"`
Images map[string]images.IdentityImage `json:"images"`
Permissions *protobuf.CommunityPermissions `json:"permissions"`
Members map[string]*protobuf.CommunityMember `json:"members"`
CanRequestAccess bool `json:"canRequestAccess"`
CanManageUsers bool `json:"canManageUsers"`
CanJoin bool `json:"canJoin"`
Color string `json:"color"`
RequestedToJoinAt uint64 `json:"requestedToJoinAt,omitempty"`
IsMember bool `json:"isMember"`
Muted bool `json:"muted"`
CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"`
Encrypted bool `json:"encrypted"`
BanList []string `json:"banList"`
ID types.HexBytes `json:"id"`
Admin bool `json:"admin"`
Verified bool `json:"verified"`
Joined bool `json:"joined"`
Spectated bool `json:"spectated"`
RequestedAccessAt int `json:"requestedAccessAt"`
Name string `json:"name"`
Description string `json:"description"`
IntroMessage string `json:"introMessage"`
OutroMessage string `json:"outroMessage"`
Tags []CommunityTag `json:"tags"`
Chats map[string]CommunityChat `json:"chats"`
Categories map[string]CommunityCategory `json:"categories"`
Images map[string]images.IdentityImage `json:"images"`
Permissions *protobuf.CommunityPermissions `json:"permissions"`
Members map[string]*protobuf.CommunityMember `json:"members"`
CanRequestAccess bool `json:"canRequestAccess"`
CanManageUsers bool `json:"canManageUsers"`
CanDeleteMessageForEveryone bool `json:"canDeleteMessageForEveryone"`
CanJoin bool `json:"canJoin"`
Color string `json:"color"`
RequestedToJoinAt uint64 `json:"requestedToJoinAt,omitempty"`
IsMember bool `json:"isMember"`
Muted bool `json:"muted"`
CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"`
Encrypted bool `json:"encrypted"`
BanList []string `json:"banList"`
}{
ID: o.ID(),
Admin: o.IsAdmin(),
Verified: o.config.Verified,
Chats: make(map[string]CommunityChat),
Categories: make(map[string]CommunityCategory),
Joined: o.config.Joined,
Spectated: o.config.Spectated,
CanRequestAccess: o.CanRequestAccess(o.config.MemberIdentity),
CanJoin: o.canJoin(),
CanManageUsers: o.CanManageUsers(o.config.MemberIdentity),
RequestedToJoinAt: o.RequestedToJoinAt(),
IsMember: o.isMember(),
Muted: o.config.Muted,
Tags: o.Tags(),
Encrypted: o.Encrypted(),
ID: o.ID(),
Admin: o.IsAdmin(),
Verified: o.config.Verified,
Chats: make(map[string]CommunityChat),
Categories: make(map[string]CommunityCategory),
Joined: o.config.Joined,
Spectated: o.config.Spectated,
CanRequestAccess: o.CanRequestAccess(o.config.MemberIdentity),
CanJoin: o.canJoin(),
CanManageUsers: o.CanManageUsers(o.config.MemberIdentity),
CanDeleteMessageForEveryone: o.CanDeleteMessageForEveryone(o.config.MemberIdentity),
RequestedToJoinAt: o.RequestedToJoinAt(),
IsMember: o.isMember(),
Muted: o.config.Muted,
Tags: o.Tags(),
Encrypted: o.Encrypted(),
}
if o.config.CommunityDescription != nil {
for id, c := range o.config.CommunityDescription.Categories {
@ -801,6 +803,64 @@ func (o *Community) BanUserFromCommunity(pk *ecdsa.PublicKey) (*protobuf.Communi
return o.config.CommunityDescription, nil
}
func (o *Community) AddRoleToMember(pk *ecdsa.PublicKey, role protobuf.CommunityMember_Roles) (*protobuf.CommunityDescription, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
updated := false
member := o.getMember(pk)
if member != nil {
roles := make(map[protobuf.CommunityMember_Roles]bool)
roles[role] = true
if !o.hasMemberPermission(member, roles) {
member.Roles = append(member.Roles, role)
o.config.CommunityDescription.Members[common.PubkeyToHex(pk)] = member
updated = true
}
}
if updated {
o.increaseClock()
}
return o.config.CommunityDescription, nil
}
func (o *Community) RemoveRoleFromMember(pk *ecdsa.PublicKey, role protobuf.CommunityMember_Roles) (*protobuf.CommunityDescription, error) {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.config.PrivateKey == nil {
return nil, ErrNotAdmin
}
updated := false
member := o.getMember(pk)
if member != nil {
roles := make(map[protobuf.CommunityMember_Roles]bool)
roles[role] = true
if o.hasMemberPermission(member, roles) {
var newRoles []protobuf.CommunityMember_Roles
for _, r := range member.Roles {
if r != role {
newRoles = append(newRoles, r)
}
}
member.Roles = newRoles
o.config.CommunityDescription.Members[common.PubkeyToHex(pk)] = member
updated = true
}
}
if updated {
o.increaseClock()
}
return o.config.CommunityDescription, nil
}
func (o *Community) Edit(description *protobuf.CommunityDescription) {
o.config.CommunityDescription.Identity.DisplayName = description.Identity.DisplayName
o.config.CommunityDescription.Identity.Description = description.Identity.Description
@ -1104,6 +1164,12 @@ func adminRolePermissions() map[protobuf.CommunityMember_Roles]bool {
return roles
}
func canDeleteMessageForEveryonePermissions() map[protobuf.CommunityMember_Roles]bool {
roles := adminRolePermissions()
roles[protobuf.CommunityMember_ROLE_MODERATE_CONTENT] = true
return roles
}
func (o *Community) validateRequestToJoinWithChatID(request *protobuf.CommunityRequestToJoin) error {
chat, ok := o.config.CommunityDescription.Chats[request.ChatId]
@ -1490,6 +1556,22 @@ func (o *Community) CanManageUsers(pk *ecdsa.PublicKey) bool {
return o.hasPermission(pk, roles)
}
func (o *Community) CanDeleteMessageForEveryone(pk *ecdsa.PublicKey) bool {
o.mutex.Lock()
defer o.mutex.Unlock()
if o.IsAdmin() {
return true
}
if !o.hasMember(pk) {
return false
}
roles := canDeleteMessageForEveryonePermissions()
return o.hasPermission(pk, roles)
}
func (o *Community) isMember() bool {
return o.hasMember(o.config.MemberIdentity)
}

View file

@ -28,3 +28,4 @@ var ErrNotAuthorized = errors.New("not authorized")
var ErrAlreadyMember = errors.New("already a member")
var ErrAlreadyJoined = errors.New("already joined")
var ErrInvalidMessage = errors.New("invalid community description message")
var ErrMemberNotFound = errors.New("member not found")

View file

@ -1278,6 +1278,74 @@ func (m *Manager) UnbanUserFromCommunity(request *requests.UnbanUserFromCommunit
return community, nil
}
func (m *Manager) AddRoleToMember(request *requests.AddRoleToMember) (*Community, error) {
id := request.CommunityID
publicKey, err := common.HexToPubkey(request.User.String())
if err != nil {
return nil, err
}
community, err := m.GetByID(id)
if err != nil {
return nil, err
}
if community == nil {
return nil, ErrOrgNotFound
}
if !community.hasMember(publicKey) {
return nil, ErrMemberNotFound
}
_, err = community.AddRoleToMember(publicKey, request.Role)
if err != nil {
return nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, err
}
m.publish(&Subscription{Community: community})
return community, nil
}
func (m *Manager) RemoveRoleFromMember(request *requests.RemoveRoleFromMember) (*Community, error) {
id := request.CommunityID
publicKey, err := common.HexToPubkey(request.User.String())
if err != nil {
return nil, err
}
community, err := m.GetByID(id)
if err != nil {
return nil, err
}
if community == nil {
return nil, ErrOrgNotFound
}
if !community.hasMember(publicKey) {
return nil, ErrMemberNotFound
}
_, err = community.RemoveRoleFromMember(publicKey, request.Role)
if err != nil {
return nil, err
}
err = m.persistence.SaveCommunity(community)
if err != nil {
return nil, err
}
m.publish(&Subscription{Community: community})
return community, nil
}
func (m *Manager) BanUserFromCommunity(request *requests.BanUserFromCommunity) (*Community, error) {
id := request.CommunityID

View file

@ -592,7 +592,7 @@ func (db sqlitePersistence) MessageByChatID(chatID string, currCursor string, li
// This new column values can also be returned as a cursor for subsequent requests.
where := fmt.Sprintf(`
WHERE
NOT(m1.hide) AND m1.local_chat_id = ? %s
NOT(m1.hide) AND m1.local_chat_id = ? %s AND NOT(m1.deleted)
ORDER BY cursor DESC
LIMIT ?`, cursorWhere)

View file

@ -1331,6 +1331,34 @@ func (m *Messenger) BanUserFromCommunity(request *requests.BanUserFromCommunity)
return response, nil
}
func (m *Messenger) AddRoleToMember(request *requests.AddRoleToMember) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
community, err := m.communitiesManager.AddRoleToMember(request)
if err != nil {
return nil, err
}
response := &MessengerResponse{}
response.AddCommunity(community)
return response, nil
}
func (m *Messenger) RemoveRoleFromMember(request *requests.RemoveRoleFromMember) (*MessengerResponse, error) {
if err := request.Validate(); err != nil {
return nil, err
}
community, err := m.communitiesManager.RemoveRoleFromMember(request)
if err != nil {
return nil, err
}
response := &MessengerResponse{}
response.AddCommunity(community)
return response, nil
}
func (m *Messenger) findCommunityInfoFromDB(communityID string) (*communities.Community, error) {
id, err := hexutil.Decode(communityID)
if err != nil {

View file

@ -0,0 +1,177 @@
package protocol
import (
"context"
"testing"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
"github.com/ethereum/go-ethereum/crypto"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/waku"
)
func TestMessengerDeleteMessageForEveryoneSuite(t *testing.T) {
suite.Run(t, new(MessengerDeleteMessageForEveryoneSuite))
}
type MessengerDeleteMessageForEveryoneSuite struct {
suite.Suite
admin *Messenger
moderator *Messenger
bob *Messenger
shh types.Waku
logger *zap.Logger
}
func (s *MessengerDeleteMessageForEveryoneSuite) SetupTest() {
s.logger = tt.MustCreateTestLogger()
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
s.admin = s.newMessenger()
s.bob = s.newMessenger()
s.moderator = s.newMessenger()
_, err := s.admin.Start()
s.Require().NoError(err)
_, err = s.bob.Start()
s.Require().NoError(err)
_, err = s.moderator.Start()
s.Require().NoError(err)
}
func (s *MessengerDeleteMessageForEveryoneSuite) TearDownTest() {
s.Require().NoError(s.admin.Shutdown())
s.Require().NoError(s.bob.Shutdown())
_ = s.logger.Sync()
}
func (s *MessengerDeleteMessageForEveryoneSuite) newMessenger() *Messenger {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
messenger, err := newMessengerWithKey(s.shh, privateKey, s.logger, nil)
s.Require().NoError(err)
return messenger
}
func (s *MessengerDeleteMessageForEveryoneSuite) TestDeleteMessageForEveryone() {
community := s.createCommunity()
communityChat := s.createCommunityChat(community)
s.inviteAndJoin(community, s.moderator)
s.inviteAndJoin(community, s.bob)
response, err := s.admin.AddRoleToMember(&requests.AddRoleToMember{
CommunityID: community.ID(),
User: common.PubkeyToHexBytes(s.moderator.IdentityPublicKey()),
Role: protobuf.CommunityMember_ROLE_MODERATE_CONTENT,
})
s.Require().NoError(err)
s.Require().Len(response.Communities(), 1)
community = response.Communities()[0]
ctx := context.Background()
inputMessage := &common.Message{}
inputMessage.ChatId = communityChat.ID
inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
inputMessage.Text = "some text"
_, err = s.bob.SendChatMessage(ctx, inputMessage)
s.Require().NoError(err)
response, err = WaitOnMessengerResponse(s.moderator, func(response *MessengerResponse) bool {
return len(response.Messages()) > 0
}, "messages not received")
s.Require().NoError(err)
message := response.Messages()[0]
s.Require().Equal(inputMessage.Text, message.Text)
_, err = s.moderator.DeleteMessageAndSend(ctx, message.ID)
s.Require().NoError(err)
_, err = WaitOnMessengerResponse(s.bob, func(response *MessengerResponse) bool {
return len(response.RemovedMessages()) > 0
}, "removed messages not received")
s.Require().NoError(err)
message, err = s.bob.MessageByID(message.ID)
s.Require().NoError(err)
s.Require().True(message.Deleted)
}
func (s *MessengerDeleteMessageForEveryoneSuite) createCommunity() *communities.Community {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
response, err := s.admin.CreateCommunity(description, false)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Chats(), 0)
return response.Communities()[0]
}
func (s *MessengerDeleteMessageForEveryoneSuite) createCommunityChat(community *communities.Community) *Chat {
orgChat := &protobuf.CommunityChat{
Permissions: &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
},
Identity: &protobuf.ChatIdentity{
DisplayName: "status-core",
Emoji: "",
Description: "status-core community chatToModerator",
},
}
response, err := s.admin.CreateCommunityChat(community.ID(), orgChat)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Chats(), 1)
return response.Chats()[0]
}
func (s *MessengerDeleteMessageForEveryoneSuite) inviteAndJoin(community *communities.Community, target *Messenger) {
response, err := s.admin.InviteUsersToCommunity(&requests.InviteUsersToCommunity{
CommunityID: community.ID(),
Users: []types.HexBytes{common.PubkeyToHexBytes(&target.identity.PublicKey)},
})
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
community = response.Communities()[0]
s.Require().True(community.HasMember(&target.identity.PublicKey))
_, err = WaitOnMessengerResponse(target, func(response *MessengerResponse) bool {
return len(response.Communities()) > 0
}, "community not received")
s.Require().NoError(err)
response, err = target.JoinCommunity(context.Background(), community.ID())
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().True(response.Communities()[0].Joined())
s.Require().Len(response.Chats(), 1)
s.Require().NoError(target.SaveChat(response.Chats()[0]))
response, err = WaitOnMessengerResponse(target, func(response *MessengerResponse) bool {
return len(response.Messages()) > 0
}, "message 'You have been invited to community' not received")
s.Require().NoError(err)
}

View file

@ -103,7 +103,7 @@ func (s *MessengerDeleteMessageSuite) TestDeleteMessage() {
// Main instance user attempts to delete the message it received from theirMessenger
_, err = s.m.DeleteMessageAndSend(context.Background(), ogMessage.ID)
s.Require().Equal(ErrInvalidEditOrDeleteAuthor, err)
s.Require().ErrorContains(err, "Chat not found")
}
func (s *MessengerDeleteMessageSuite) TestDeleteMessagePreviousLastMessage() {

View file

@ -1257,9 +1257,22 @@ func (m *Messenger) HandleDeleteMessage(state *ReceivedMessageState, deleteMessa
return errors.New("chat not found")
}
// Check edit is valid
var canDeleteMessageForEveryone = false
if originalMessage.From != deleteMessage.From {
return errors.New("invalid delete, not the right author")
if chat.ChatType == ChatTypeCommunityChat {
fromPublicKey, err := common.HexToPubkey(deleteMessage.From)
if err != nil {
return err
}
canDeleteMessageForEveryone = m.CanDeleteMessageForEveryone(chat.CommunityID, fromPublicKey)
if !canDeleteMessageForEveryone {
return ErrInvalidDeletePermission
}
}
// Check edit is valid
if !canDeleteMessageForEveryone {
return errors.New("invalid delete, not the right author")
}
}
// Update message and return it

View file

@ -2,8 +2,11 @@ package protocol
import (
"context"
"crypto/ecdsa"
"errors"
"go.uber.org/zap"
"github.com/golang/protobuf/proto"
"github.com/status-im/status-go/protocol/common"
@ -14,6 +17,7 @@ import (
var ErrInvalidEditOrDeleteAuthor = errors.New("sender is not the author of the message")
var ErrInvalidDeleteTypeAuthor = errors.New("message type cannot be deleted")
var ErrInvalidEditContentType = errors.New("only text or emoji messages can be replaced")
var ErrInvalidDeletePermission = errors.New("don't have enough permission to delete")
func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessage) (*MessengerResponse, error) {
err := request.Validate()
@ -86,22 +90,44 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
return response, nil
}
func (m *Messenger) CanDeleteMessageForEveryone(communityID string, publicKey *ecdsa.PublicKey) bool {
if communityID != "" {
community, err := m.communitiesManager.GetByIDString(communityID)
if err != nil {
m.logger.Error("failed to find community", zap.String("communityID", communityID), zap.Error(err))
return false
}
return community.CanDeleteMessageForEveryone(publicKey)
}
return false
}
func (m *Messenger) DeleteMessageAndSend(ctx context.Context, messageID string) (*MessengerResponse, error) {
message, err := m.persistence.MessageByID(messageID)
if err != nil {
return nil, err
}
if message.From != common.PubkeyToHex(&m.identity.PublicKey) {
return nil, ErrInvalidEditOrDeleteAuthor
}
// A valid added chat is required.
chat, ok := m.allChats.Load(message.ChatId)
if !ok {
return nil, errors.New("Chat not found")
}
var canDeleteMessageForEveryone = false
if message.From != common.PubkeyToHex(&m.identity.PublicKey) {
if message.MessageType == protobuf.MessageType_COMMUNITY_CHAT {
communityID := chat.CommunityID
canDeleteMessageForEveryone = m.CanDeleteMessageForEveryone(communityID, &m.identity.PublicKey)
if !canDeleteMessageForEveryone {
return nil, ErrInvalidDeletePermission
}
}
if !canDeleteMessageForEveryone {
return nil, ErrInvalidEditOrDeleteAuthor
}
}
// Only certain types of messages can be deleted
if message.ContentType != protobuf.ChatMessage_TEXT_PLAIN &&
message.ContentType != protobuf.ChatMessage_STICKER &&

View file

@ -23,21 +23,24 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type CommunityMember_Roles int32
const (
CommunityMember_UNKNOWN_ROLE CommunityMember_Roles = 0
CommunityMember_ROLE_ALL CommunityMember_Roles = 1
CommunityMember_ROLE_MANAGE_USERS CommunityMember_Roles = 2
CommunityMember_UNKNOWN_ROLE CommunityMember_Roles = 0
CommunityMember_ROLE_ALL CommunityMember_Roles = 1
CommunityMember_ROLE_MANAGE_USERS CommunityMember_Roles = 2
CommunityMember_ROLE_MODERATE_CONTENT CommunityMember_Roles = 3
)
var CommunityMember_Roles_name = map[int32]string{
0: "UNKNOWN_ROLE",
1: "ROLE_ALL",
2: "ROLE_MANAGE_USERS",
3: "ROLE_MODERATE_CONTENT",
}
var CommunityMember_Roles_value = map[string]int32{
"UNKNOWN_ROLE": 0,
"ROLE_ALL": 1,
"ROLE_MANAGE_USERS": 2,
"UNKNOWN_ROLE": 0,
"ROLE_ALL": 1,
"ROLE_MANAGE_USERS": 2,
"ROLE_MODERATE_CONTENT": 3,
}
func (x CommunityMember_Roles) String() string {
@ -1255,92 +1258,93 @@ func init() {
}
var fileDescriptor_f937943d74c1cd8b = []byte{
// 1382 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0x4b, 0x6f, 0x1b, 0xb7,
0x16, 0xce, 0xe8, 0x61, 0x49, 0x47, 0x92, 0x23, 0x33, 0x89, 0x3d, 0x71, 0x1e, 0x56, 0xe6, 0xde,
0x0b, 0x38, 0xb8, 0xb8, 0x0a, 0xe2, 0xe0, 0x02, 0x41, 0x1f, 0x49, 0x14, 0x47, 0x48, 0xd5, 0xd8,
0x72, 0x42, 0xd9, 0x4d, 0x9b, 0x45, 0x07, 0xd4, 0x0c, 0x2d, 0x13, 0x1e, 0x71, 0xd4, 0x21, 0x25,
0x54, 0x5d, 0x74, 0xdd, 0x9f, 0x50, 0x74, 0xdb, 0x4d, 0x57, 0xfd, 0x0b, 0x5d, 0x74, 0xd5, 0x4d,
0xf7, 0xdd, 0xf5, 0xa7, 0x14, 0xe4, 0x3c, 0x34, 0x7a, 0x39, 0x01, 0x82, 0x02, 0x5d, 0x89, 0xe7,
0xf0, 0x9c, 0xef, 0xbc, 0x79, 0x46, 0xb0, 0xe1, 0xf8, 0x83, 0xc1, 0x88, 0x33, 0xc9, 0xa8, 0x68,
0x0c, 0x03, 0x5f, 0xfa, 0xa8, 0xa8, 0x7f, 0x7a, 0xa3, 0xd3, 0xed, 0x2b, 0xce, 0x19, 0x91, 0x36,
0x73, 0x29, 0x97, 0x4c, 0x4e, 0xc2, 0x6b, 0x6b, 0x0c, 0xf9, 0xe7, 0x01, 0xe1, 0x12, 0xdd, 0x81,
0x4a, 0xac, 0x3c, 0xb1, 0x99, 0x6b, 0x1a, 0x75, 0x63, 0xb7, 0x82, 0xcb, 0x09, 0xaf, 0xed, 0xa2,
0x1b, 0x50, 0x1a, 0xd0, 0x41, 0x8f, 0x06, 0xea, 0x3e, 0xa3, 0xef, 0x8b, 0x21, 0xa3, 0xed, 0xa2,
0x2d, 0x28, 0x44, 0xf8, 0x66, 0xb6, 0x6e, 0xec, 0x96, 0xf0, 0x9a, 0x22, 0xdb, 0x2e, 0xba, 0x0a,
0x79, 0xc7, 0xf3, 0x9d, 0x73, 0x33, 0x57, 0x37, 0x76, 0x73, 0x38, 0x24, 0xac, 0xef, 0x0c, 0xb8,
0xbc, 0x1f, 0x63, 0x1f, 0x6a, 0x10, 0xf4, 0x7f, 0xc8, 0x07, 0xbe, 0x47, 0x85, 0x69, 0xd4, 0xb3,
0xbb, 0xeb, 0x7b, 0x3b, 0x8d, 0xd8, 0xf5, 0xc6, 0x9c, 0x64, 0x03, 0x2b, 0x31, 0x1c, 0x4a, 0x5b,
0x8f, 0x20, 0xaf, 0x69, 0x54, 0x83, 0xca, 0x49, 0xe7, 0x45, 0xe7, 0xe8, 0x75, 0xc7, 0xc6, 0x47,
0x07, 0xad, 0xda, 0x25, 0x54, 0x81, 0xa2, 0x3a, 0xd9, 0xcd, 0x83, 0x83, 0x9a, 0x81, 0xae, 0xc1,
0x86, 0xa6, 0x0e, 0x9b, 0x9d, 0xe6, 0xf3, 0x96, 0x7d, 0xd2, 0x6d, 0xe1, 0x6e, 0x2d, 0x63, 0xfd,
0x69, 0xc0, 0xd5, 0xc4, 0xc0, 0x4b, 0x1a, 0x0c, 0x98, 0x10, 0xcc, 0xe7, 0x02, 0x5d, 0x87, 0x22,
0xe5, 0xc2, 0xf6, 0xb9, 0x37, 0xd1, 0xe9, 0x28, 0xe2, 0x02, 0xe5, 0xe2, 0x88, 0x7b, 0x13, 0x64,
0x42, 0x61, 0x18, 0xb0, 0x31, 0x91, 0x54, 0x27, 0xa2, 0x88, 0x63, 0x12, 0x7d, 0x0c, 0x6b, 0xc4,
0x71, 0xa8, 0x10, 0x3a, 0x0d, 0xeb, 0x7b, 0xff, 0x59, 0x12, 0x45, 0xca, 0x48, 0xa3, 0xa9, 0x85,
0x71, 0xa4, 0x64, 0x1d, 0xc3, 0x5a, 0xc8, 0x41, 0x08, 0xd6, 0xe3, 0x68, 0x9a, 0xfb, 0xfb, 0xad,
0x6e, 0xb7, 0x76, 0x09, 0x6d, 0x40, 0xb5, 0x73, 0x64, 0x1f, 0xb6, 0x0e, 0x9f, 0xb6, 0x70, 0xf7,
0x93, 0xf6, 0xcb, 0x9a, 0x81, 0xae, 0xc0, 0xe5, 0x76, 0xe7, 0xb3, 0xf6, 0x71, 0xf3, 0xb8, 0x7d,
0xd4, 0xb1, 0x8f, 0x3a, 0x07, 0x5f, 0xd4, 0x32, 0x68, 0x1d, 0xe0, 0xa8, 0x63, 0xe3, 0xd6, 0xab,
0x93, 0x56, 0xf7, 0xb8, 0x96, 0xb5, 0x7e, 0x28, 0xa4, 0x42, 0x7c, 0x46, 0x85, 0x13, 0xb0, 0xa1,
0x64, 0x3e, 0x9f, 0x16, 0xc7, 0x48, 0x15, 0x07, 0xb5, 0xa0, 0x10, 0xd6, 0x55, 0x98, 0x99, 0x7a,
0x76, 0xb7, 0xbc, 0xf7, 0xdf, 0x25, 0x41, 0xa4, 0x60, 0x1a, 0x61, 0x59, 0x44, 0x8b, 0xcb, 0x60,
0x82, 0x63, 0x5d, 0xf4, 0x04, 0xca, 0xc3, 0x69, 0xa4, 0x3a, 0x1f, 0xe5, 0xbd, 0xdb, 0x17, 0xe7,
0x03, 0xa7, 0x55, 0xd0, 0x1e, 0x14, 0xe3, 0x7e, 0x35, 0xf3, 0x5a, 0x7d, 0x33, 0xa5, 0xae, 0xfb,
0x2b, 0xbc, 0xc5, 0x89, 0x1c, 0x7a, 0x0c, 0x79, 0xd5, 0x79, 0xc2, 0x5c, 0xd3, 0xae, 0xdf, 0x7d,
0x8b, 0xeb, 0x0a, 0x25, 0x72, 0x3c, 0xd4, 0x53, 0x65, 0xef, 0x11, 0x6e, 0x7b, 0x4c, 0x48, 0xb3,
0x50, 0xcf, 0xee, 0x96, 0x70, 0xa1, 0x47, 0xf8, 0x01, 0x13, 0x12, 0x75, 0x00, 0x1c, 0x22, 0x69,
0xdf, 0x0f, 0x18, 0x15, 0x66, 0x51, 0x1b, 0x68, 0xbc, 0xcd, 0x40, 0xa2, 0x10, 0x5a, 0x49, 0x21,
0xa0, 0x87, 0x60, 0x92, 0xc0, 0x39, 0x63, 0x63, 0x6a, 0x0f, 0x48, 0x9f, 0x53, 0xe9, 0x31, 0x7e,
0x6e, 0x87, 0x15, 0x29, 0xe9, 0x8a, 0x6c, 0x46, 0xf7, 0x87, 0xc9, 0xf5, 0xbe, 0x2e, 0xd1, 0x73,
0x58, 0x27, 0xee, 0x80, 0x71, 0x5b, 0x50, 0x29, 0x19, 0xef, 0x0b, 0x13, 0x74, 0x7e, 0xea, 0x4b,
0xbc, 0x69, 0x2a, 0xc1, 0x6e, 0x24, 0x87, 0xab, 0x24, 0x4d, 0xa2, 0x7f, 0x41, 0x95, 0x71, 0x19,
0xf8, 0xf6, 0x80, 0x0a, 0x41, 0xfa, 0xd4, 0x2c, 0xeb, 0xe9, 0xad, 0x68, 0xe6, 0x61, 0xc8, 0x53,
0x42, 0xfe, 0x28, 0x2d, 0x54, 0x09, 0x85, 0x34, 0x33, 0x16, 0xba, 0x09, 0x25, 0xca, 0x9d, 0x60,
0x32, 0x94, 0xd4, 0x35, 0xab, 0x7a, 0x2a, 0xa6, 0x0c, 0x84, 0x20, 0x27, 0x49, 0x5f, 0x98, 0xeb,
0x3a, 0xa3, 0xfa, 0xbc, 0x7d, 0x02, 0x95, 0x74, 0xe7, 0xa0, 0x1a, 0x64, 0xcf, 0x69, 0x38, 0x6b,
0x25, 0xac, 0x8e, 0xe8, 0x1e, 0xe4, 0xc7, 0xc4, 0x1b, 0x85, 0x53, 0x56, 0xde, 0xbb, 0xbe, 0xf2,
0x49, 0xc0, 0xa1, 0xdc, 0x07, 0x99, 0x87, 0xc6, 0xf6, 0x2b, 0x80, 0x69, 0x55, 0x97, 0x80, 0xfe,
0x6f, 0x16, 0x74, 0x6b, 0x09, 0xa8, 0xd2, 0x4f, 0x43, 0xbe, 0x81, 0xcb, 0x73, 0x75, 0x5c, 0x82,
0x7b, 0x7f, 0x16, 0xf7, 0xc6, 0x32, 0xdc, 0x10, 0x64, 0x92, 0xc2, 0xb6, 0xbe, 0x84, 0xcd, 0xe5,
0xa5, 0x42, 0xcf, 0x60, 0x67, 0xc8, 0x78, 0x9c, 0x74, 0x9b, 0x78, 0x9e, 0x1d, 0xcd, 0x96, 0x4d,
0x39, 0xe9, 0x79, 0xd4, 0x8d, 0xde, 0xa5, 0x1b, 0x43, 0xc6, 0xa3, 0x32, 0x34, 0x3d, 0x2f, 0xc9,
0xa9, 0x16, 0xb1, 0xfe, 0xc8, 0x40, 0x75, 0x26, 0x30, 0xf4, 0x68, 0x3a, 0xdf, 0x86, 0xee, 0xe1,
0x7f, 0xaf, 0x48, 0xc1, 0xbb, 0x0d, 0x76, 0xe6, 0xfd, 0x06, 0x3b, 0xfb, 0x8e, 0x83, 0xbd, 0x03,
0xe5, 0x68, 0x74, 0xf4, 0x82, 0xca, 0xe9, 0xc4, 0xc7, 0xd3, 0xa4, 0xf6, 0xd3, 0x36, 0x14, 0x87,
0xbe, 0x60, 0x6a, 0xea, 0xf4, 0x6b, 0x91, 0xc7, 0x09, 0xfd, 0x37, 0xb5, 0x9a, 0xe5, 0xc2, 0xc6,
0x42, 0x6d, 0xe7, 0x1d, 0x35, 0x16, 0x1c, 0x45, 0x90, 0xe3, 0x64, 0x10, 0x5a, 0x2a, 0x61, 0x7d,
0x9e, 0x71, 0x3e, 0x3b, 0xeb, 0xbc, 0xf5, 0xbd, 0x01, 0x57, 0x12, 0x33, 0x6d, 0x3e, 0x66, 0x92,
0xe8, 0xd7, 0xfb, 0x01, 0x5c, 0x9b, 0xee, 0x6c, 0x77, 0xfa, 0xe6, 0x44, 0xcb, 0xfb, 0xaa, 0xb3,
0xe2, 0xc9, 0xef, 0xab, 0x8d, 0x1f, 0x6d, 0xf0, 0x90, 0x58, 0xbd, 0xbe, 0x6f, 0x01, 0x0c, 0x47,
0x3d, 0x8f, 0x39, 0xb6, 0xca, 0x57, 0x4e, 0xeb, 0x94, 0x42, 0xce, 0x0b, 0x3a, 0xb1, 0x7e, 0x32,
0x52, 0xdd, 0x8b, 0xe9, 0x57, 0x23, 0x2a, 0xe4, 0xb1, 0xff, 0xa9, 0xcf, 0x56, 0xed, 0x96, 0x68,
0xa9, 0xa6, 0xe2, 0x57, 0x4b, 0xb5, 0xa3, 0x52, 0xb0, 0xd2, 0x87, 0xf9, 0x6f, 0x93, 0xdc, 0xe2,
0xb7, 0xc9, 0x1d, 0xa8, 0xb8, 0x4c, 0x0c, 0x3d, 0x32, 0x09, 0xa1, 0xf3, 0x1a, 0xa0, 0x1c, 0xf1,
0x14, 0xbc, 0xf5, 0xb3, 0x01, 0x37, 0x53, 0xc5, 0xe2, 0x0e, 0xf5, 0xfe, 0xd9, 0x0e, 0xff, 0x66,
0xc0, 0xed, 0xe5, 0xb9, 0xc5, 0x54, 0x0c, 0x7d, 0x2e, 0xe8, 0x0a, 0x97, 0x3f, 0x82, 0x52, 0x62,
0xea, 0x82, 0xe9, 0x4c, 0x75, 0x05, 0x9e, 0x2a, 0xa8, 0x4e, 0x54, 0x1f, 0x23, 0xfa, 0x19, 0xcf,
0xea, 0xe7, 0x25, 0xa1, 0xa7, 0xcd, 0x93, 0x4b, 0x37, 0xcf, 0x7c, 0xb8, 0xf9, 0x85, 0x70, 0x2d,
0x0c, 0x5b, 0x8b, 0xa1, 0x1c, 0x50, 0x32, 0x5e, 0x15, 0xc3, 0x3c, 0x66, 0x66, 0x11, 0xf3, 0x73,
0xb8, 0x93, 0x1a, 0xcd, 0xf0, 0xf5, 0x9b, 0xdf, 0x96, 0x2b, 0xd0, 0x6f, 0x01, 0x84, 0x0b, 0xd7,
0x1e, 0x05, 0x2c, 0x2a, 0x6b, 0x29, 0xe4, 0x9c, 0x04, 0xcc, 0xfa, 0xc5, 0x80, 0xf2, 0x6b, 0x72,
0x3e, 0x8a, 0x57, 0x5b, 0x0d, 0xb2, 0x82, 0xf5, 0xa3, 0xb1, 0x52, 0x47, 0xb5, 0xec, 0x24, 0x1b,
0x50, 0x21, 0xc9, 0x60, 0xa8, 0xf5, 0x73, 0x78, 0xca, 0x50, 0x46, 0xa5, 0x3f, 0x64, 0x8e, 0xce,
0x5f, 0x05, 0x87, 0x84, 0xfe, 0x68, 0x24, 0x13, 0xcf, 0x27, 0x71, 0x43, 0xc4, 0x64, 0x78, 0xe3,
0xba, 0x8c, 0xf7, 0xa3, 0xdc, 0xc5, 0xa4, 0x7a, 0x2a, 0xce, 0x88, 0x38, 0x33, 0xd7, 0x34, 0x5b,
0x9f, 0x91, 0x05, 0x15, 0x79, 0xc6, 0x02, 0xf7, 0x25, 0x09, 0x54, 0x1e, 0xcc, 0x42, 0xb8, 0x8c,
0xd3, 0x3c, 0xeb, 0x5b, 0xd8, 0x4e, 0x05, 0x10, 0xa7, 0x85, 0x4a, 0xe2, 0x12, 0x49, 0x94, 0xbd,
0x31, 0x0d, 0x44, 0xfc, 0x54, 0x54, 0x71, 0x4c, 0x2a, 0x7b, 0xa7, 0x81, 0x3f, 0x88, 0x42, 0xd2,
0x67, 0xb4, 0x0e, 0x19, 0xe9, 0xeb, 0x50, 0x72, 0x38, 0x23, 0x7d, 0x65, 0xdf, 0xf1, 0xb9, 0xa4,
0x5c, 0x1e, 0xeb, 0x20, 0x73, 0xf5, 0xec, 0x6e, 0x05, 0xcf, 0xf0, 0xac, 0x1f, 0x0d, 0x40, 0x8b,
0x0e, 0x5c, 0x60, 0xf8, 0x09, 0x14, 0x07, 0x91, 0x7b, 0x51, 0xcb, 0xa6, 0x96, 0xd2, 0xea, 0x50,
0x70, 0xa2, 0x85, 0xee, 0x2b, 0x04, 0x2d, 0xa3, 0xbe, 0x35, 0xd5, 0x5a, 0xbb, 0xb6, 0x14, 0x01,
0x27, 0x62, 0xd6, 0xaf, 0x06, 0xec, 0x2c, 0x62, 0xb7, 0xb9, 0x4b, 0xbf, 0x7e, 0x87, 0x5c, 0xbd,
0xbf, 0xcb, 0x9b, 0xb0, 0xe6, 0x9f, 0x9e, 0x0a, 0x2a, 0xa3, 0xec, 0x46, 0x94, 0xaa, 0x82, 0x60,
0xdf, 0xd0, 0xe8, 0x2f, 0x93, 0x3e, 0xcf, 0xf7, 0x48, 0x2e, 0xe9, 0x11, 0xeb, 0x77, 0x03, 0xb6,
0x56, 0x44, 0x81, 0x5e, 0x40, 0x31, 0xfa, 0x82, 0x8c, 0x77, 0xfd, 0xbd, 0x8b, 0x7c, 0xd4, 0x4a,
0x8d, 0x88, 0x88, 0xd6, 0x7e, 0x02, 0xb0, 0x7d, 0x0a, 0xd5, 0x99, 0xab, 0x25, 0x5b, 0xf4, 0xf1,
0xec, 0x16, 0xbd, 0xfb, 0x56, 0x63, 0x49, 0x56, 0xa6, 0x5b, 0xf5, 0x69, 0xf5, 0x4d, 0xb9, 0x71,
0xef, 0xc3, 0x58, 0xb3, 0xb7, 0xa6, 0x4f, 0x0f, 0xfe, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x35, 0x39,
0x1e, 0x9e, 0xde, 0x0e, 0x00, 0x00,
// 1396 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcd, 0x6f, 0x1b, 0x45,
0x14, 0xef, 0xfa, 0x23, 0xb6, 0x9f, 0xed, 0xd4, 0x99, 0x36, 0xc9, 0x36, 0xfd, 0x88, 0xbb, 0x80,
0x94, 0x0a, 0xe1, 0xaa, 0xa9, 0x90, 0x2a, 0xbe, 0x5a, 0x37, 0xb5, 0x8a, 0x69, 0x62, 0xb7, 0x13,
0x87, 0xd2, 0x1e, 0x58, 0x4d, 0x76, 0x27, 0xce, 0x28, 0xeb, 0x59, 0xb3, 0x33, 0x8e, 0x30, 0x07,
0xfe, 0x0e, 0xc4, 0x95, 0x0b, 0x27, 0xfe, 0x05, 0x0e, 0x9c, 0xb8, 0x70, 0xe7, 0xc6, 0x9f, 0x82,
0x66, 0xf6, 0xc3, 0xeb, 0xaf, 0xb4, 0x52, 0x85, 0xc4, 0xc9, 0xf3, 0xde, 0xbe, 0xf7, 0x7b, 0xdf,
0xf3, 0xc6, 0xb0, 0xe6, 0xf8, 0x83, 0xc1, 0x88, 0x33, 0xc9, 0xa8, 0x68, 0x0c, 0x03, 0x5f, 0xfa,
0xa8, 0xa8, 0x7f, 0x8e, 0x47, 0x27, 0x5b, 0x57, 0x9c, 0x53, 0x22, 0x6d, 0xe6, 0x52, 0x2e, 0x99,
0x1c, 0x87, 0x9f, 0xad, 0x73, 0xc8, 0x3f, 0x0d, 0x08, 0x97, 0xe8, 0x36, 0x54, 0x62, 0xe5, 0xb1,
0xcd, 0x5c, 0xd3, 0xa8, 0x1b, 0x3b, 0x15, 0x5c, 0x4e, 0x78, 0x6d, 0x17, 0x5d, 0x87, 0xd2, 0x80,
0x0e, 0x8e, 0x69, 0xa0, 0xbe, 0x67, 0xf4, 0xf7, 0x62, 0xc8, 0x68, 0xbb, 0x68, 0x13, 0x0a, 0x11,
0xbe, 0x99, 0xad, 0x1b, 0x3b, 0x25, 0xbc, 0xa2, 0xc8, 0xb6, 0x8b, 0xae, 0x42, 0xde, 0xf1, 0x7c,
0xe7, 0xcc, 0xcc, 0xd5, 0x8d, 0x9d, 0x1c, 0x0e, 0x09, 0xeb, 0x17, 0x03, 0x2e, 0xef, 0xc5, 0xd8,
0x07, 0x1a, 0x04, 0x7d, 0x0c, 0xf9, 0xc0, 0xf7, 0xa8, 0x30, 0x8d, 0x7a, 0x76, 0x67, 0x75, 0x77,
0xbb, 0x11, 0xbb, 0xde, 0x98, 0x91, 0x6c, 0x60, 0x25, 0x86, 0x43, 0x69, 0xeb, 0x15, 0xe4, 0x35,
0x8d, 0x6a, 0x50, 0x39, 0xea, 0x3c, 0xeb, 0x74, 0x5f, 0x76, 0x6c, 0xdc, 0xdd, 0x6f, 0xd5, 0x2e,
0xa1, 0x0a, 0x14, 0xd5, 0xc9, 0x6e, 0xee, 0xef, 0xd7, 0x0c, 0xb4, 0x0e, 0x6b, 0x9a, 0x3a, 0x68,
0x76, 0x9a, 0x4f, 0x5b, 0xf6, 0xd1, 0x61, 0x0b, 0x1f, 0xd6, 0x32, 0xe8, 0x1a, 0xac, 0x87, 0xec,
0xee, 0x93, 0x16, 0x6e, 0xf6, 0x5a, 0xf6, 0x5e, 0xb7, 0xd3, 0x6b, 0x75, 0x7a, 0xb5, 0xac, 0xf5,
0x8f, 0x01, 0x57, 0x13, 0xdb, 0xcf, 0x69, 0x30, 0x60, 0x42, 0x30, 0x9f, 0x0b, 0x74, 0x0d, 0x8a,
0x94, 0x0b, 0xdb, 0xe7, 0xde, 0x58, 0x67, 0xaa, 0x88, 0x0b, 0x94, 0x8b, 0x2e, 0xf7, 0xc6, 0xc8,
0x84, 0xc2, 0x30, 0x60, 0xe7, 0x44, 0x52, 0x9d, 0xa3, 0x22, 0x8e, 0x49, 0xf4, 0x39, 0xac, 0x10,
0xc7, 0xa1, 0x42, 0xe8, 0x0c, 0xad, 0xee, 0x7e, 0xb0, 0x20, 0xc0, 0x94, 0x91, 0x46, 0x53, 0x0b,
0xe3, 0x48, 0xc9, 0xea, 0xc1, 0x4a, 0xc8, 0x41, 0x08, 0x56, 0xe3, 0x40, 0x9b, 0x7b, 0x7b, 0xad,
0xc3, 0xc3, 0xda, 0x25, 0xb4, 0x06, 0xd5, 0x4e, 0xd7, 0x3e, 0x68, 0x1d, 0x3c, 0x6e, 0xe1, 0xc3,
0x2f, 0xdb, 0xcf, 0x6b, 0x06, 0xba, 0x02, 0x97, 0xdb, 0x9d, 0xaf, 0xdb, 0xbd, 0x66, 0xaf, 0xdd,
0xed, 0xd8, 0xdd, 0xce, 0xfe, 0xab, 0x5a, 0x06, 0xad, 0x02, 0x74, 0x3b, 0x36, 0x6e, 0xbd, 0x38,
0x6a, 0x1d, 0xaa, 0x10, 0x7f, 0x2e, 0xa4, 0x42, 0x7c, 0x42, 0x85, 0x13, 0xb0, 0xa1, 0x64, 0x3e,
0x9f, 0xd4, 0xcd, 0x48, 0xd5, 0x0d, 0xb5, 0xa0, 0x10, 0x96, 0x5c, 0x98, 0x99, 0x7a, 0x76, 0xa7,
0xbc, 0xfb, 0xe1, 0x82, 0x20, 0x52, 0x30, 0x8d, 0xb0, 0x62, 0xa2, 0xc5, 0x65, 0x30, 0xc6, 0xb1,
0x2e, 0x7a, 0x04, 0xe5, 0xe1, 0x24, 0x52, 0x9d, 0x8f, 0xf2, 0xee, 0xad, 0x8b, 0xf3, 0x81, 0xd3,
0x2a, 0x68, 0x17, 0x8a, 0x71, 0x2b, 0x9b, 0x79, 0xad, 0xbe, 0x91, 0x52, 0xd7, 0xad, 0x17, 0x7e,
0xc5, 0x89, 0x1c, 0x7a, 0x08, 0x79, 0xd5, 0x94, 0xc2, 0x5c, 0xd1, 0xae, 0xdf, 0x79, 0x83, 0xeb,
0x0a, 0x25, 0x72, 0x3c, 0xd4, 0x53, 0x65, 0x3f, 0x26, 0xdc, 0xf6, 0x98, 0x90, 0x66, 0xa1, 0x9e,
0xdd, 0x29, 0xe1, 0xc2, 0x31, 0xe1, 0xfb, 0x4c, 0x48, 0xd4, 0x01, 0x70, 0x88, 0xa4, 0x7d, 0x3f,
0x60, 0x54, 0x98, 0x45, 0x6d, 0xa0, 0xf1, 0x26, 0x03, 0x89, 0x42, 0x68, 0x25, 0x85, 0x80, 0x1e,
0x80, 0x49, 0x02, 0xe7, 0x94, 0x9d, 0x53, 0x7b, 0x40, 0xfa, 0x9c, 0x4a, 0x8f, 0xf1, 0x33, 0x3b,
0xac, 0x48, 0x49, 0x57, 0x64, 0x23, 0xfa, 0x7e, 0x90, 0x7c, 0xde, 0xd3, 0x25, 0x7a, 0x0a, 0xab,
0xc4, 0x1d, 0x30, 0x6e, 0x0b, 0x2a, 0x25, 0xe3, 0x7d, 0x61, 0x82, 0xce, 0x4f, 0x7d, 0x81, 0x37,
0x4d, 0x25, 0x78, 0x18, 0xc9, 0xe1, 0x2a, 0x49, 0x93, 0xe8, 0x3d, 0xa8, 0x32, 0x2e, 0x03, 0xdf,
0x1e, 0x50, 0x21, 0x48, 0x9f, 0x9a, 0x65, 0x3d, 0xd8, 0x15, 0xcd, 0x3c, 0x08, 0x79, 0x4a, 0xc8,
0x1f, 0xa5, 0x85, 0x2a, 0xa1, 0x90, 0x66, 0xc6, 0x42, 0x37, 0xa0, 0x44, 0xb9, 0x13, 0x8c, 0x87,
0x92, 0xba, 0x66, 0x55, 0x4f, 0xc5, 0x84, 0x81, 0x10, 0xe4, 0x24, 0xe9, 0x0b, 0x73, 0x55, 0x67,
0x54, 0x9f, 0xb7, 0x8e, 0xa0, 0x92, 0xee, 0x1c, 0x54, 0x83, 0xec, 0x19, 0x0d, 0x67, 0xad, 0x84,
0xd5, 0x11, 0xdd, 0x85, 0xfc, 0x39, 0xf1, 0x46, 0xe1, 0x94, 0x95, 0x77, 0xaf, 0x2d, 0xbd, 0x2d,
0x70, 0x28, 0xf7, 0x49, 0xe6, 0x81, 0xb1, 0xf5, 0x02, 0x60, 0x52, 0xd5, 0x05, 0xa0, 0x1f, 0x4d,
0x83, 0x6e, 0x2e, 0x00, 0x55, 0xfa, 0x69, 0xc8, 0xd7, 0x70, 0x79, 0xa6, 0x8e, 0x0b, 0x70, 0xef,
0x4d, 0xe3, 0x5e, 0x5f, 0x84, 0x1b, 0x82, 0x8c, 0x53, 0xd8, 0xd6, 0xb7, 0xb0, 0xb1, 0xb8, 0x54,
0xe8, 0x09, 0x6c, 0x0f, 0x19, 0x8f, 0x93, 0x6e, 0x13, 0xcf, 0xb3, 0xa3, 0xd9, 0xb2, 0x29, 0x27,
0xc7, 0x1e, 0x75, 0xa3, 0x7b, 0xe9, 0xfa, 0x90, 0xf1, 0xa8, 0x0c, 0x4d, 0xcf, 0x4b, 0x72, 0xaa,
0x45, 0xac, 0xbf, 0x33, 0x50, 0x9d, 0x0a, 0x0c, 0x7d, 0x31, 0x99, 0x6f, 0x43, 0xf7, 0xf0, 0xfb,
0x4b, 0x52, 0xf0, 0x76, 0x83, 0x9d, 0x79, 0xb7, 0xc1, 0xce, 0xbe, 0xe5, 0x60, 0x6f, 0x43, 0x39,
0x1a, 0x1d, 0xbd, 0xbb, 0x72, 0x3a, 0xf1, 0xf1, 0x34, 0xa9, 0xd5, 0xb5, 0x05, 0xc5, 0xa1, 0x2f,
0x98, 0x9a, 0x3a, 0x7d, 0x5b, 0xe4, 0x71, 0x42, 0xff, 0x47, 0xad, 0x66, 0xb9, 0xb0, 0x36, 0x57,
0xdb, 0x59, 0x47, 0x8d, 0x39, 0x47, 0x11, 0xe4, 0x38, 0x19, 0x84, 0x96, 0x4a, 0x58, 0x9f, 0xa7,
0x9c, 0xcf, 0x4e, 0x3b, 0x6f, 0xfd, 0x64, 0xc0, 0x95, 0xc4, 0x4c, 0x9b, 0x9f, 0x33, 0x49, 0xf4,
0xed, 0x7d, 0x1f, 0xd6, 0x27, 0xeb, 0xdc, 0x9d, 0xdc, 0x39, 0xd1, 0x5e, 0xbf, 0xea, 0x2c, 0xb9,
0xf2, 0xfb, 0xea, 0x31, 0x10, 0x2d, 0xf7, 0x90, 0x58, 0xbe, 0xd9, 0x6f, 0x02, 0x0c, 0x47, 0xc7,
0x1e, 0x73, 0x6c, 0x95, 0xaf, 0x9c, 0xd6, 0x29, 0x85, 0x9c, 0x67, 0x74, 0x6c, 0xfd, 0x6a, 0xa4,
0xba, 0x17, 0xd3, 0xef, 0x46, 0x54, 0xc8, 0x9e, 0xff, 0x95, 0xcf, 0x96, 0xed, 0x96, 0x68, 0xa9,
0xa6, 0xe2, 0x57, 0x4b, 0xb5, 0xa3, 0x52, 0xb0, 0xd4, 0x87, 0xd9, 0x67, 0x4b, 0x6e, 0xfe, 0xd9,
0x72, 0x1b, 0x2a, 0x2e, 0x13, 0x43, 0x8f, 0x8c, 0x43, 0xe8, 0xbc, 0x06, 0x28, 0x47, 0x3c, 0x05,
0x6f, 0xfd, 0x66, 0xc0, 0x8d, 0x54, 0xb1, 0xb8, 0x43, 0xbd, 0xff, 0xb7, 0xc3, 0x7f, 0x1a, 0x70,
0x6b, 0x71, 0x6e, 0x31, 0x15, 0x43, 0x9f, 0x0b, 0xba, 0xc4, 0xe5, 0xcf, 0xa0, 0x94, 0x98, 0xba,
0x60, 0x3a, 0x53, 0x5d, 0x81, 0x27, 0x0a, 0xaa, 0x13, 0xd5, 0x63, 0x44, 0x5f, 0xe3, 0x59, 0x7d,
0xbd, 0x24, 0xf4, 0xa4, 0x79, 0x72, 0xe9, 0xe6, 0x99, 0x0d, 0x37, 0x3f, 0x17, 0xae, 0x85, 0x61,
0x73, 0x3e, 0x94, 0x7d, 0x4a, 0xce, 0x97, 0xc5, 0x30, 0x8b, 0x99, 0x99, 0xc7, 0xfc, 0x06, 0x6e,
0xa7, 0x46, 0x33, 0xbc, 0xfd, 0x66, 0xb7, 0xe5, 0x12, 0xf4, 0x9b, 0x00, 0xe1, 0xc2, 0xb5, 0x47,
0x01, 0x8b, 0xca, 0x5a, 0x0a, 0x39, 0x47, 0x01, 0xb3, 0x7e, 0x37, 0xa0, 0xfc, 0x92, 0x9c, 0x8d,
0xe2, 0xd5, 0x56, 0x83, 0xac, 0x60, 0xfd, 0x68, 0xac, 0xd4, 0x51, 0x2d, 0x3b, 0xc9, 0x06, 0x54,
0x48, 0x32, 0x18, 0x6a, 0xfd, 0x1c, 0x9e, 0x30, 0x94, 0x51, 0xe9, 0x0f, 0x99, 0xa3, 0xf3, 0x57,
0xc1, 0x21, 0xa1, 0x1f, 0x8d, 0x64, 0xec, 0xf9, 0x24, 0x6e, 0x88, 0x98, 0x0c, 0xbf, 0xb8, 0x2e,
0xe3, 0xfd, 0x28, 0x77, 0x31, 0xa9, 0xae, 0x8a, 0x53, 0x22, 0x4e, 0xcd, 0x15, 0xcd, 0xd6, 0x67,
0x64, 0x41, 0x45, 0x9e, 0xb2, 0xc0, 0x7d, 0x4e, 0x02, 0x95, 0x07, 0xb3, 0x10, 0x2e, 0xe3, 0x34,
0xcf, 0xfa, 0x11, 0xb6, 0x52, 0x01, 0xc4, 0x69, 0xa1, 0x92, 0xb8, 0x44, 0x12, 0x65, 0xef, 0x9c,
0x06, 0x22, 0xbe, 0x2a, 0xaa, 0x38, 0x26, 0x95, 0xbd, 0x93, 0xc0, 0x1f, 0x44, 0x21, 0xe9, 0x33,
0x5a, 0x85, 0x8c, 0xf4, 0x75, 0x28, 0x39, 0x9c, 0x91, 0xbe, 0xb2, 0xef, 0xf8, 0x5c, 0x52, 0x2e,
0x7b, 0x3a, 0xc8, 0x5c, 0x3d, 0xbb, 0x53, 0xc1, 0x53, 0x3c, 0xf5, 0xf4, 0x47, 0xf3, 0x0e, 0x5c,
0x60, 0xf8, 0x11, 0x14, 0x07, 0x91, 0x7b, 0x51, 0xcb, 0xa6, 0x96, 0xd2, 0xf2, 0x50, 0x70, 0xa2,
0x85, 0xee, 0x29, 0x04, 0x2d, 0xa3, 0xde, 0x9a, 0x6a, 0xad, 0xad, 0x2f, 0x44, 0xc0, 0x89, 0x98,
0xf5, 0x87, 0x01, 0xdb, 0xf3, 0xd8, 0x6d, 0xee, 0xd2, 0xef, 0xdf, 0x22, 0x57, 0xef, 0xee, 0xf2,
0x06, 0xac, 0xf8, 0x27, 0x27, 0x82, 0xca, 0x28, 0xbb, 0x11, 0xa5, 0xaa, 0x20, 0xd8, 0x0f, 0x34,
0xfa, 0x37, 0xa5, 0xcf, 0xb3, 0x3d, 0x92, 0x4b, 0x7a, 0xc4, 0xfa, 0xcb, 0x80, 0xcd, 0x25, 0x51,
0xa0, 0x67, 0x50, 0x8c, 0x5e, 0x90, 0xf1, 0xae, 0xbf, 0x7b, 0x91, 0x8f, 0x5a, 0xa9, 0x11, 0x11,
0xd1, 0xda, 0x4f, 0x00, 0xb6, 0x4e, 0xa0, 0x3a, 0xf5, 0x69, 0xc1, 0x16, 0x7d, 0x38, 0xbd, 0x45,
0xef, 0xbc, 0xd1, 0x58, 0x92, 0x95, 0xc9, 0x56, 0x7d, 0x5c, 0x7d, 0x5d, 0x6e, 0xdc, 0xfd, 0x34,
0xd6, 0x3c, 0x5e, 0xd1, 0xa7, 0xfb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x01, 0x61, 0xde, 0x61,
0xf9, 0x0e, 0x00, 0x00,
}

View file

@ -17,6 +17,7 @@ message CommunityMember {
UNKNOWN_ROLE = 0;
ROLE_ALL = 1;
ROLE_MANAGE_USERS = 2;
ROLE_MODERATE_CONTENT = 3;
}
repeated Roles roles = 1;
}

View file

@ -0,0 +1,34 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf"
)
var ErrAddRoleToMemberInvalidCommunityID = errors.New("add-role-to-member: invalid community id")
var ErrAddRoleToMemberInvalidUser = errors.New("add-role-to-member: invalid user id")
var ErrAddRoleToMemberInvalidRole = errors.New("add-role-to-member: invalid role")
type AddRoleToMember struct {
CommunityID types.HexBytes `json:"communityId"`
User types.HexBytes `json:"user"`
Role protobuf.CommunityMember_Roles `json:"role"`
}
func (a *AddRoleToMember) Validate() error {
if len(a.CommunityID) == 0 {
return ErrAddRoleToMemberInvalidCommunityID
}
if len(a.User) == 0 {
return ErrAddRoleToMemberInvalidUser
}
if a.Role == protobuf.CommunityMember_UNKNOWN_ROLE {
return ErrAddRoleToMemberInvalidRole
}
return nil
}

View file

@ -0,0 +1,34 @@
package requests
import (
"errors"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf"
)
var ErrRemoveRoleFromMemberInvalidCommunityID = errors.New("remove-role-from-member: invalid community id")
var ErrRemoveRoleFromMemberInvalidUser = errors.New("remove-role-from-member: invalid user id")
var ErrRemoveRoleFromMemberInvalidRole = errors.New("remove-role-from-member: invalid role")
type RemoveRoleFromMember struct {
CommunityID types.HexBytes `json:"communityId"`
User types.HexBytes `json:"user"`
Role protobuf.CommunityMember_Roles `json:"role"`
}
func (r *RemoveRoleFromMember) Validate() error {
if len(r.CommunityID) == 0 {
return ErrRemoveRoleFromMemberInvalidCommunityID
}
if len(r.User) == 0 {
return ErrRemoveRoleFromMemberInvalidUser
}
if r.Role == protobuf.CommunityMember_UNKNOWN_ROLE {
return ErrRemoveRoleFromMemberInvalidRole
}
return nil
}

View file

@ -472,6 +472,14 @@ func (api *PublicAPI) UnbanUserFromCommunity(request *requests.UnbanUserFromComm
return api.service.messenger.UnbanUserFromCommunity(request)
}
func (api *PublicAPI) AddRoleToMember(request *requests.AddRoleToMember) (*protocol.MessengerResponse, error) {
return api.service.messenger.AddRoleToMember(request)
}
func (api *PublicAPI) RemoveRoleFromMember(request *requests.RemoveRoleFromMember) (*protocol.MessengerResponse, error) {
return api.service.messenger.RemoveRoleFromMember(request)
}
// MyPendingRequestsToJoin returns the pending requests for the logged in user
func (api *PublicAPI) MyPendingRequestsToJoin() ([]*communities.RequestToJoin, error) {
return api.service.messenger.MyPendingRequestsToJoin()