2023-07-13 15:25:43 +00:00
|
|
|
package communities
|
|
|
|
|
2023-10-18 17:01:02 +00:00
|
|
|
import (
|
|
|
|
"crypto/ecdsa"
|
|
|
|
|
2024-02-22 10:25:13 +00:00
|
|
|
slices "golang.org/x/exp/slices"
|
|
|
|
|
2023-10-18 17:01:02 +00:00
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
|
|
)
|
2023-07-13 15:25:43 +00:00
|
|
|
|
|
|
|
type CommunityChatChanges struct {
|
|
|
|
ChatModified *protobuf.CommunityChat
|
|
|
|
MembersAdded map[string]*protobuf.CommunityMember
|
|
|
|
MembersRemoved map[string]*protobuf.CommunityMember
|
|
|
|
CategoryModified string
|
|
|
|
PositionModified int
|
|
|
|
FirstMessageTimestampModified uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
type CommunityChanges struct {
|
2023-07-13 17:49:19 +00:00
|
|
|
Community *Community `json:"community"`
|
|
|
|
|
2023-10-18 17:01:02 +00:00
|
|
|
ControlNodeChanged *ecdsa.PublicKey `json:"controlNodeChanged"`
|
|
|
|
|
2024-02-22 10:25:13 +00:00
|
|
|
MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded"`
|
|
|
|
MembersRemoved map[string]*protobuf.CommunityMember `json:"membersRemoved"`
|
|
|
|
MembersBanned map[string]bool `json:"membersBanned"`
|
|
|
|
MembersUnbanned map[string]bool `json:"membersUnbanned"`
|
2023-07-13 15:25:43 +00:00
|
|
|
|
2023-08-17 17:14:23 +00:00
|
|
|
TokenPermissionsAdded map[string]*CommunityTokenPermission `json:"tokenPermissionsAdded"`
|
|
|
|
TokenPermissionsModified map[string]*CommunityTokenPermission `json:"tokenPermissionsModified"`
|
|
|
|
TokenPermissionsRemoved map[string]*CommunityTokenPermission `json:"tokenPermissionsRemoved"`
|
2023-07-13 15:25:43 +00:00
|
|
|
|
|
|
|
ChatsRemoved map[string]*protobuf.CommunityChat `json:"chatsRemoved"`
|
|
|
|
ChatsAdded map[string]*protobuf.CommunityChat `json:"chatsAdded"`
|
|
|
|
ChatsModified map[string]*CommunityChatChanges `json:"chatsModified"`
|
|
|
|
|
|
|
|
CategoriesRemoved []string `json:"categoriesRemoved"`
|
|
|
|
CategoriesAdded map[string]*protobuf.CommunityCategory `json:"categoriesAdded"`
|
|
|
|
CategoriesModified map[string]*protobuf.CommunityCategory `json:"categoriesModified"`
|
|
|
|
|
|
|
|
MemberWalletsRemoved []string `json:"memberWalletsRemoved"`
|
|
|
|
MemberWalletsAdded map[string][]*protobuf.RevealedAccount `json:"memberWalletsAdded"`
|
|
|
|
|
|
|
|
// ShouldMemberJoin indicates whether the user should join this community
|
|
|
|
// automatically
|
|
|
|
ShouldMemberJoin bool `json:"memberAdded"`
|
|
|
|
|
2023-10-27 19:20:08 +00:00
|
|
|
// MemberKicked indicates whether the user has been kicked out
|
|
|
|
MemberKicked bool `json:"memberRemoved"`
|
2023-07-13 15:25:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func EmptyCommunityChanges() *CommunityChanges {
|
|
|
|
return &CommunityChanges{
|
2024-02-22 10:25:13 +00:00
|
|
|
MembersAdded: make(map[string]*protobuf.CommunityMember),
|
|
|
|
MembersRemoved: make(map[string]*protobuf.CommunityMember),
|
|
|
|
MembersBanned: make(map[string]bool),
|
|
|
|
MembersUnbanned: make(map[string]bool),
|
2023-07-13 15:25:43 +00:00
|
|
|
|
2023-08-17 17:14:23 +00:00
|
|
|
TokenPermissionsAdded: make(map[string]*CommunityTokenPermission),
|
|
|
|
TokenPermissionsModified: make(map[string]*CommunityTokenPermission),
|
|
|
|
TokenPermissionsRemoved: make(map[string]*CommunityTokenPermission),
|
2023-07-13 17:49:19 +00:00
|
|
|
|
2023-07-13 15:25:43 +00:00
|
|
|
ChatsRemoved: make(map[string]*protobuf.CommunityChat),
|
|
|
|
ChatsAdded: make(map[string]*protobuf.CommunityChat),
|
|
|
|
ChatsModified: make(map[string]*CommunityChatChanges),
|
|
|
|
|
|
|
|
CategoriesRemoved: []string{},
|
|
|
|
CategoriesAdded: make(map[string]*protobuf.CommunityCategory),
|
|
|
|
CategoriesModified: make(map[string]*protobuf.CommunityCategory),
|
|
|
|
|
|
|
|
MemberWalletsRemoved: []string{},
|
|
|
|
MemberWalletsAdded: make(map[string][]*protobuf.RevealedAccount),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-07 16:30:01 +00:00
|
|
|
func (c *CommunityChanges) Merge(other *CommunityChanges) {
|
|
|
|
for memberID, member := range other.MembersAdded {
|
|
|
|
c.MembersAdded[memberID] = member
|
|
|
|
}
|
|
|
|
for memberID := range other.MembersRemoved {
|
|
|
|
c.MembersRemoved[memberID] = other.MembersRemoved[memberID]
|
|
|
|
}
|
|
|
|
for memberID, banned := range other.MembersBanned {
|
|
|
|
c.MembersBanned[memberID] = banned
|
|
|
|
}
|
|
|
|
for memberID, unbanned := range other.MembersUnbanned {
|
|
|
|
c.MembersUnbanned[memberID] = unbanned
|
|
|
|
}
|
|
|
|
for permissionID, permission := range other.TokenPermissionsAdded {
|
|
|
|
c.TokenPermissionsAdded[permissionID] = permission
|
|
|
|
}
|
|
|
|
for permissionID, permission := range other.TokenPermissionsModified {
|
|
|
|
c.TokenPermissionsModified[permissionID] = permission
|
|
|
|
}
|
|
|
|
for permissionID, permission := range other.TokenPermissionsRemoved {
|
|
|
|
c.TokenPermissionsRemoved[permissionID] = permission
|
|
|
|
}
|
|
|
|
for chatID, chat := range other.ChatsRemoved {
|
|
|
|
c.ChatsRemoved[chatID] = chat
|
|
|
|
}
|
|
|
|
for chatID, chat := range other.ChatsAdded {
|
|
|
|
c.ChatsAdded[chatID] = chat
|
|
|
|
}
|
|
|
|
for chatID, changes := range other.ChatsModified {
|
|
|
|
c.ChatsModified[chatID] = changes
|
|
|
|
}
|
|
|
|
|
|
|
|
c.CategoriesRemoved = append(c.CategoriesRemoved, other.CategoriesRemoved...)
|
|
|
|
|
|
|
|
for categoryID, category := range other.CategoriesAdded {
|
|
|
|
c.CategoriesAdded[categoryID] = category
|
|
|
|
}
|
|
|
|
for categoryID, category := range other.CategoriesModified {
|
|
|
|
c.CategoriesModified[categoryID] = category
|
|
|
|
}
|
|
|
|
|
|
|
|
c.MemberWalletsRemoved = append(c.MemberWalletsRemoved, other.MemberWalletsRemoved...)
|
|
|
|
|
|
|
|
for walletID, wallets := range other.MemberWalletsAdded {
|
|
|
|
c.MemberWalletsAdded[walletID] = wallets
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-13 15:25:43 +00:00
|
|
|
func (c *CommunityChanges) HasNewMember(identity string) bool {
|
|
|
|
if len(c.MembersAdded) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
_, ok := c.MembersAdded[identity]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *CommunityChanges) HasMemberLeft(identity string) bool {
|
|
|
|
if len(c.MembersRemoved) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
_, ok := c.MembersRemoved[identity]
|
|
|
|
return ok
|
|
|
|
}
|
2023-07-13 17:49:19 +00:00
|
|
|
|
2024-02-22 10:25:13 +00:00
|
|
|
func (c *CommunityChanges) IsMemberBanned(identity string) bool {
|
|
|
|
if len(c.MembersBanned) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
_, ok := c.MembersBanned[identity]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *CommunityChanges) IsMemberUnbanned(identity string) bool {
|
|
|
|
if len(c.MembersUnbanned) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
_, ok := c.MembersUnbanned[identity]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2023-08-17 17:14:23 +00:00
|
|
|
func EvaluateCommunityChanges(origin, modified *Community) *CommunityChanges {
|
|
|
|
changes := evaluateCommunityChangesByDescription(origin.Description(), modified.Description())
|
|
|
|
|
2023-10-18 17:01:02 +00:00
|
|
|
if origin.ControlNode() != nil && !modified.ControlNode().Equal(origin.ControlNode()) {
|
|
|
|
changes.ControlNodeChanged = modified.ControlNode()
|
|
|
|
}
|
|
|
|
|
2023-08-17 17:14:23 +00:00
|
|
|
originTokenPermissions := origin.tokenPermissions()
|
|
|
|
modifiedTokenPermissions := modified.tokenPermissions()
|
|
|
|
|
|
|
|
// Check for modified or removed token permissions
|
|
|
|
for id, originPermission := range originTokenPermissions {
|
|
|
|
if modifiedPermission := modifiedTokenPermissions[id]; modifiedPermission != nil {
|
|
|
|
if !modifiedPermission.Equals(originPermission) {
|
|
|
|
changes.TokenPermissionsModified[id] = modifiedPermission
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
changes.TokenPermissionsRemoved[id] = originPermission
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for added token permissions
|
|
|
|
for id, permission := range modifiedTokenPermissions {
|
|
|
|
if _, ok := originTokenPermissions[id]; !ok {
|
|
|
|
changes.TokenPermissionsAdded[id] = permission
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
changes.Community = modified
|
|
|
|
return changes
|
|
|
|
}
|
|
|
|
|
|
|
|
func evaluateCommunityChangesByDescription(origin, modified *protobuf.CommunityDescription) *CommunityChanges {
|
2023-07-13 17:49:19 +00:00
|
|
|
changes := EmptyCommunityChanges()
|
|
|
|
|
|
|
|
// Check for new members at the org level
|
|
|
|
for pk, member := range modified.Members {
|
|
|
|
if _, ok := origin.Members[pk]; !ok {
|
|
|
|
changes.MembersAdded[pk] = member
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-22 10:25:13 +00:00
|
|
|
// Check ban/unban
|
|
|
|
findDiffInBannedMembers(modified.BannedMembers, origin.BannedMembers, changes.MembersBanned)
|
|
|
|
findDiffInBannedMembers(origin.BannedMembers, modified.BannedMembers, changes.MembersUnbanned)
|
|
|
|
|
|
|
|
// Check for new banned members (from deprecated BanList)
|
|
|
|
findDiffInBanList(modified.BanList, origin.BanList, changes.MembersBanned)
|
|
|
|
|
|
|
|
// Check for new unbanned members (from deprecated BanList)
|
|
|
|
findDiffInBanList(origin.BanList, modified.BanList, changes.MembersUnbanned)
|
|
|
|
|
2023-07-13 17:49:19 +00:00
|
|
|
// Check for removed members at the org level
|
|
|
|
for pk, member := range origin.Members {
|
|
|
|
if _, ok := modified.Members[pk]; !ok {
|
|
|
|
changes.MembersRemoved[pk] = member
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for removed chats
|
|
|
|
for chatID, chat := range origin.Chats {
|
|
|
|
if modified.Chats == nil {
|
|
|
|
modified.Chats = make(map[string]*protobuf.CommunityChat)
|
|
|
|
}
|
|
|
|
if _, ok := modified.Chats[chatID]; !ok {
|
|
|
|
changes.ChatsRemoved[chatID] = chat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for chatID, chat := range modified.Chats {
|
|
|
|
if origin.Chats == nil {
|
|
|
|
origin.Chats = make(map[string]*protobuf.CommunityChat)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := origin.Chats[chatID]; !ok {
|
|
|
|
changes.ChatsAdded[chatID] = chat
|
|
|
|
} else {
|
2024-05-15 19:57:12 +00:00
|
|
|
|
2023-07-13 17:49:19 +00:00
|
|
|
// Check for members added
|
|
|
|
for pk, member := range modified.Chats[chatID].Members {
|
|
|
|
if _, ok := origin.Chats[chatID].Members[pk]; !ok {
|
|
|
|
if changes.ChatsModified[chatID] == nil {
|
|
|
|
changes.ChatsModified[chatID] = &CommunityChatChanges{
|
|
|
|
MembersAdded: make(map[string]*protobuf.CommunityMember),
|
|
|
|
MembersRemoved: make(map[string]*protobuf.CommunityMember),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
changes.ChatsModified[chatID].MembersAdded[pk] = member
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for members removed
|
|
|
|
for pk, member := range origin.Chats[chatID].Members {
|
|
|
|
if _, ok := modified.Chats[chatID].Members[pk]; !ok {
|
|
|
|
if changes.ChatsModified[chatID] == nil {
|
|
|
|
changes.ChatsModified[chatID] = &CommunityChatChanges{
|
|
|
|
MembersAdded: make(map[string]*protobuf.CommunityMember),
|
|
|
|
MembersRemoved: make(map[string]*protobuf.CommunityMember),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
changes.ChatsModified[chatID].MembersRemoved[pk] = member
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if first message timestamp was modified
|
|
|
|
if origin.Chats[chatID].Identity.FirstMessageTimestamp !=
|
|
|
|
modified.Chats[chatID].Identity.FirstMessageTimestamp {
|
|
|
|
if changes.ChatsModified[chatID] == nil {
|
|
|
|
changes.ChatsModified[chatID] = &CommunityChatChanges{
|
|
|
|
MembersAdded: make(map[string]*protobuf.CommunityMember),
|
|
|
|
MembersRemoved: make(map[string]*protobuf.CommunityMember),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
changes.ChatsModified[chatID].FirstMessageTimestampModified = modified.Chats[chatID].Identity.FirstMessageTimestamp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for categories that were removed
|
|
|
|
for categoryID := range origin.Categories {
|
|
|
|
if modified.Categories == nil {
|
|
|
|
modified.Categories = make(map[string]*protobuf.CommunityCategory)
|
|
|
|
}
|
|
|
|
|
|
|
|
if modified.Chats == nil {
|
|
|
|
modified.Chats = make(map[string]*protobuf.CommunityChat)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := modified.Categories[categoryID]; !ok {
|
|
|
|
changes.CategoriesRemoved = append(changes.CategoriesRemoved, categoryID)
|
|
|
|
}
|
|
|
|
|
|
|
|
if origin.Chats == nil {
|
|
|
|
origin.Chats = make(map[string]*protobuf.CommunityChat)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for categories that were added
|
|
|
|
for categoryID, category := range modified.Categories {
|
|
|
|
if origin.Categories == nil {
|
|
|
|
origin.Categories = make(map[string]*protobuf.CommunityCategory)
|
|
|
|
}
|
|
|
|
if _, ok := origin.Categories[categoryID]; !ok {
|
|
|
|
changes.CategoriesAdded[categoryID] = category
|
|
|
|
} else {
|
|
|
|
if origin.Categories[categoryID].Name != category.Name || origin.Categories[categoryID].Position != category.Position {
|
|
|
|
changes.CategoriesModified[categoryID] = category
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for chat categories that were modified
|
|
|
|
for chatID, chat := range modified.Chats {
|
|
|
|
if origin.Chats == nil {
|
|
|
|
origin.Chats = make(map[string]*protobuf.CommunityChat)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := origin.Chats[chatID]; !ok {
|
|
|
|
continue // It's a new chat
|
|
|
|
}
|
|
|
|
|
|
|
|
if origin.Chats[chatID].CategoryId != chat.CategoryId {
|
|
|
|
if changes.ChatsModified[chatID] == nil {
|
|
|
|
changes.ChatsModified[chatID] = &CommunityChatChanges{
|
|
|
|
MembersAdded: make(map[string]*protobuf.CommunityMember),
|
|
|
|
MembersRemoved: make(map[string]*protobuf.CommunityMember),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
changes.ChatsModified[chatID].CategoryModified = chat.CategoryId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changes
|
|
|
|
}
|
2024-02-22 10:25:13 +00:00
|
|
|
|
|
|
|
func findDiffInBanList(searchFrom []string, searchIn []string, storeTo map[string]bool) {
|
|
|
|
for _, memberToFind := range searchFrom {
|
|
|
|
if _, stored := storeTo[memberToFind]; stored {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
exists := slices.Contains(searchIn, memberToFind)
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
storeTo[memberToFind] = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func findDiffInBannedMembers(searchFrom map[string]*protobuf.CommunityBanInfo, searchIn map[string]*protobuf.CommunityBanInfo, storeTo map[string]bool) {
|
|
|
|
if searchFrom == nil {
|
|
|
|
return
|
|
|
|
} else if searchIn == nil {
|
|
|
|
for memberToFind, value := range searchFrom {
|
|
|
|
storeTo[memberToFind] = value.DeleteAllMessages
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for memberToFind, value := range searchFrom {
|
|
|
|
if _, exists := searchIn[memberToFind]; !exists {
|
|
|
|
storeTo[memberToFind] = value.DeleteAllMessages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|