From 77214dcb5d6b5b9d91f863d6d9ead7ac3f13be58 Mon Sep 17 00:00:00 2001 From: Mykhailo Prakhov Date: Thu, 29 Feb 2024 18:54:17 +0100 Subject: [PATCH] feat: admins can delete all members messages during the ban (#4834) --- protocol/communities/community.go | 24 ++- protocol/communities/community_event.go | 7 +- .../communities/community_events_factory.go | 8 + .../community_events_processing.go | 12 ++ protocol/communities/errors.go | 1 + protocol/communities/roles_authorization.go | 5 +- ...events_owner_without_community_key_test.go | 5 + .../communities_events_token_master_test.go | 5 + protocol/communities_events_utils_test.go | 94 +++++++++++- protocol/communities_messenger_admin_test.go | 5 + protocol/messenger_communities.go | 2 +- protocol/protobuf/community_update.pb.go | 140 +++++++++--------- protocol/protobuf/community_update.proto | 1 + 13 files changed, 230 insertions(+), 79 deletions(-) diff --git a/protocol/communities/community.go b/protocol/communities/community.go index 678d36396..56081456e 100644 --- a/protocol/communities/community.go +++ b/protocol/communities/community.go @@ -862,10 +862,17 @@ func (o *Community) BanUserFromCommunity(pk *ecdsa.PublicKey, communityBanInfo * o.banUserFromCommunity(pk, communityBanInfo) o.increaseClock() } else { - err := o.addNewCommunityEvent(o.ToBanCommunityMemberCommunityEvent(common.PubkeyToHex(pk))) + pkStr := common.PubkeyToHex(pk) + err := o.addNewCommunityEvent(o.ToBanCommunityMemberCommunityEvent(pkStr)) if err != nil { return nil, err } + if communityBanInfo.DeleteAllMessages { + err := o.addNewCommunityEvent(o.ToDeleteAllMemberMessagesEvent(pkStr)) + if err != nil { + return nil, err + } + } } return o.config.CommunityDescription, nil @@ -2257,6 +2264,21 @@ func (o *Community) banUserFromCommunity(pk *ecdsa.PublicKey, communityBanInfo * o.config.CommunityDescription.BanList = append(o.config.CommunityDescription.BanList, key) } +func (o *Community) deleteBannedMemberAllMessages(pk *ecdsa.PublicKey) error { + key := common.PubkeyToHex(pk) + + if o.config.CommunityDescription.BannedMembers == nil { + return ErrBannedMemberNotFound + } + + if _, exists := o.config.CommunityDescription.BannedMembers[key]; !exists { + return ErrBannedMemberNotFound + } + + o.config.CommunityDescription.BannedMembers[key].DeleteAllMessages = true + return nil +} + func (o *Community) editChat(chatID string, chat *protobuf.CommunityChat) error { err := validateCommunityChat(o.config.CommunityDescription, chat) if err != nil { diff --git a/protocol/communities/community_event.go b/protocol/communities/community_event.go index 7f1176fbd..a737341a6 100644 --- a/protocol/communities/community_event.go +++ b/protocol/communities/community_event.go @@ -203,6 +203,10 @@ func (e *CommunityEvent) Validate() error { if e.TokenMetadata == nil || len(e.TokenMetadata.ContractAddresses) == 0 { return errors.New("invalid add community token event") } + case protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES: + if len(e.MemberToAction) == 0 { + return errors.New("invalid delete all community member messages event") + } } return nil } @@ -233,7 +237,8 @@ func (e *CommunityEvent) EventTypeID() string { protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_REJECT, protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK, protobuf.CommunityEvent_COMMUNITY_MEMBER_BAN, - protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN: + protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN, + protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES: return fmt.Sprintf("%d-%s", e.Type, e.MemberToAction) case protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD: diff --git a/protocol/communities/community_events_factory.go b/protocol/communities/community_events_factory.go index 311e21f85..685bb2d85 100644 --- a/protocol/communities/community_events_factory.go +++ b/protocol/communities/community_events_factory.go @@ -99,6 +99,14 @@ func (o *Community) ToBanCommunityMemberCommunityEvent(pubkey string) *Community } } +func (o *Community) ToDeleteAllMemberMessagesEvent(pubkey string) *CommunityEvent { + return &CommunityEvent{ + CommunityEventClock: o.nextEventClock(), + Type: protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES, + MemberToAction: pubkey, + } +} + func (o *Community) ToUnbanCommunityMemberCommunityEvent(pubkey string) *CommunityEvent { return &CommunityEvent{ CommunityEventClock: o.nextEventClock(), diff --git a/protocol/communities/community_events_processing.go b/protocol/communities/community_events_processing.go index 47c1ee42f..d37a50043 100644 --- a/protocol/communities/community_events_processing.go +++ b/protocol/communities/community_events_processing.go @@ -258,6 +258,18 @@ func (o *Community) applyEvent(communityEvent CommunityEvent) error { } case protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD: o.config.CommunityDescription.CommunityTokensMetadata = append(o.config.CommunityDescription.CommunityTokensMetadata, communityEvent.TokenMetadata) + case protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES: + if o.IsControlNode() { + pk, err := common.HexToPubkey(communityEvent.MemberToAction) + if err != nil { + return err + } + + err = o.deleteBannedMemberAllMessages(pk) + if err != nil { + return err + } + } } return nil } diff --git a/protocol/communities/errors.go b/protocol/communities/errors.go index a34a552b9..811483530 100644 --- a/protocol/communities/errors.go +++ b/protocol/communities/errors.go @@ -45,3 +45,4 @@ var ErrRevealedAccountsAbsent = errors.New("revealed accounts is absent") var ErrNoRevealedAccountsSignature = errors.New("revealed accounts without the signature") var ErrNoFreeSpaceForHistoryArchives = errors.New("history archive: No free space for downloading history archives") var ErrPermissionToJoinNotSatisfied = errors.New("permission to join not satisfied") +var ErrBannedMemberNotFound = errors.New("banned member not found") diff --git a/protocol/communities/roles_authorization.go b/protocol/communities/roles_authorization.go index 591b61a34..d32a3a05c 100644 --- a/protocol/communities/roles_authorization.go +++ b/protocol/communities/roles_authorization.go @@ -23,6 +23,7 @@ var adminAuthorizedEventTypes = []protobuf.CommunityEvent_EventType{ protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK, protobuf.CommunityEvent_COMMUNITY_MEMBER_BAN, protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN, + protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES, } var tokenMasterAuthorizedEventTypes = append(adminAuthorizedEventTypes, []protobuf.CommunityEvent_EventType{ @@ -112,7 +113,9 @@ func RolesAuthorizedToPerformEvent(senderRoles []protobuf.CommunityMember_Roles, } if event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_BAN || - event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK { + event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK || + event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN || + event.Type == protobuf.CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES { return canRolesKickOrBanMember(senderRoles, memberRoles) } diff --git a/protocol/communities_events_owner_without_community_key_test.go b/protocol/communities_events_owner_without_community_key_test.go index 4a8db1303..52423d20a 100644 --- a/protocol/communities_events_owner_without_community_key_test.go +++ b/protocol/communities_events_owner_without_community_key_test.go @@ -276,3 +276,8 @@ func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestMemberReceiveOwnerEve community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) testMemberReceiveEventsWhenControlNodeOffline(s, community) } + +func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerBanMemberWithDeletingAllMessages() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) + testBanMemberWithDeletingAllMessages(s, community) +} diff --git a/protocol/communities_events_token_master_test.go b/protocol/communities_events_token_master_test.go index b5497d9db..e0741f138 100644 --- a/protocol/communities_events_token_master_test.go +++ b/protocol/communities_events_token_master_test.go @@ -321,3 +321,8 @@ func (s *TokenMasterCommunityEventsSuite) TestTokenMasterAcceptsRequestToJoinAft s.SetupAdditionalMessengers([]*Messenger{user}) testPrivilegedMemberAcceptsRequestToJoinAfterMemberLeave(s, community, user) } + +func (s *TokenMasterCommunityEventsSuite) TestTokenMasterBanMemberWithDeletingAllMessages() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_TOKEN_MASTER) + testBanMemberWithDeletingAllMessages(s, community) +} diff --git a/protocol/communities_events_utils_test.go b/protocol/communities_events_utils_test.go index 105a520c3..a4cdca7fb 100644 --- a/protocol/communities_events_utils_test.go +++ b/protocol/communities_events_utils_test.go @@ -6,6 +6,7 @@ import ( "math/big" "github.com/stretchr/testify/suite" + "go.uber.org/zap" gethcommon "github.com/ethereum/go-ethereum/common" hexutil "github.com/ethereum/go-ethereum/common/hexutil" @@ -48,6 +49,9 @@ func waitOnMessengerResponse(s *suite.Suite, fnWait MessageResponseValidator, us user, func(r *MessengerResponse) bool { err := fnWait(r) + if err != nil { + user.logger.Error("WaitOnMessengerResponse: ", zap.Error(err)) + } return err == nil }, "MessengerResponse data not received", @@ -690,7 +694,8 @@ func kickMember(base CommunityEventsTestsInterface, communityID types.HexBytes, } func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserFromCommunity) { - pubkey := common.PubkeyToHex(&base.GetMember().identity.PublicKey) + bannedPK := banRequest.User.String() + communityStr := banRequest.CommunityID.String() checkBanned := func(response *MessengerResponse) error { modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(banRequest.CommunityID)) @@ -706,10 +711,16 @@ func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserF return errors.New("alice was not added to the banned list") } - if modifiedCommmunity.PendingAndBannedMembers()[pubkey] != communities.CommunityMemberBanned { + if modifiedCommmunity.PendingAndBannedMembers()[bannedPK] != communities.CommunityMemberBanned { return errors.New("alice should be in the pending state") } + if banRequest.DeleteAllMessages { + if len(response.DeletedMessages()) == 0 { + return errors.New("alice message must be deleted") + } + } + return nil } @@ -722,7 +733,7 @@ func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserF modifiedCommmunity, err := getModifiedCommunity(response, types.EncodeHex(banRequest.CommunityID)) s.Require().NoError(err) s.Require().True(modifiedCommmunity.HasMember(&base.GetMember().identity.PublicKey)) - s.Require().Equal(communities.CommunityMemberBanPending, modifiedCommmunity.PendingAndBannedMembers()[pubkey]) + s.Require().Equal(communities.CommunityMemberBanPending, modifiedCommmunity.PendingAndBannedMembers()[bannedPK]) // 2. wait for event as a sender waitOnMessengerResponse(s, func(response *MessengerResponse) error { @@ -732,11 +743,11 @@ func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserF } if !modifiedCommmunity.HasMember(&base.GetMember().identity.PublicKey) { - return errors.New("alice should not be not banned (yet)") + return errors.New("event sender: alice should not be not banned (yet)") } - if modifiedCommmunity.PendingAndBannedMembers()[pubkey] != communities.CommunityMemberBanPending { - return errors.New("alice should be in the pending state") + if modifiedCommmunity.PendingAndBannedMembers()[bannedPK] != communities.CommunityMemberBanPending { + return errors.New("event sender: alice should be in the pending state") } return nil @@ -750,16 +761,27 @@ func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserF } if !modifiedCommmunity.HasMember(&base.GetMember().identity.PublicKey) { - return errors.New("alice should not be not banned (yet)") + return errors.New("member: alice should not be not banned (yet)") } if len(modifiedCommmunity.PendingAndBannedMembers()) == 0 { - return errors.New("alice should know about banned and pending members") + return errors.New("member: alice should know about banned and pending members") } return nil }, base.GetMember()) + checkMsgDeletion := func(messenger *Messenger, expectedMsgsCount int) { + msgs, err := messenger.persistence.GetCommunityMemberAllMessagesID(bannedPK, communityStr) + s.Require().NoError(err) + s.Require().Len(msgs, expectedMsgsCount) + } + + if banRequest.DeleteAllMessages { + checkMsgDeletion(base.GetEventSender(), 1) + checkMsgDeletion(base.GetMember(), 1) + } + // 4. control node should handle event and actually ban member waitOnMessengerResponse(s, checkBanned, base.GetControlNode()) @@ -768,6 +790,12 @@ func banMember(base CommunityEventsTestsInterface, banRequest *requests.BanUserF // 6. member should be notified about actual removal waitOnMessengerResponse(s, checkBanned, base.GetMember()) + + if banRequest.DeleteAllMessages { + checkMsgDeletion(base.GetEventSender(), 0) + checkMsgDeletion(base.GetMember(), 0) + checkMsgDeletion(base.GetControlNode(), 0) + } } func unbanMember(base CommunityEventsTestsInterface, unbanRequest *requests.UnbanUserFromCommunity) { @@ -2446,3 +2474,53 @@ func testPrivilegedMemberAcceptsRequestToJoinAfterMemberLeave(base CommunityEven s.Require().NoError(err) s.Require().Len(acceptedRequestsPending, 0) } + +func testBanMemberWithDeletingAllMessages(base CommunityEventsTestsInterface, community *communities.Community) { + // verify that event sender can't ban a control node and delete his messages + banRequest := &requests.BanUserFromCommunity{ + CommunityID: community.ID(), + User: common.PubkeyToHexBytes(&base.GetControlNode().identity.PublicKey), + DeleteAllMessages: true, + } + + _, err := base.GetEventSender().BanUserFromCommunity( + context.Background(), + banRequest, + ) + s := base.GetSuite() + s.Require().Error(err) + + chatIds := community.ChatIDs() + s.Require().Len(chatIds, 1) + chat := base.GetEventSender().Chat(chatIds[0]) + s.Require().NotNil(chat) + + inputMessage := buildTestMessage(*chat) + + sendResponse, err := base.GetMember().SendChatMessage(context.Background(), inputMessage) + s.NoError(err) + s.Require().NotNil(sendResponse) + s.Require().Len(sendResponse.Messages(), 1) + messageID := sendResponse.Messages()[0].ID + + checkMsgDelivered := func(response *MessengerResponse) error { + if len(response.Messages()) == 0 { + return errors.New("response does not contain message") + } + + for _, message := range response.Messages() { + if message.ID == messageID { + return nil + } + } + return errors.New("messages was not found in the response") + } + + waitOnMessengerResponse(s, checkMsgDelivered, base.GetControlNode()) + + waitOnMessengerResponse(s, checkMsgDelivered, base.GetEventSender()) + + banRequest.User = common.PubkeyToHexBytes(&base.GetMember().identity.PublicKey) + + banMember(base, banRequest) +} diff --git a/protocol/communities_messenger_admin_test.go b/protocol/communities_messenger_admin_test.go index a8b7888a0..714544dd9 100644 --- a/protocol/communities_messenger_admin_test.go +++ b/protocol/communities_messenger_admin_test.go @@ -354,3 +354,8 @@ func (s *AdminCommunityEventsSuite) TestAdminAcceptsRequestToJoinAfterMemberLeav s.SetupAdditionalMessengers([]*Messenger{user}) testPrivilegedMemberAcceptsRequestToJoinAfterMemberLeave(s, community, user) } + +func (s *AdminCommunityEventsSuite) TestAdminBanMemberWithDeletingAllMessages() { + community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) + testBanMemberWithDeletingAllMessages(s, community) +} diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index decbcb509..655db1183 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -2676,7 +2676,7 @@ func (m *Messenger) BanUserFromCommunity(ctx context.Context, request *requests. response.AddCommunity(community) - if request.DeleteAllMessages { + if request.DeleteAllMessages && community.IsControlNode() { deleteMessagesResponse, err := m.DeleteAllCommunityMemberMessages(request.User.String(), request.CommunityID.String()) if err != nil { return nil, err diff --git a/protocol/protobuf/community_update.pb.go b/protocol/protobuf/community_update.pb.go index 385e3e964..dc5f6cc6b 100644 --- a/protocol/protobuf/community_update.pb.go +++ b/protocol/protobuf/community_update.pb.go @@ -41,6 +41,7 @@ const ( CommunityEvent_COMMUNITY_MEMBER_BAN CommunityEvent_EventType = 15 CommunityEvent_COMMUNITY_MEMBER_UNBAN CommunityEvent_EventType = 16 CommunityEvent_COMMUNITY_TOKEN_ADD CommunityEvent_EventType = 17 + CommunityEvent_COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES CommunityEvent_EventType = 18 ) // Enum value maps for CommunityEvent_EventType. @@ -64,6 +65,7 @@ var ( 15: "COMMUNITY_MEMBER_BAN", 16: "COMMUNITY_MEMBER_UNBAN", 17: "COMMUNITY_TOKEN_ADD", + 18: "COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES", } CommunityEvent_EventType_value = map[string]int32{ "UNKNOWN": 0, @@ -84,6 +86,7 @@ var ( "COMMUNITY_MEMBER_BAN": 15, "COMMUNITY_MEMBER_UNBAN": 16, "COMMUNITY_TOKEN_ADD": 17, + "COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES": 18, } ) @@ -661,7 +664,7 @@ var file_community_update_proto_rawDesc = []byte{ 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x13, 0x63, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, - 0x74, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x90, 0x0d, 0x0a, 0x0e, 0x43, + 0x74, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbd, 0x0d, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x63, 0x6f, @@ -731,7 +734,7 @@ var file_community_update_proto_rawDesc = []byte{ 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb0, 0x04, 0x0a, 0x09, 0x45, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xdd, 0x04, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x45, 0x44, 0x49, 0x54, 0x10, 0x01, 0x12, 0x2c, 0x0a, 0x28, 0x43, 0x4f, 0x4d, @@ -766,71 +769,74 @@ var file_community_update_proto_rawDesc = []byte{ 0x45, 0x52, 0x5f, 0x42, 0x41, 0x4e, 0x10, 0x0f, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x42, 0x41, 0x4e, 0x10, 0x10, 0x12, 0x17, 0x0a, 0x13, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, - 0x59, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, 0x5f, 0x41, 0x44, 0x44, 0x10, 0x11, 0x22, 0xae, 0x02, - 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x32, 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, - 0x68, 0x61, 0x74, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x08, 0x69, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x40, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x61, 0x64, 0x6d, 0x69, 0x6e, - 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, - 0x6e, 0x69, 0x74, 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, - 0x73, 0x52, 0x0d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x72, 0x6f, 0x5f, 0x6d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x75, - 0x74, 0x72, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x82, - 0x01, 0x0a, 0x0c, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x6e, 0x65, 0x6c, 0x73, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0x9c, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, - 0x6c, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x31, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, - 0x75, 0x6e, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, - 0x65, 0x6c, 0x22, 0x4e, 0x0a, 0x14, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, - 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x22, 0xe7, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x49, 0x64, - 0x12, 0x49, 0x0a, 0x21, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, - 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1e, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, - 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x06, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0c, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x54, 0x0a, 0x1e, - 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x32, - 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x6d, - 0x73, 0x67, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x59, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, 0x5f, 0x41, 0x44, 0x44, 0x10, 0x11, 0x12, 0x2b, 0x0a, + 0x27, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, + 0x45, 0x5f, 0x42, 0x41, 0x4e, 0x4e, 0x45, 0x44, 0x5f, 0x4d, 0x45, 0x4d, 0x42, 0x45, 0x52, 0x5f, + 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x53, 0x10, 0x12, 0x22, 0xae, 0x02, 0x0a, 0x0f, 0x43, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, + 0x0a, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x68, 0x61, 0x74, + 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x08, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x12, 0x40, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x50, 0x65, 0x72, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x47, 0x0a, 0x0e, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x65, + 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, + 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0d, + 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x23, 0x0a, + 0x0d, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x74, 0x72, 0x6f, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x72, 0x6f, 0x5f, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x75, 0x74, 0x72, 0x6f, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0c, + 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, + 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x5f, 0x69, 0x64, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, + 0x73, 0x49, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x9c, 0x01, 0x0a, 0x0b, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x49, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x07, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x74, 0x79, 0x43, 0x68, 0x61, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x22, + 0x4e, 0x0a, 0x14, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, + 0xe7, 0x01, 0x0a, 0x16, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, + 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x49, 0x0a, + 0x21, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x1e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x42, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x44, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x43, 0x0a, 0x0d, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0c, 0x73, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x54, 0x0a, 0x1e, 0x43, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x32, 0x0a, 0x03, 0x6d, + 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x42, + 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protocol/protobuf/community_update.proto b/protocol/protobuf/community_update.proto index 892b87162..5d37c9293 100644 --- a/protocol/protobuf/community_update.proto +++ b/protocol/protobuf/community_update.proto @@ -38,6 +38,7 @@ message CommunityEvent { COMMUNITY_MEMBER_BAN = 15; COMMUNITY_MEMBER_UNBAN = 16; COMMUNITY_TOKEN_ADD = 17; + COMMUNITY_DELETE_BANNED_MEMBER_MESSAGES = 18; } }