Allow owner/admin to delete messages of a community (#2958)
This commit is contained in:
parent
8865a0e4b0
commit
684e9654de
|
@ -183,48 +183,50 @@ func (o *Community) MarshalJSON() ([]byte, error) {
|
||||||
return nil, errors.New("member identity not set")
|
return nil, errors.New("member identity not set")
|
||||||
}
|
}
|
||||||
communityItem := struct {
|
communityItem := struct {
|
||||||
ID types.HexBytes `json:"id"`
|
ID types.HexBytes `json:"id"`
|
||||||
Admin bool `json:"admin"`
|
Admin bool `json:"admin"`
|
||||||
Verified bool `json:"verified"`
|
Verified bool `json:"verified"`
|
||||||
Joined bool `json:"joined"`
|
Joined bool `json:"joined"`
|
||||||
Spectated bool `json:"spectated"`
|
Spectated bool `json:"spectated"`
|
||||||
RequestedAccessAt int `json:"requestedAccessAt"`
|
RequestedAccessAt int `json:"requestedAccessAt"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
IntroMessage string `json:"introMessage"`
|
IntroMessage string `json:"introMessage"`
|
||||||
OutroMessage string `json:"outroMessage"`
|
OutroMessage string `json:"outroMessage"`
|
||||||
Tags []CommunityTag `json:"tags"`
|
Tags []CommunityTag `json:"tags"`
|
||||||
Chats map[string]CommunityChat `json:"chats"`
|
Chats map[string]CommunityChat `json:"chats"`
|
||||||
Categories map[string]CommunityCategory `json:"categories"`
|
Categories map[string]CommunityCategory `json:"categories"`
|
||||||
Images map[string]images.IdentityImage `json:"images"`
|
Images map[string]images.IdentityImage `json:"images"`
|
||||||
Permissions *protobuf.CommunityPermissions `json:"permissions"`
|
Permissions *protobuf.CommunityPermissions `json:"permissions"`
|
||||||
Members map[string]*protobuf.CommunityMember `json:"members"`
|
Members map[string]*protobuf.CommunityMember `json:"members"`
|
||||||
CanRequestAccess bool `json:"canRequestAccess"`
|
CanRequestAccess bool `json:"canRequestAccess"`
|
||||||
CanManageUsers bool `json:"canManageUsers"`
|
CanManageUsers bool `json:"canManageUsers"`
|
||||||
CanJoin bool `json:"canJoin"`
|
CanDeleteMessageForEveryone bool `json:"canDeleteMessageForEveryone"`
|
||||||
Color string `json:"color"`
|
CanJoin bool `json:"canJoin"`
|
||||||
RequestedToJoinAt uint64 `json:"requestedToJoinAt,omitempty"`
|
Color string `json:"color"`
|
||||||
IsMember bool `json:"isMember"`
|
RequestedToJoinAt uint64 `json:"requestedToJoinAt,omitempty"`
|
||||||
Muted bool `json:"muted"`
|
IsMember bool `json:"isMember"`
|
||||||
CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"`
|
Muted bool `json:"muted"`
|
||||||
Encrypted bool `json:"encrypted"`
|
CommunityAdminSettings CommunityAdminSettings `json:"adminSettings"`
|
||||||
BanList []string `json:"banList"`
|
Encrypted bool `json:"encrypted"`
|
||||||
|
BanList []string `json:"banList"`
|
||||||
}{
|
}{
|
||||||
ID: o.ID(),
|
ID: o.ID(),
|
||||||
Admin: o.IsAdmin(),
|
Admin: o.IsAdmin(),
|
||||||
Verified: o.config.Verified,
|
Verified: o.config.Verified,
|
||||||
Chats: make(map[string]CommunityChat),
|
Chats: make(map[string]CommunityChat),
|
||||||
Categories: make(map[string]CommunityCategory),
|
Categories: make(map[string]CommunityCategory),
|
||||||
Joined: o.config.Joined,
|
Joined: o.config.Joined,
|
||||||
Spectated: o.config.Spectated,
|
Spectated: o.config.Spectated,
|
||||||
CanRequestAccess: o.CanRequestAccess(o.config.MemberIdentity),
|
CanRequestAccess: o.CanRequestAccess(o.config.MemberIdentity),
|
||||||
CanJoin: o.canJoin(),
|
CanJoin: o.canJoin(),
|
||||||
CanManageUsers: o.CanManageUsers(o.config.MemberIdentity),
|
CanManageUsers: o.CanManageUsers(o.config.MemberIdentity),
|
||||||
RequestedToJoinAt: o.RequestedToJoinAt(),
|
CanDeleteMessageForEveryone: o.CanDeleteMessageForEveryone(o.config.MemberIdentity),
|
||||||
IsMember: o.isMember(),
|
RequestedToJoinAt: o.RequestedToJoinAt(),
|
||||||
Muted: o.config.Muted,
|
IsMember: o.isMember(),
|
||||||
Tags: o.Tags(),
|
Muted: o.config.Muted,
|
||||||
Encrypted: o.Encrypted(),
|
Tags: o.Tags(),
|
||||||
|
Encrypted: o.Encrypted(),
|
||||||
}
|
}
|
||||||
if o.config.CommunityDescription != nil {
|
if o.config.CommunityDescription != nil {
|
||||||
for id, c := range o.config.CommunityDescription.Categories {
|
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
|
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) {
|
func (o *Community) Edit(description *protobuf.CommunityDescription) {
|
||||||
o.config.CommunityDescription.Identity.DisplayName = description.Identity.DisplayName
|
o.config.CommunityDescription.Identity.DisplayName = description.Identity.DisplayName
|
||||||
o.config.CommunityDescription.Identity.Description = description.Identity.Description
|
o.config.CommunityDescription.Identity.Description = description.Identity.Description
|
||||||
|
@ -1104,6 +1164,12 @@ func adminRolePermissions() map[protobuf.CommunityMember_Roles]bool {
|
||||||
return roles
|
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 {
|
func (o *Community) validateRequestToJoinWithChatID(request *protobuf.CommunityRequestToJoin) error {
|
||||||
|
|
||||||
chat, ok := o.config.CommunityDescription.Chats[request.ChatId]
|
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)
|
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 {
|
func (o *Community) isMember() bool {
|
||||||
return o.hasMember(o.config.MemberIdentity)
|
return o.hasMember(o.config.MemberIdentity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,4 @@ var ErrNotAuthorized = errors.New("not authorized")
|
||||||
var ErrAlreadyMember = errors.New("already a member")
|
var ErrAlreadyMember = errors.New("already a member")
|
||||||
var ErrAlreadyJoined = errors.New("already joined")
|
var ErrAlreadyJoined = errors.New("already joined")
|
||||||
var ErrInvalidMessage = errors.New("invalid community description message")
|
var ErrInvalidMessage = errors.New("invalid community description message")
|
||||||
|
var ErrMemberNotFound = errors.New("member not found")
|
||||||
|
|
|
@ -1278,6 +1278,74 @@ func (m *Manager) UnbanUserFromCommunity(request *requests.UnbanUserFromCommunit
|
||||||
return community, nil
|
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) {
|
func (m *Manager) BanUserFromCommunity(request *requests.BanUserFromCommunity) (*Community, error) {
|
||||||
id := request.CommunityID
|
id := request.CommunityID
|
||||||
|
|
||||||
|
|
|
@ -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.
|
// This new column values can also be returned as a cursor for subsequent requests.
|
||||||
where := fmt.Sprintf(`
|
where := fmt.Sprintf(`
|
||||||
WHERE
|
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
|
ORDER BY cursor DESC
|
||||||
LIMIT ?`, cursorWhere)
|
LIMIT ?`, cursorWhere)
|
||||||
|
|
||||||
|
|
|
@ -1331,6 +1331,34 @@ func (m *Messenger) BanUserFromCommunity(request *requests.BanUserFromCommunity)
|
||||||
return response, nil
|
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) {
|
func (m *Messenger) findCommunityInfoFromDB(communityID string) (*communities.Community, error) {
|
||||||
id, err := hexutil.Decode(communityID)
|
id, err := hexutil.Decode(communityID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -103,7 +103,7 @@ func (s *MessengerDeleteMessageSuite) TestDeleteMessage() {
|
||||||
// Main instance user attempts to delete the message it received from theirMessenger
|
// Main instance user attempts to delete the message it received from theirMessenger
|
||||||
_, err = s.m.DeleteMessageAndSend(context.Background(), ogMessage.ID)
|
_, err = s.m.DeleteMessageAndSend(context.Background(), ogMessage.ID)
|
||||||
|
|
||||||
s.Require().Equal(ErrInvalidEditOrDeleteAuthor, err)
|
s.Require().ErrorContains(err, "Chat not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MessengerDeleteMessageSuite) TestDeleteMessagePreviousLastMessage() {
|
func (s *MessengerDeleteMessageSuite) TestDeleteMessagePreviousLastMessage() {
|
||||||
|
|
|
@ -1257,9 +1257,22 @@ func (m *Messenger) HandleDeleteMessage(state *ReceivedMessageState, deleteMessa
|
||||||
return errors.New("chat not found")
|
return errors.New("chat not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check edit is valid
|
var canDeleteMessageForEveryone = false
|
||||||
if originalMessage.From != deleteMessage.From {
|
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
|
// Update message and return it
|
||||||
|
|
|
@ -2,8 +2,11 @@ package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/common"
|
"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 ErrInvalidEditOrDeleteAuthor = errors.New("sender is not the author of the message")
|
||||||
var ErrInvalidDeleteTypeAuthor = errors.New("message type cannot be deleted")
|
var ErrInvalidDeleteTypeAuthor = errors.New("message type cannot be deleted")
|
||||||
var ErrInvalidEditContentType = errors.New("only text or emoji messages can be replaced")
|
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) {
|
func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessage) (*MessengerResponse, error) {
|
||||||
err := request.Validate()
|
err := request.Validate()
|
||||||
|
@ -86,22 +90,44 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
|
||||||
return response, nil
|
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) {
|
func (m *Messenger) DeleteMessageAndSend(ctx context.Context, messageID string) (*MessengerResponse, error) {
|
||||||
message, err := m.persistence.MessageByID(messageID)
|
message, err := m.persistence.MessageByID(messageID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.From != common.PubkeyToHex(&m.identity.PublicKey) {
|
|
||||||
return nil, ErrInvalidEditOrDeleteAuthor
|
|
||||||
}
|
|
||||||
|
|
||||||
// A valid added chat is required.
|
// A valid added chat is required.
|
||||||
chat, ok := m.allChats.Load(message.ChatId)
|
chat, ok := m.allChats.Load(message.ChatId)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("Chat not found")
|
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
|
// Only certain types of messages can be deleted
|
||||||
if message.ContentType != protobuf.ChatMessage_TEXT_PLAIN &&
|
if message.ContentType != protobuf.ChatMessage_TEXT_PLAIN &&
|
||||||
message.ContentType != protobuf.ChatMessage_STICKER &&
|
message.ContentType != protobuf.ChatMessage_STICKER &&
|
||||||
|
|
|
@ -23,21 +23,24 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
type CommunityMember_Roles int32
|
type CommunityMember_Roles int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CommunityMember_UNKNOWN_ROLE CommunityMember_Roles = 0
|
CommunityMember_UNKNOWN_ROLE CommunityMember_Roles = 0
|
||||||
CommunityMember_ROLE_ALL CommunityMember_Roles = 1
|
CommunityMember_ROLE_ALL CommunityMember_Roles = 1
|
||||||
CommunityMember_ROLE_MANAGE_USERS CommunityMember_Roles = 2
|
CommunityMember_ROLE_MANAGE_USERS CommunityMember_Roles = 2
|
||||||
|
CommunityMember_ROLE_MODERATE_CONTENT CommunityMember_Roles = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
var CommunityMember_Roles_name = map[int32]string{
|
var CommunityMember_Roles_name = map[int32]string{
|
||||||
0: "UNKNOWN_ROLE",
|
0: "UNKNOWN_ROLE",
|
||||||
1: "ROLE_ALL",
|
1: "ROLE_ALL",
|
||||||
2: "ROLE_MANAGE_USERS",
|
2: "ROLE_MANAGE_USERS",
|
||||||
|
3: "ROLE_MODERATE_CONTENT",
|
||||||
}
|
}
|
||||||
|
|
||||||
var CommunityMember_Roles_value = map[string]int32{
|
var CommunityMember_Roles_value = map[string]int32{
|
||||||
"UNKNOWN_ROLE": 0,
|
"UNKNOWN_ROLE": 0,
|
||||||
"ROLE_ALL": 1,
|
"ROLE_ALL": 1,
|
||||||
"ROLE_MANAGE_USERS": 2,
|
"ROLE_MANAGE_USERS": 2,
|
||||||
|
"ROLE_MODERATE_CONTENT": 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x CommunityMember_Roles) String() string {
|
func (x CommunityMember_Roles) String() string {
|
||||||
|
@ -1255,92 +1258,93 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptor_f937943d74c1cd8b = []byte{
|
var fileDescriptor_f937943d74c1cd8b = []byte{
|
||||||
// 1382 bytes of a gzipped FileDescriptorProto
|
// 1396 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0x4b, 0x6f, 0x1b, 0xb7,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x57, 0xcd, 0x6f, 0x1b, 0x45,
|
||||||
0x16, 0xce, 0xe8, 0x61, 0x49, 0x47, 0x92, 0x23, 0x33, 0x89, 0x3d, 0x71, 0x1e, 0x56, 0xe6, 0xde,
|
0x14, 0xef, 0xfa, 0x23, 0xb6, 0x9f, 0xed, 0xd4, 0x99, 0x36, 0xc9, 0x36, 0xfd, 0x88, 0xbb, 0x80,
|
||||||
0x0b, 0x38, 0xb8, 0xb8, 0x0a, 0xe2, 0xe0, 0x02, 0x41, 0x1f, 0x49, 0x14, 0x47, 0x48, 0xd5, 0xd8,
|
0x94, 0x0a, 0xe1, 0xaa, 0xa9, 0x90, 0x2a, 0xbe, 0x5a, 0x37, 0xb5, 0x8a, 0x69, 0x62, 0xb7, 0x13,
|
||||||
0x72, 0x42, 0xd9, 0x4d, 0x9b, 0x45, 0x07, 0xd4, 0x0c, 0x2d, 0x13, 0x1e, 0x71, 0xd4, 0x21, 0x25,
|
0x87, 0xd2, 0x1e, 0x58, 0x4d, 0x76, 0x27, 0xce, 0x28, 0xeb, 0x59, 0xb3, 0x33, 0x8e, 0x30, 0x07,
|
||||||
0x54, 0x5d, 0x74, 0xdd, 0x9f, 0x50, 0x74, 0xdb, 0x4d, 0x57, 0xfd, 0x0b, 0x5d, 0x74, 0xd5, 0x4d,
|
0xfe, 0x0e, 0xc4, 0x95, 0x0b, 0x27, 0xfe, 0x05, 0x0e, 0x9c, 0xb8, 0x70, 0xe7, 0xc6, 0x9f, 0x82,
|
||||||
0xf7, 0xdd, 0xf5, 0xa7, 0x14, 0xe4, 0x3c, 0x34, 0x7a, 0x39, 0x01, 0x82, 0x02, 0x5d, 0x89, 0xe7,
|
0x66, 0xf6, 0xc3, 0xeb, 0xaf, 0xb4, 0x52, 0x85, 0xc4, 0xc9, 0xf3, 0xde, 0xbe, 0xf7, 0x7b, 0xdf,
|
||||||
0xf0, 0x9c, 0xef, 0xbc, 0x79, 0x46, 0xb0, 0xe1, 0xf8, 0x83, 0xc1, 0x88, 0x33, 0xc9, 0xa8, 0x68,
|
0xf3, 0xc6, 0xb0, 0xe6, 0xf8, 0x83, 0xc1, 0x88, 0x33, 0xc9, 0xa8, 0x68, 0x0c, 0x03, 0x5f, 0xfa,
|
||||||
0x0c, 0x03, 0x5f, 0xfa, 0xa8, 0xa8, 0x7f, 0x7a, 0xa3, 0xd3, 0xed, 0x2b, 0xce, 0x19, 0x91, 0x36,
|
0xa8, 0xa8, 0x7f, 0x8e, 0x47, 0x27, 0x5b, 0x57, 0x9c, 0x53, 0x22, 0x6d, 0xe6, 0x52, 0x2e, 0x99,
|
||||||
0x73, 0x29, 0x97, 0x4c, 0x4e, 0xc2, 0x6b, 0x6b, 0x0c, 0xf9, 0xe7, 0x01, 0xe1, 0x12, 0xdd, 0x81,
|
0x1c, 0x87, 0x9f, 0xad, 0x73, 0xc8, 0x3f, 0x0d, 0x08, 0x97, 0xe8, 0x36, 0x54, 0x62, 0xe5, 0xb1,
|
||||||
0x4a, 0xac, 0x3c, 0xb1, 0x99, 0x6b, 0x1a, 0x75, 0x63, 0xb7, 0x82, 0xcb, 0x09, 0xaf, 0xed, 0xa2,
|
0xcd, 0x5c, 0xd3, 0xa8, 0x1b, 0x3b, 0x15, 0x5c, 0x4e, 0x78, 0x6d, 0x17, 0x5d, 0x87, 0xd2, 0x80,
|
||||||
0x1b, 0x50, 0x1a, 0xd0, 0x41, 0x8f, 0x06, 0xea, 0x3e, 0xa3, 0xef, 0x8b, 0x21, 0xa3, 0xed, 0xa2,
|
0x0e, 0x8e, 0x69, 0xa0, 0xbe, 0x67, 0xf4, 0xf7, 0x62, 0xc8, 0x68, 0xbb, 0x68, 0x13, 0x0a, 0x11,
|
||||||
0x2d, 0x28, 0x44, 0xf8, 0x66, 0xb6, 0x6e, 0xec, 0x96, 0xf0, 0x9a, 0x22, 0xdb, 0x2e, 0xba, 0x0a,
|
0xbe, 0x99, 0xad, 0x1b, 0x3b, 0x25, 0xbc, 0xa2, 0xc8, 0xb6, 0x8b, 0xae, 0x42, 0xde, 0xf1, 0x7c,
|
||||||
0x79, 0xc7, 0xf3, 0x9d, 0x73, 0x33, 0x57, 0x37, 0x76, 0x73, 0x38, 0x24, 0xac, 0xef, 0x0c, 0xb8,
|
0xe7, 0xcc, 0xcc, 0xd5, 0x8d, 0x9d, 0x1c, 0x0e, 0x09, 0xeb, 0x17, 0x03, 0x2e, 0xef, 0xc5, 0xd8,
|
||||||
0xbc, 0x1f, 0x63, 0x1f, 0x6a, 0x10, 0xf4, 0x7f, 0xc8, 0x07, 0xbe, 0x47, 0x85, 0x69, 0xd4, 0xb3,
|
0x07, 0x1a, 0x04, 0x7d, 0x0c, 0xf9, 0xc0, 0xf7, 0xa8, 0x30, 0x8d, 0x7a, 0x76, 0x67, 0x75, 0x77,
|
||||||
0xbb, 0xeb, 0x7b, 0x3b, 0x8d, 0xd8, 0xf5, 0xc6, 0x9c, 0x64, 0x03, 0x2b, 0x31, 0x1c, 0x4a, 0x5b,
|
0xbb, 0x11, 0xbb, 0xde, 0x98, 0x91, 0x6c, 0x60, 0x25, 0x86, 0x43, 0x69, 0xeb, 0x15, 0xe4, 0x35,
|
||||||
0x8f, 0x20, 0xaf, 0x69, 0x54, 0x83, 0xca, 0x49, 0xe7, 0x45, 0xe7, 0xe8, 0x75, 0xc7, 0xc6, 0x47,
|
0x8d, 0x6a, 0x50, 0x39, 0xea, 0x3c, 0xeb, 0x74, 0x5f, 0x76, 0x6c, 0xdc, 0xdd, 0x6f, 0xd5, 0x2e,
|
||||||
0x07, 0xad, 0xda, 0x25, 0x54, 0x81, 0xa2, 0x3a, 0xd9, 0xcd, 0x83, 0x83, 0x9a, 0x81, 0xae, 0xc1,
|
0xa1, 0x0a, 0x14, 0xd5, 0xc9, 0x6e, 0xee, 0xef, 0xd7, 0x0c, 0xb4, 0x0e, 0x6b, 0x9a, 0x3a, 0x68,
|
||||||
0x86, 0xa6, 0x0e, 0x9b, 0x9d, 0xe6, 0xf3, 0x96, 0x7d, 0xd2, 0x6d, 0xe1, 0x6e, 0x2d, 0x63, 0xfd,
|
0x76, 0x9a, 0x4f, 0x5b, 0xf6, 0xd1, 0x61, 0x0b, 0x1f, 0xd6, 0x32, 0xe8, 0x1a, 0xac, 0x87, 0xec,
|
||||||
0x69, 0xc0, 0xd5, 0xc4, 0xc0, 0x4b, 0x1a, 0x0c, 0x98, 0x10, 0xcc, 0xe7, 0x02, 0x5d, 0x87, 0x22,
|
0xee, 0x93, 0x16, 0x6e, 0xf6, 0x5a, 0xf6, 0x5e, 0xb7, 0xd3, 0x6b, 0x75, 0x7a, 0xb5, 0xac, 0xf5,
|
||||||
0xe5, 0xc2, 0xf6, 0xb9, 0x37, 0xd1, 0xe9, 0x28, 0xe2, 0x02, 0xe5, 0xe2, 0x88, 0x7b, 0x13, 0x64,
|
0x8f, 0x01, 0x57, 0x13, 0xdb, 0xcf, 0x69, 0x30, 0x60, 0x42, 0x30, 0x9f, 0x0b, 0x74, 0x0d, 0x8a,
|
||||||
0x42, 0x61, 0x18, 0xb0, 0x31, 0x91, 0x54, 0x27, 0xa2, 0x88, 0x63, 0x12, 0x7d, 0x0c, 0x6b, 0xc4,
|
0x94, 0x0b, 0xdb, 0xe7, 0xde, 0x58, 0x67, 0xaa, 0x88, 0x0b, 0x94, 0x8b, 0x2e, 0xf7, 0xc6, 0xc8,
|
||||||
0x71, 0xa8, 0x10, 0x3a, 0x0d, 0xeb, 0x7b, 0xff, 0x59, 0x12, 0x45, 0xca, 0x48, 0xa3, 0xa9, 0x85,
|
0x84, 0xc2, 0x30, 0x60, 0xe7, 0x44, 0x52, 0x9d, 0xa3, 0x22, 0x8e, 0x49, 0xf4, 0x39, 0xac, 0x10,
|
||||||
0x71, 0xa4, 0x64, 0x1d, 0xc3, 0x5a, 0xc8, 0x41, 0x08, 0xd6, 0xe3, 0x68, 0x9a, 0xfb, 0xfb, 0xad,
|
0xc7, 0xa1, 0x42, 0xe8, 0x0c, 0xad, 0xee, 0x7e, 0xb0, 0x20, 0xc0, 0x94, 0x91, 0x46, 0x53, 0x0b,
|
||||||
0x6e, 0xb7, 0x76, 0x09, 0x6d, 0x40, 0xb5, 0x73, 0x64, 0x1f, 0xb6, 0x0e, 0x9f, 0xb6, 0x70, 0xf7,
|
0xe3, 0x48, 0xc9, 0xea, 0xc1, 0x4a, 0xc8, 0x41, 0x08, 0x56, 0xe3, 0x40, 0x9b, 0x7b, 0x7b, 0xad,
|
||||||
0x93, 0xf6, 0xcb, 0x9a, 0x81, 0xae, 0xc0, 0xe5, 0x76, 0xe7, 0xb3, 0xf6, 0x71, 0xf3, 0xb8, 0x7d,
|
0xc3, 0xc3, 0xda, 0x25, 0xb4, 0x06, 0xd5, 0x4e, 0xd7, 0x3e, 0x68, 0x1d, 0x3c, 0x6e, 0xe1, 0xc3,
|
||||||
0xd4, 0xb1, 0x8f, 0x3a, 0x07, 0x5f, 0xd4, 0x32, 0x68, 0x1d, 0xe0, 0xa8, 0x63, 0xe3, 0xd6, 0xab,
|
0x2f, 0xdb, 0xcf, 0x6b, 0x06, 0xba, 0x02, 0x97, 0xdb, 0x9d, 0xaf, 0xdb, 0xbd, 0x66, 0xaf, 0xdd,
|
||||||
0x93, 0x56, 0xf7, 0xb8, 0x96, 0xb5, 0x7e, 0x28, 0xa4, 0x42, 0x7c, 0x46, 0x85, 0x13, 0xb0, 0xa1,
|
0xed, 0xd8, 0xdd, 0xce, 0xfe, 0xab, 0x5a, 0x06, 0xad, 0x02, 0x74, 0x3b, 0x36, 0x6e, 0xbd, 0x38,
|
||||||
0x64, 0x3e, 0x9f, 0x16, 0xc7, 0x48, 0x15, 0x07, 0xb5, 0xa0, 0x10, 0xd6, 0x55, 0x98, 0x99, 0x7a,
|
0x6a, 0x1d, 0xaa, 0x10, 0x7f, 0x2e, 0xa4, 0x42, 0x7c, 0x42, 0x85, 0x13, 0xb0, 0xa1, 0x64, 0x3e,
|
||||||
0x76, 0xb7, 0xbc, 0xf7, 0xdf, 0x25, 0x41, 0xa4, 0x60, 0x1a, 0x61, 0x59, 0x44, 0x8b, 0xcb, 0x60,
|
0x9f, 0xd4, 0xcd, 0x48, 0xd5, 0x0d, 0xb5, 0xa0, 0x10, 0x96, 0x5c, 0x98, 0x99, 0x7a, 0x76, 0xa7,
|
||||||
0x82, 0x63, 0x5d, 0xf4, 0x04, 0xca, 0xc3, 0x69, 0xa4, 0x3a, 0x1f, 0xe5, 0xbd, 0xdb, 0x17, 0xe7,
|
0xbc, 0xfb, 0xe1, 0x82, 0x20, 0x52, 0x30, 0x8d, 0xb0, 0x62, 0xa2, 0xc5, 0x65, 0x30, 0xc6, 0xb1,
|
||||||
0x03, 0xa7, 0x55, 0xd0, 0x1e, 0x14, 0xe3, 0x7e, 0x35, 0xf3, 0x5a, 0x7d, 0x33, 0xa5, 0xae, 0xfb,
|
0x2e, 0x7a, 0x04, 0xe5, 0xe1, 0x24, 0x52, 0x9d, 0x8f, 0xf2, 0xee, 0xad, 0x8b, 0xf3, 0x81, 0xd3,
|
||||||
0x2b, 0xbc, 0xc5, 0x89, 0x1c, 0x7a, 0x0c, 0x79, 0xd5, 0x79, 0xc2, 0x5c, 0xd3, 0xae, 0xdf, 0x7d,
|
0x2a, 0x68, 0x17, 0x8a, 0x71, 0x2b, 0x9b, 0x79, 0xad, 0xbe, 0x91, 0x52, 0xd7, 0xad, 0x17, 0x7e,
|
||||||
0x8b, 0xeb, 0x0a, 0x25, 0x72, 0x3c, 0xd4, 0x53, 0x65, 0xef, 0x11, 0x6e, 0x7b, 0x4c, 0x48, 0xb3,
|
0xc5, 0x89, 0x1c, 0x7a, 0x08, 0x79, 0xd5, 0x94, 0xc2, 0x5c, 0xd1, 0xae, 0xdf, 0x79, 0x83, 0xeb,
|
||||||
0x50, 0xcf, 0xee, 0x96, 0x70, 0xa1, 0x47, 0xf8, 0x01, 0x13, 0x12, 0x75, 0x00, 0x1c, 0x22, 0x69,
|
0x0a, 0x25, 0x72, 0x3c, 0xd4, 0x53, 0x65, 0x3f, 0x26, 0xdc, 0xf6, 0x98, 0x90, 0x66, 0xa1, 0x9e,
|
||||||
0xdf, 0x0f, 0x18, 0x15, 0x66, 0x51, 0x1b, 0x68, 0xbc, 0xcd, 0x40, 0xa2, 0x10, 0x5a, 0x49, 0x21,
|
0xdd, 0x29, 0xe1, 0xc2, 0x31, 0xe1, 0xfb, 0x4c, 0x48, 0xd4, 0x01, 0x70, 0x88, 0xa4, 0x7d, 0x3f,
|
||||||
0xa0, 0x87, 0x60, 0x92, 0xc0, 0x39, 0x63, 0x63, 0x6a, 0x0f, 0x48, 0x9f, 0x53, 0xe9, 0x31, 0x7e,
|
0x60, 0x54, 0x98, 0x45, 0x6d, 0xa0, 0xf1, 0x26, 0x03, 0x89, 0x42, 0x68, 0x25, 0x85, 0x80, 0x1e,
|
||||||
0x6e, 0x87, 0x15, 0x29, 0xe9, 0x8a, 0x6c, 0x46, 0xf7, 0x87, 0xc9, 0xf5, 0xbe, 0x2e, 0xd1, 0x73,
|
0x80, 0x49, 0x02, 0xe7, 0x94, 0x9d, 0x53, 0x7b, 0x40, 0xfa, 0x9c, 0x4a, 0x8f, 0xf1, 0x33, 0x3b,
|
||||||
0x58, 0x27, 0xee, 0x80, 0x71, 0x5b, 0x50, 0x29, 0x19, 0xef, 0x0b, 0x13, 0x74, 0x7e, 0xea, 0x4b,
|
0xac, 0x48, 0x49, 0x57, 0x64, 0x23, 0xfa, 0x7e, 0x90, 0x7c, 0xde, 0xd3, 0x25, 0x7a, 0x0a, 0xab,
|
||||||
0xbc, 0x69, 0x2a, 0xc1, 0x6e, 0x24, 0x87, 0xab, 0x24, 0x4d, 0xa2, 0x7f, 0x41, 0x95, 0x71, 0x19,
|
0xc4, 0x1d, 0x30, 0x6e, 0x0b, 0x2a, 0x25, 0xe3, 0x7d, 0x61, 0x82, 0xce, 0x4f, 0x7d, 0x81, 0x37,
|
||||||
0xf8, 0xf6, 0x80, 0x0a, 0x41, 0xfa, 0xd4, 0x2c, 0xeb, 0xe9, 0xad, 0x68, 0xe6, 0x61, 0xc8, 0x53,
|
0x4d, 0x25, 0x78, 0x18, 0xc9, 0xe1, 0x2a, 0x49, 0x93, 0xe8, 0x3d, 0xa8, 0x32, 0x2e, 0x03, 0xdf,
|
||||||
0x42, 0xfe, 0x28, 0x2d, 0x54, 0x09, 0x85, 0x34, 0x33, 0x16, 0xba, 0x09, 0x25, 0xca, 0x9d, 0x60,
|
0x1e, 0x50, 0x21, 0x48, 0x9f, 0x9a, 0x65, 0x3d, 0xd8, 0x15, 0xcd, 0x3c, 0x08, 0x79, 0x4a, 0xc8,
|
||||||
0x32, 0x94, 0xd4, 0x35, 0xab, 0x7a, 0x2a, 0xa6, 0x0c, 0x84, 0x20, 0x27, 0x49, 0x5f, 0x98, 0xeb,
|
0x1f, 0xa5, 0x85, 0x2a, 0xa1, 0x90, 0x66, 0xc6, 0x42, 0x37, 0xa0, 0x44, 0xb9, 0x13, 0x8c, 0x87,
|
||||||
0x3a, 0xa3, 0xfa, 0xbc, 0x7d, 0x02, 0x95, 0x74, 0xe7, 0xa0, 0x1a, 0x64, 0xcf, 0x69, 0x38, 0x6b,
|
0x92, 0xba, 0x66, 0x55, 0x4f, 0xc5, 0x84, 0x81, 0x10, 0xe4, 0x24, 0xe9, 0x0b, 0x73, 0x55, 0x67,
|
||||||
0x25, 0xac, 0x8e, 0xe8, 0x1e, 0xe4, 0xc7, 0xc4, 0x1b, 0x85, 0x53, 0x56, 0xde, 0xbb, 0xbe, 0xf2,
|
0x54, 0x9f, 0xb7, 0x8e, 0xa0, 0x92, 0xee, 0x1c, 0x54, 0x83, 0xec, 0x19, 0x0d, 0x67, 0xad, 0x84,
|
||||||
0x49, 0xc0, 0xa1, 0xdc, 0x07, 0x99, 0x87, 0xc6, 0xf6, 0x2b, 0x80, 0x69, 0x55, 0x97, 0x80, 0xfe,
|
0xd5, 0x11, 0xdd, 0x85, 0xfc, 0x39, 0xf1, 0x46, 0xe1, 0x94, 0x95, 0x77, 0xaf, 0x2d, 0xbd, 0x2d,
|
||||||
0x6f, 0x16, 0x74, 0x6b, 0x09, 0xa8, 0xd2, 0x4f, 0x43, 0xbe, 0x81, 0xcb, 0x73, 0x75, 0x5c, 0x82,
|
0x70, 0x28, 0xf7, 0x49, 0xe6, 0x81, 0xb1, 0xf5, 0x02, 0x60, 0x52, 0xd5, 0x05, 0xa0, 0x1f, 0x4d,
|
||||||
0x7b, 0x7f, 0x16, 0xf7, 0xc6, 0x32, 0xdc, 0x10, 0x64, 0x92, 0xc2, 0xb6, 0xbe, 0x84, 0xcd, 0xe5,
|
0x83, 0x6e, 0x2e, 0x00, 0x55, 0xfa, 0x69, 0xc8, 0xd7, 0x70, 0x79, 0xa6, 0x8e, 0x0b, 0x70, 0xef,
|
||||||
0xa5, 0x42, 0xcf, 0x60, 0x67, 0xc8, 0x78, 0x9c, 0x74, 0x9b, 0x78, 0x9e, 0x1d, 0xcd, 0x96, 0x4d,
|
0x4d, 0xe3, 0x5e, 0x5f, 0x84, 0x1b, 0x82, 0x8c, 0x53, 0xd8, 0xd6, 0xb7, 0xb0, 0xb1, 0xb8, 0x54,
|
||||||
0x39, 0xe9, 0x79, 0xd4, 0x8d, 0xde, 0xa5, 0x1b, 0x43, 0xc6, 0xa3, 0x32, 0x34, 0x3d, 0x2f, 0xc9,
|
0xe8, 0x09, 0x6c, 0x0f, 0x19, 0x8f, 0x93, 0x6e, 0x13, 0xcf, 0xb3, 0xa3, 0xd9, 0xb2, 0x29, 0x27,
|
||||||
0xa9, 0x16, 0xb1, 0xfe, 0xc8, 0x40, 0x75, 0x26, 0x30, 0xf4, 0x68, 0x3a, 0xdf, 0x86, 0xee, 0xe1,
|
0xc7, 0x1e, 0x75, 0xa3, 0x7b, 0xe9, 0xfa, 0x90, 0xf1, 0xa8, 0x0c, 0x4d, 0xcf, 0x4b, 0x72, 0xaa,
|
||||||
0x7f, 0xaf, 0x48, 0xc1, 0xbb, 0x0d, 0x76, 0xe6, 0xfd, 0x06, 0x3b, 0xfb, 0x8e, 0x83, 0xbd, 0x03,
|
0x45, 0xac, 0xbf, 0x33, 0x50, 0x9d, 0x0a, 0x0c, 0x7d, 0x31, 0x99, 0x6f, 0x43, 0xf7, 0xf0, 0xfb,
|
||||||
0xe5, 0x68, 0x74, 0xf4, 0x82, 0xca, 0xe9, 0xc4, 0xc7, 0xd3, 0xa4, 0xf6, 0xd3, 0x36, 0x14, 0x87,
|
0x4b, 0x52, 0xf0, 0x76, 0x83, 0x9d, 0x79, 0xb7, 0xc1, 0xce, 0xbe, 0xe5, 0x60, 0x6f, 0x43, 0x39,
|
||||||
0xbe, 0x60, 0x6a, 0xea, 0xf4, 0x6b, 0x91, 0xc7, 0x09, 0xfd, 0x37, 0xb5, 0x9a, 0xe5, 0xc2, 0xc6,
|
0x1a, 0x1d, 0xbd, 0xbb, 0x72, 0x3a, 0xf1, 0xf1, 0x34, 0xa9, 0xd5, 0xb5, 0x05, 0xc5, 0xa1, 0x2f,
|
||||||
0x42, 0x6d, 0xe7, 0x1d, 0x35, 0x16, 0x1c, 0x45, 0x90, 0xe3, 0x64, 0x10, 0x5a, 0x2a, 0x61, 0x7d,
|
0x98, 0x9a, 0x3a, 0x7d, 0x5b, 0xe4, 0x71, 0x42, 0xff, 0x47, 0xad, 0x66, 0xb9, 0xb0, 0x36, 0x57,
|
||||||
0x9e, 0x71, 0x3e, 0x3b, 0xeb, 0xbc, 0xf5, 0xbd, 0x01, 0x57, 0x12, 0x33, 0x6d, 0x3e, 0x66, 0x92,
|
0xdb, 0x59, 0x47, 0x8d, 0x39, 0x47, 0x11, 0xe4, 0x38, 0x19, 0x84, 0x96, 0x4a, 0x58, 0x9f, 0xa7,
|
||||||
0xe8, 0xd7, 0xfb, 0x01, 0x5c, 0x9b, 0xee, 0x6c, 0x77, 0xfa, 0xe6, 0x44, 0xcb, 0xfb, 0xaa, 0xb3,
|
0x9c, 0xcf, 0x4e, 0x3b, 0x6f, 0xfd, 0x64, 0xc0, 0x95, 0xc4, 0x4c, 0x9b, 0x9f, 0x33, 0x49, 0xf4,
|
||||||
0xe2, 0xc9, 0xef, 0xab, 0x8d, 0x1f, 0x6d, 0xf0, 0x90, 0x58, 0xbd, 0xbe, 0x6f, 0x01, 0x0c, 0x47,
|
0xed, 0x7d, 0x1f, 0xd6, 0x27, 0xeb, 0xdc, 0x9d, 0xdc, 0x39, 0xd1, 0x5e, 0xbf, 0xea, 0x2c, 0xb9,
|
||||||
0x3d, 0x8f, 0x39, 0xb6, 0xca, 0x57, 0x4e, 0xeb, 0x94, 0x42, 0xce, 0x0b, 0x3a, 0xb1, 0x7e, 0x32,
|
0xf2, 0xfb, 0xea, 0x31, 0x10, 0x2d, 0xf7, 0x90, 0x58, 0xbe, 0xd9, 0x6f, 0x02, 0x0c, 0x47, 0xc7,
|
||||||
0x52, 0xdd, 0x8b, 0xe9, 0x57, 0x23, 0x2a, 0xe4, 0xb1, 0xff, 0xa9, 0xcf, 0x56, 0xed, 0x96, 0x68,
|
0x1e, 0x73, 0x6c, 0x95, 0xaf, 0x9c, 0xd6, 0x29, 0x85, 0x9c, 0x67, 0x74, 0x6c, 0xfd, 0x6a, 0xa4,
|
||||||
0xa9, 0xa6, 0xe2, 0x57, 0x4b, 0xb5, 0xa3, 0x52, 0xb0, 0xd2, 0x87, 0xf9, 0x6f, 0x93, 0xdc, 0xe2,
|
0xba, 0x17, 0xd3, 0xef, 0x46, 0x54, 0xc8, 0x9e, 0xff, 0x95, 0xcf, 0x96, 0xed, 0x96, 0x68, 0xa9,
|
||||||
0xb7, 0xc9, 0x1d, 0xa8, 0xb8, 0x4c, 0x0c, 0x3d, 0x32, 0x09, 0xa1, 0xf3, 0x1a, 0xa0, 0x1c, 0xf1,
|
0xa6, 0xe2, 0x57, 0x4b, 0xb5, 0xa3, 0x52, 0xb0, 0xd4, 0x87, 0xd9, 0x67, 0x4b, 0x6e, 0xfe, 0xd9,
|
||||||
0x14, 0xbc, 0xf5, 0xb3, 0x01, 0x37, 0x53, 0xc5, 0xe2, 0x0e, 0xf5, 0xfe, 0xd9, 0x0e, 0xff, 0x66,
|
0x72, 0x1b, 0x2a, 0x2e, 0x13, 0x43, 0x8f, 0x8c, 0x43, 0xe8, 0xbc, 0x06, 0x28, 0x47, 0x3c, 0x05,
|
||||||
0xc0, 0xed, 0xe5, 0xb9, 0xc5, 0x54, 0x0c, 0x7d, 0x2e, 0xe8, 0x0a, 0x97, 0x3f, 0x82, 0x52, 0x62,
|
0x6f, 0xfd, 0x66, 0xc0, 0x8d, 0x54, 0xb1, 0xb8, 0x43, 0xbd, 0xff, 0xb7, 0xc3, 0x7f, 0x1a, 0x70,
|
||||||
0xea, 0x82, 0xe9, 0x4c, 0x75, 0x05, 0x9e, 0x2a, 0xa8, 0x4e, 0x54, 0x1f, 0x23, 0xfa, 0x19, 0xcf,
|
0x6b, 0x71, 0x6e, 0x31, 0x15, 0x43, 0x9f, 0x0b, 0xba, 0xc4, 0xe5, 0xcf, 0xa0, 0x94, 0x98, 0xba,
|
||||||
0xea, 0xe7, 0x25, 0xa1, 0xa7, 0xcd, 0x93, 0x4b, 0x37, 0xcf, 0x7c, 0xb8, 0xf9, 0x85, 0x70, 0x2d,
|
0x60, 0x3a, 0x53, 0x5d, 0x81, 0x27, 0x0a, 0xaa, 0x13, 0xd5, 0x63, 0x44, 0x5f, 0xe3, 0x59, 0x7d,
|
||||||
0x0c, 0x5b, 0x8b, 0xa1, 0x1c, 0x50, 0x32, 0x5e, 0x15, 0xc3, 0x3c, 0x66, 0x66, 0x11, 0xf3, 0x73,
|
0xbd, 0x24, 0xf4, 0xa4, 0x79, 0x72, 0xe9, 0xe6, 0x99, 0x0d, 0x37, 0x3f, 0x17, 0xae, 0x85, 0x61,
|
||||||
0xb8, 0x93, 0x1a, 0xcd, 0xf0, 0xf5, 0x9b, 0xdf, 0x96, 0x2b, 0xd0, 0x6f, 0x01, 0x84, 0x0b, 0xd7,
|
0x73, 0x3e, 0x94, 0x7d, 0x4a, 0xce, 0x97, 0xc5, 0x30, 0x8b, 0x99, 0x99, 0xc7, 0xfc, 0x06, 0x6e,
|
||||||
0x1e, 0x05, 0x2c, 0x2a, 0x6b, 0x29, 0xe4, 0x9c, 0x04, 0xcc, 0xfa, 0xc5, 0x80, 0xf2, 0x6b, 0x72,
|
0xa7, 0x46, 0x33, 0xbc, 0xfd, 0x66, 0xb7, 0xe5, 0x12, 0xf4, 0x9b, 0x00, 0xe1, 0xc2, 0xb5, 0x47,
|
||||||
0x3e, 0x8a, 0x57, 0x5b, 0x0d, 0xb2, 0x82, 0xf5, 0xa3, 0xb1, 0x52, 0x47, 0xb5, 0xec, 0x24, 0x1b,
|
0x01, 0x8b, 0xca, 0x5a, 0x0a, 0x39, 0x47, 0x01, 0xb3, 0x7e, 0x37, 0xa0, 0xfc, 0x92, 0x9c, 0x8d,
|
||||||
0x50, 0x21, 0xc9, 0x60, 0xa8, 0xf5, 0x73, 0x78, 0xca, 0x50, 0x46, 0xa5, 0x3f, 0x64, 0x8e, 0xce,
|
0xe2, 0xd5, 0x56, 0x83, 0xac, 0x60, 0xfd, 0x68, 0xac, 0xd4, 0x51, 0x2d, 0x3b, 0xc9, 0x06, 0x54,
|
||||||
0x5f, 0x05, 0x87, 0x84, 0xfe, 0x68, 0x24, 0x13, 0xcf, 0x27, 0x71, 0x43, 0xc4, 0x64, 0x78, 0xe3,
|
0x48, 0x32, 0x18, 0x6a, 0xfd, 0x1c, 0x9e, 0x30, 0x94, 0x51, 0xe9, 0x0f, 0x99, 0xa3, 0xf3, 0x57,
|
||||||
0xba, 0x8c, 0xf7, 0xa3, 0xdc, 0xc5, 0xa4, 0x7a, 0x2a, 0xce, 0x88, 0x38, 0x33, 0xd7, 0x34, 0x5b,
|
0xc1, 0x21, 0xa1, 0x1f, 0x8d, 0x64, 0xec, 0xf9, 0x24, 0x6e, 0x88, 0x98, 0x0c, 0xbf, 0xb8, 0x2e,
|
||||||
0x9f, 0x91, 0x05, 0x15, 0x79, 0xc6, 0x02, 0xf7, 0x25, 0x09, 0x54, 0x1e, 0xcc, 0x42, 0xb8, 0x8c,
|
0xe3, 0xfd, 0x28, 0x77, 0x31, 0xa9, 0xae, 0x8a, 0x53, 0x22, 0x4e, 0xcd, 0x15, 0xcd, 0xd6, 0x67,
|
||||||
0xd3, 0x3c, 0xeb, 0x5b, 0xd8, 0x4e, 0x05, 0x10, 0xa7, 0x85, 0x4a, 0xe2, 0x12, 0x49, 0x94, 0xbd,
|
0x64, 0x41, 0x45, 0x9e, 0xb2, 0xc0, 0x7d, 0x4e, 0x02, 0x95, 0x07, 0xb3, 0x10, 0x2e, 0xe3, 0x34,
|
||||||
0x31, 0x0d, 0x44, 0xfc, 0x54, 0x54, 0x71, 0x4c, 0x2a, 0x7b, 0xa7, 0x81, 0x3f, 0x88, 0x42, 0xd2,
|
0xcf, 0xfa, 0x11, 0xb6, 0x52, 0x01, 0xc4, 0x69, 0xa1, 0x92, 0xb8, 0x44, 0x12, 0x65, 0xef, 0x9c,
|
||||||
0x67, 0xb4, 0x0e, 0x19, 0xe9, 0xeb, 0x50, 0x72, 0x38, 0x23, 0x7d, 0x65, 0xdf, 0xf1, 0xb9, 0xa4,
|
0x06, 0x22, 0xbe, 0x2a, 0xaa, 0x38, 0x26, 0x95, 0xbd, 0x93, 0xc0, 0x1f, 0x44, 0x21, 0xe9, 0x33,
|
||||||
0x5c, 0x1e, 0xeb, 0x20, 0x73, 0xf5, 0xec, 0x6e, 0x05, 0xcf, 0xf0, 0xac, 0x1f, 0x0d, 0x40, 0x8b,
|
0x5a, 0x85, 0x8c, 0xf4, 0x75, 0x28, 0x39, 0x9c, 0x91, 0xbe, 0xb2, 0xef, 0xf8, 0x5c, 0x52, 0x2e,
|
||||||
0x0e, 0x5c, 0x60, 0xf8, 0x09, 0x14, 0x07, 0x91, 0x7b, 0x51, 0xcb, 0xa6, 0x96, 0xd2, 0xea, 0x50,
|
0x7b, 0x3a, 0xc8, 0x5c, 0x3d, 0xbb, 0x53, 0xc1, 0x53, 0x3c, 0xf5, 0xf4, 0x47, 0xf3, 0x0e, 0x5c,
|
||||||
0x70, 0xa2, 0x85, 0xee, 0x2b, 0x04, 0x2d, 0xa3, 0xbe, 0x35, 0xd5, 0x5a, 0xbb, 0xb6, 0x14, 0x01,
|
0x60, 0xf8, 0x11, 0x14, 0x07, 0x91, 0x7b, 0x51, 0xcb, 0xa6, 0x96, 0xd2, 0xf2, 0x50, 0x70, 0xa2,
|
||||||
0x27, 0x62, 0xd6, 0xaf, 0x06, 0xec, 0x2c, 0x62, 0xb7, 0xb9, 0x4b, 0xbf, 0x7e, 0x87, 0x5c, 0xbd,
|
0x85, 0xee, 0x29, 0x04, 0x2d, 0xa3, 0xde, 0x9a, 0x6a, 0xad, 0xad, 0x2f, 0x44, 0xc0, 0x89, 0x98,
|
||||||
0xbf, 0xcb, 0x9b, 0xb0, 0xe6, 0x9f, 0x9e, 0x0a, 0x2a, 0xa3, 0xec, 0x46, 0x94, 0xaa, 0x82, 0x60,
|
0xf5, 0x87, 0x01, 0xdb, 0xf3, 0xd8, 0x6d, 0xee, 0xd2, 0xef, 0xdf, 0x22, 0x57, 0xef, 0xee, 0xf2,
|
||||||
0xdf, 0xd0, 0xe8, 0x2f, 0x93, 0x3e, 0xcf, 0xf7, 0x48, 0x2e, 0xe9, 0x11, 0xeb, 0x77, 0x03, 0xb6,
|
0x06, 0xac, 0xf8, 0x27, 0x27, 0x82, 0xca, 0x28, 0xbb, 0x11, 0xa5, 0xaa, 0x20, 0xd8, 0x0f, 0x34,
|
||||||
0x56, 0x44, 0x81, 0x5e, 0x40, 0x31, 0xfa, 0x82, 0x8c, 0x77, 0xfd, 0xbd, 0x8b, 0x7c, 0xd4, 0x4a,
|
0xfa, 0x37, 0xa5, 0xcf, 0xb3, 0x3d, 0x92, 0x4b, 0x7a, 0xc4, 0xfa, 0xcb, 0x80, 0xcd, 0x25, 0x51,
|
||||||
0x8d, 0x88, 0x88, 0xd6, 0x7e, 0x02, 0xb0, 0x7d, 0x0a, 0xd5, 0x99, 0xab, 0x25, 0x5b, 0xf4, 0xf1,
|
0xa0, 0x67, 0x50, 0x8c, 0x5e, 0x90, 0xf1, 0xae, 0xbf, 0x7b, 0x91, 0x8f, 0x5a, 0xa9, 0x11, 0x11,
|
||||||
0xec, 0x16, 0xbd, 0xfb, 0x56, 0x63, 0x49, 0x56, 0xa6, 0x5b, 0xf5, 0x69, 0xf5, 0x4d, 0xb9, 0x71,
|
0xd1, 0xda, 0x4f, 0x00, 0xb6, 0x4e, 0xa0, 0x3a, 0xf5, 0x69, 0xc1, 0x16, 0x7d, 0x38, 0xbd, 0x45,
|
||||||
0xef, 0xc3, 0x58, 0xb3, 0xb7, 0xa6, 0x4f, 0x0f, 0xfe, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x35, 0x39,
|
0xef, 0xbc, 0xd1, 0x58, 0x92, 0x95, 0xc9, 0x56, 0x7d, 0x5c, 0x7d, 0x5d, 0x6e, 0xdc, 0xfd, 0x34,
|
||||||
0x1e, 0x9e, 0xde, 0x0e, 0x00, 0x00,
|
0xd6, 0x3c, 0x5e, 0xd1, 0xa7, 0xfb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x01, 0x61, 0xde, 0x61,
|
||||||
|
0xf9, 0x0e, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ message CommunityMember {
|
||||||
UNKNOWN_ROLE = 0;
|
UNKNOWN_ROLE = 0;
|
||||||
ROLE_ALL = 1;
|
ROLE_ALL = 1;
|
||||||
ROLE_MANAGE_USERS = 2;
|
ROLE_MANAGE_USERS = 2;
|
||||||
|
ROLE_MODERATE_CONTENT = 3;
|
||||||
}
|
}
|
||||||
repeated Roles roles = 1;
|
repeated Roles roles = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -472,6 +472,14 @@ func (api *PublicAPI) UnbanUserFromCommunity(request *requests.UnbanUserFromComm
|
||||||
return api.service.messenger.UnbanUserFromCommunity(request)
|
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
|
// MyPendingRequestsToJoin returns the pending requests for the logged in user
|
||||||
func (api *PublicAPI) MyPendingRequestsToJoin() ([]*communities.RequestToJoin, error) {
|
func (api *PublicAPI) MyPendingRequestsToJoin() ([]*communities.RequestToJoin, error) {
|
||||||
return api.service.messenger.MyPendingRequestsToJoin()
|
return api.service.messenger.MyPendingRequestsToJoin()
|
||||||
|
|
Loading…
Reference in New Issue