power levels: bridge rights for group chats

Signed-off-by: Sumner Evans <sumner.evans@automattic.com>
This commit is contained in:
Sumner Evans
2024-10-02 22:57:56 -06:00
parent 1c7e626c97
commit 7d9836c86b
4 changed files with 215 additions and 11 deletions
+151 -10
View File
@@ -10,11 +10,68 @@ import (
"maunium.net/go/mautrix/bridgev2"
"maunium.net/go/mautrix/bridgev2/database"
"maunium.net/go/mautrix/bridgev2/networkid"
"maunium.net/go/mautrix/event"
"go.mau.fi/mautrix-telegram/pkg/connector/ids"
"go.mau.fi/mautrix-telegram/pkg/connector/media"
)
var (
anyonePowerLevel = ptr.Ptr(0)
modPowerLevel = ptr.Ptr(50)
superadminPowerLevel = ptr.Ptr(75)
creatorPowerLevel = ptr.Ptr(95)
otherPowerLevel = ptr.Ptr(40)
anonymousPowerLevel = ptr.Ptr(41)
postMessagesPowerLevel = ptr.Ptr(42)
editMessagesPowerLevel = ptr.Ptr(43)
deleteMessagesPowerLevel = ptr.Ptr(44)
postStoriesPowerLevel = ptr.Ptr(45)
editStoriesPowerLevel = ptr.Ptr(46)
deleteStoriesPowerLevel = ptr.Ptr(47)
changeInfoPowerLevel = ptr.Ptr(50)
inviteUsersPowerLevel = ptr.Ptr(51)
manageCallPowerLevel = ptr.Ptr(52)
pinMessagesPowerLevel = ptr.Ptr(53)
manageTopicsPowerLevel = ptr.Ptr(54)
banUsersPowerLevel = ptr.Ptr(55)
addAdminsPowerLevel = ptr.Ptr(60)
)
func adminRightsToPowerLevel(rights tg.ChatAdminRights) *int {
if rights.AddAdmins {
return addAdminsPowerLevel
} else if rights.BanUsers {
return banUsersPowerLevel
} else if rights.ManageTopics {
return manageTopicsPowerLevel
} else if rights.PinMessages {
return pinMessagesPowerLevel
} else if rights.ManageCall {
return manageCallPowerLevel
} else if rights.InviteUsers {
return inviteUsersPowerLevel
} else if rights.ChangeInfo {
return changeInfoPowerLevel
} else if rights.DeleteStories {
return deleteStoriesPowerLevel
} else if rights.EditStories {
return editStoriesPowerLevel
} else if rights.PostStories {
return postStoriesPowerLevel
} else if rights.DeleteMessages {
return deleteMessagesPowerLevel
} else if rights.EditMessages {
return editMessagesPowerLevel
} else if rights.PostMessages {
return postMessagesPowerLevel
} else if rights.Anonymous {
return anonymousPowerLevel
}
return otherPowerLevel
}
func (t *TelegramClient) getDMChatInfo(userID int64) (*bridgev2.ChatInfo, error) {
networkUserID := ids.MakeUserID(userID)
chatInfo := bridgev2.ChatInfo{
@@ -100,15 +157,28 @@ func (t *TelegramClient) avatarFromPhoto(photo tg.PhotoClass) *bridgev2.Avatar {
}
}
func (t *TelegramClient) filterChannelParticipants(chatParticipants []tg.ChannelParticipantClass, limit int) (members []bridgev2.ChatMember) {
for _, u := range chatParticipants {
userIDable, ok := u.(interface{ GetUserID() int64 })
if !ok {
func (t *TelegramClient) filterChannelParticipants(participants []tg.ChannelParticipantClass, limit int) (members []bridgev2.ChatMember) {
for _, u := range participants {
var userID int64
var powerLevel *int
switch participant := u.(type) {
case *tg.ChannelParticipant:
userID = participant.GetUserID()
case *tg.ChannelParticipantSelf:
userID = participant.GetUserID()
case *tg.ChannelParticipantCreator:
userID = participant.GetUserID()
powerLevel = creatorPowerLevel
case *tg.ChannelParticipantAdmin:
userID = participant.GetUserID()
powerLevel = adminRightsToPowerLevel(participant.AdminRights)
default:
continue
}
members = append(members, bridgev2.ChatMember{
EventSender: t.senderForUserID(userIDable.GetUserID()),
EventSender: t.senderForUserID(userID),
PowerLevel: powerLevel,
})
if len(members) >= limit {
@@ -163,8 +233,17 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
continue
}
var powerLevel *int
switch user.(type) {
case *tg.ChatParticipantCreator:
powerLevel = creatorPowerLevel
case *tg.ChatParticipantAdmin:
powerLevel = modPowerLevel
}
chatInfo.Members.MemberMap[ids.MakeUserID(user.GetUserID())] = bridgev2.ChatMember{
EventSender: t.senderForUserID(user.GetUserID()),
PowerLevel: powerLevel,
}
if len(chatInfo.Members.MemberMap) >= t.main.Config.MemberList.NormalizedMaxInitialSync() {
@@ -207,14 +286,13 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
// TODO save emojiset?
chatInfo.Members.IsFull = false
chatInfo.Members.PowerLevels = t.getGroupChatPowerLevels(fullChat.GetChats()[0])
if !portal.Metadata.(*PortalMetadata).IsSuperGroup {
// Add the channel user
sender := ids.MakeUserID(id)
sender := ids.MakeChannelUserID(id)
chatInfo.Members.MemberMap[sender] = bridgev2.ChatMember{
EventSender: bridgev2.EventSender{
SenderLogin: ids.MakeUserLoginID(id),
Sender: sender,
},
EventSender: bridgev2.EventSender{Sender: sender},
PowerLevel: superadminPowerLevel,
}
}
@@ -298,3 +376,66 @@ func (t *TelegramClient) GetChatInfo(ctx context.Context, portal *bridgev2.Porta
panic(fmt.Sprintf("unsupported peer type %s", peerType))
}
}
func (t *TelegramClient) getGroupChatPowerLevels(entity tg.ChatClass) *bridgev2.PowerLevelOverrides {
dbrAble, ok := entity.(interface {
GetDefaultBannedRights() (tg.ChatBannedRights, bool)
})
if !ok {
panic(fmt.Sprintf("unsupported chat type %T", entity))
}
dbr, ok := dbrAble.GetDefaultBannedRights()
if !ok {
dbr = tg.ChatBannedRights{
InviteUsers: true,
ChangeInfo: true,
PinMessages: true,
SendStickers: false,
SendMessages: false,
}
}
return t.getPowerLevelOverridesFromBannedRights(entity, dbr)
}
func (t *TelegramClient) getPowerLevelOverridesFromBannedRights(entity tg.ChatClass, dbr tg.ChatBannedRights) *bridgev2.PowerLevelOverrides {
var plo bridgev2.PowerLevelOverrides
plo.Ban = banUsersPowerLevel
plo.Kick = banUsersPowerLevel
plo.Redact = deleteMessagesPowerLevel
if dbr.InviteUsers {
plo.Invite = inviteUsersPowerLevel
} else {
plo.Invite = anyonePowerLevel
}
plo.StateDefault = superadminPowerLevel
plo.UsersDefault = anyonePowerLevel
if c, ok := entity.(*tg.Channel); (ok && !c.Megagroup) || dbr.SendMessages {
plo.EventsDefault = postMessagesPowerLevel
} else {
plo.EventsDefault = anyonePowerLevel
}
plo.Events = map[event.Type]int{
event.StateEncryption: 99,
event.StateTombstone: 99,
event.StatePowerLevels: 85,
event.StateHistoryVisibility: 85,
}
if dbr.ChangeInfo {
plo.Events[event.StateRoomName] = *changeInfoPowerLevel
plo.Events[event.StateRoomAvatar] = *changeInfoPowerLevel
plo.Events[event.StateTopic] = *changeInfoPowerLevel
}
if dbr.PinMessages {
plo.Events[event.StatePinnedEvents] = *pinMessagesPowerLevel
} else {
plo.Events[event.StatePinnedEvents] = 0
}
if dbr.SendStickers {
plo.Events[event.EventSticker] = *postMessagesPowerLevel
}
return &plo
}
+3
View File
@@ -186,6 +186,9 @@ func NewTelegramClient(ctx context.Context, tc *TelegramConnector, login *bridge
dispatcher.OnPinnedDialogs(func(ctx context.Context, e tg.Entities, update *tg.UpdatePinnedDialogs) error {
return client.onPinnedDialogs(ctx, update)
})
dispatcher.OnChatDefaultBannedRights(func(ctx context.Context, e tg.Entities, update *tg.UpdateChatDefaultBannedRights) error {
return client.onChatDefaultBannedRights(ctx, e, update)
})
client.ScopedStore = tc.Store.GetScopedStore(telegramUserID)
+41 -1
View File
@@ -66,6 +66,10 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
for _, user := range dialogs.GetUsers() {
users[ids.MakeUserID(user.GetID())] = user
}
chats := map[int64]tg.ChatClass{}
for _, chat := range dialogs.GetChats() {
chats[chat.GetID()] = chat
}
messages := map[networkid.MessageID]tg.MessageClass{}
for _, message := range dialogs.GetMessages() {
messages[ids.GetMessageIDFromMessage(message)] = message
@@ -99,6 +103,38 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
}
}
var members bridgev2.ChatMemberList
switch peer := dialog.Peer.(type) {
case *tg.PeerUser:
if users[ids.MakeUserID(peer.UserID)].(*tg.User).GetDeleted() {
log.Debug().Msg("Not syncing portal because user is deleted")
continue
}
case *tg.PeerChat:
members.PowerLevels = t.getGroupChatPowerLevels(chats[peer.ChatID])
case *tg.PeerChannel:
members.PowerLevels = t.getGroupChatPowerLevels(chats[peer.ChannelID])
if !portal.Metadata.(*PortalMetadata).IsSuperGroup {
// Add the channel user
sender := ids.MakeChannelUserID(peer.ChannelID)
members.MemberMap = map[networkid.UserID]bridgev2.ChatMember{
sender: bridgev2.ChatMember{
EventSender: bridgev2.EventSender{Sender: sender},
PowerLevel: modPowerLevel,
},
}
if chats[peer.ChannelID].(*tg.Channel).AdminRights.PostMessages {
members.MemberMap = map[networkid.UserID]bridgev2.ChatMember{
t.userID: bridgev2.ChatMember{
EventSender: t.mySender(),
PowerLevel: modPowerLevel,
},
}
}
}
}
if portal == nil || portal.MXID == "" {
// Check what the latest message is
topMessage := messages[ids.MakeMessageID(dialog.Peer, dialog.TopMessage)]
@@ -127,7 +163,11 @@ func (t *TelegramClient) handleDialogs(ctx context.Context, dialogs tg.ModifiedM
}
t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{
ChatInfo: &bridgev2.ChatInfo{Name: &portal.Name, UserLocal: &userLocalInfo},
ChatInfo: &bridgev2.ChatInfo{
Name: &portal.Name,
UserLocal: &userLocalInfo,
Members: &members,
},
EventMeta: simplevent.EventMeta{
Type: bridgev2.RemoteEventChatResync,
LogContext: func(c zerolog.Context) zerolog.Context {
+20
View File
@@ -224,6 +224,7 @@ func (t *TelegramClient) onUpdateNewMessage(ctx context.Context, channels map[in
case *tg.MessageActionChannelCreate:
eventMeta.Type = bridgev2.RemoteEventChatResync
eventMeta.CreatePortal = true
modLevel := 50
t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{
EventMeta: eventMeta,
ChatInfo: &bridgev2.ChatInfo{
@@ -233,8 +234,12 @@ func (t *TelegramClient) onUpdateNewMessage(ctx context.Context, channels map[in
t.userID: {
EventSender: t.mySender(),
Membership: event.MembershipJoin,
PowerLevel: &modLevel,
},
},
PowerLevels: &bridgev2.PowerLevelOverrides{
EventsDefault: &modLevel,
},
},
},
})
@@ -817,3 +822,18 @@ func (t *TelegramClient) HandleRoomTag(ctx context.Context, msg *bridgev2.Matrix
})
return err
}
func (t *TelegramClient) onChatDefaultBannedRights(ctx context.Context, entities tg.Entities, update *tg.UpdateChatDefaultBannedRights) error {
t.main.Bridge.QueueRemoteEvent(t.userLogin, &simplevent.ChatResync{
ChatInfo: &bridgev2.ChatInfo{
Members: &bridgev2.ChatMemberList{
PowerLevels: t.getPowerLevelOverridesFromBannedRights(entities.Chats[0], update.DefaultBannedRights),
},
},
EventMeta: simplevent.EventMeta{
Type: bridgev2.RemoteEventChatResync,
PortalKey: t.makePortalKeyFromPeer(update.Peer),
},
})
return nil
}