Support bulk muc#admin operations on occupantsu

This commit is contained in:
Bohdan Horbeshko 2025-06-15 21:36:03 -04:00
parent cff81d227c
commit b1ecfc29cd
3 changed files with 172 additions and 64 deletions

View file

@ -85,10 +85,12 @@ const (
type MembersList int type MembersList int
const ( const (
MembersListMembers MembersList = iota MembersListNone MembersList = iota
MembersListMembers
MembersListRestricted MembersListRestricted
MembersListBanned MembersListBanned
MembersListBannedAndAdministrators MembersListBannedAndAdministrators
MembersListAdministrators
) )
const ( const (
@ -621,9 +623,7 @@ func (c *Client) updateMUCMembers(mucState *MUCState, chatID int64, members []*c
myRole := "participant" myRole := "participant"
for _, member := range members { for _, member := range members {
senderId := c.GetSenderId(member.MemberId) senderId, nickname, affiliation, role := c.TgMemberToMUCMember(member)
nickname := c.GetMUCNickname(senderId)
affiliation, role := c.memberStatusToAffiliationAndRole(member.Status)
mucState.Members[senderId] = &MUCMember{ mucState.Members[senderId] = &MUCMember{
Nickname: nickname, Nickname: nickname,
Affiliation: affiliation, Affiliation: affiliation,
@ -2485,6 +2485,14 @@ func (c *Client) memberStatusToAffiliationAndRole(memberStatus client.ChatMember
return "member", "participant" return "member", "participant"
} }
// TgMemberToMUCMember resolves useful data to generate a MUC member
func (c *Client) TgMemberToMUCMember(member *client.ChatMember) (senderId int64, nickname, affiliation, role string) {
senderId = c.GetSenderId(member.MemberId)
nickname = c.GetMUCNickname(senderId)
affiliation, role = c.memberStatusToAffiliationAndRole(member.Status)
return
}
func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, plain bool, toJid string) { func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, plain bool, toJid string) {
sChatId := gateway.CHATNODE(chatID) sChatId := gateway.CHATNODE(chatID)
var mucJid string var mucJid string
@ -2540,6 +2548,8 @@ func (c *Client) GetChatMembers(chatID int64, limited bool, query string, member
filters = []client.ChatMembersFilter{&client.ChatMembersFilterBanned{}} filters = []client.ChatMembersFilter{&client.ChatMembersFilterBanned{}}
case MembersListBannedAndAdministrators: case MembersListBannedAndAdministrators:
filters = []client.ChatMembersFilter{&client.ChatMembersFilterBanned{}, &client.ChatMembersFilterAdministrators{}} filters = []client.ChatMembersFilter{&client.ChatMembersFilterBanned{}, &client.ChatMembersFilterAdministrators{}}
case MembersListAdministrators:
filters = []client.ChatMembersFilter{&client.ChatMembersFilterAdministrators{}}
} }
limit := int32(9999) limit := int32(9999)

View file

@ -310,7 +310,7 @@ type EmptySubject struct {
// QueryMucAdmin is from XEP-0045 // QueryMucAdmin is from XEP-0045
type QueryMucAdmin struct { type QueryMucAdmin struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#admin query"` XMLName xml.Name `xml:"http://jabber.org/protocol/muc#admin query"`
Item QueryMucAdminItem `xml:"item"` Items []*QueryMucAdminItem `xml:"item"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"` ResultSet *stanza.ResultSet `xml:"set,omitempty"`
} }

View file

@ -71,6 +71,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
go handleGetQueryRegister(s, iq) go handleGetQueryRegister(s, iq)
return return
} }
queryMucAdmin, ok := iq.Payload.(*extensions.QueryMucAdmin)
if ok {
go handleGetQueryMucAdmin(s, iq, queryMucAdmin)
return
}
} else if iq.Type == stanza.IQTypeSet { } else if iq.Type == stanza.IQTypeSet {
queryRegister, ok := iq.Payload.(*extensions.QueryRegister) queryRegister, ok := iq.Payload.(*extensions.QueryRegister)
if ok { if ok {
@ -1147,6 +1152,84 @@ func handleGetQueryRegister(s xmpp.Sender, iq *stanza.IQ) {
} }
} }
func handleGetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryMucAdmin) {
component, answer, ok := iqResultStub(s, iq)
if !ok {
return
}
defer gateway.ResumableSend(component, answer)
bare, _, fromOk := gateway.SplitJID(iq.From)
if !fromOk {
iqAnswerSetError(answer, 400)
return
}
session, sessionOk := sessions[bare]
if !sessionOk || !session.Session.MUC {
iqAnswerSetError(answer, 403)
return
}
toID, toOk, toIsGroup := toToID(iq.To)
if !toOk || !toIsGroup {
iqAnswerSetError(answer, 406)
return
}
if len(query.Items) != 1 {
iqAnswerSetError(answer, 400)
return
}
item := query.Items[0]
var membersList telegram.MembersList
switch item.Role {
case "moderator":
membersList = telegram.MembersListAdministrators
case "participant":
membersList = telegram.MembersListMembers
}
switch item.Affiliation {
case "owner":
iqAnswerSetError(answer, 403)
return
case "admin":
membersList = telegram.MembersListAdministrators
case "member":
membersList = telegram.MembersListMembers
case "outcast":
membersList = telegram.MembersListBanned
}
if membersList == telegram.MembersListNone {
iqAnswerSetError(answer, 400)
return
}
payload := &extensions.QueryMucAdmin{}
answer.Payload = payload
members, err := session.GetChatMembers(toID, false, "", membersList)
if err == nil {
for _, member := range members {
senderId, nickname, affiliation, role := session.TgMemberToMUCMember(member)
if item.Role != "" && role != item.Role {
continue
}
if item.Affiliation != "" && affiliation != item.Affiliation {
continue
}
payload.Items = append(payload.Items, &extensions.QueryMucAdminItem{
Jid: gateway.CHATJID(senderId, true),
Nick: nickname,
Role: role,
Affiliation: affiliation,
})
}
}
}
func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryRegister) { func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryRegister) {
component, answer, ok := iqResultStub(s, iq) component, answer, ok := iqResultStub(s, iq)
if !ok { if !ok {
@ -1620,27 +1703,36 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
return return
} }
// pre-bake all data to make it transactional as much as possible
type Item struct {
UserID int64
Nick string
Status telegram.ChatMemberStatus
}
var items []Item
for _, item := range query.Items {
if item.Affiliation == "owner" {
iqAnswerSetError(answer, 403)
return
}
var userID int64 var userID int64
if query.Item.Jid != "" { if item.Jid != "" {
userID, _, _ = toToID(query.Item.Jid) userID, _, _ = toToID(item.Jid)
} else if query.Item.Nick != "" { } else if item.Nick != "" {
userID = session.GetMUCMemberIdByNickname(toID, query.Item.Nick) userID = session.GetMUCMemberIdByNickname(toID, item.Nick)
} }
if userID == 0 { if userID == 0 {
iqAnswerSetError(answer, 404) iqAnswerSetError(answer, 404)
return return
} }
nick := query.Item.Nick nick := session.GetMUCNickname(userID)
if nick == "" {
nick = session.GetMUCNickname(userID)
}
var status telegram.ChatMemberStatus var status telegram.ChatMemberStatus
var integerPayload int64
var stringPayload string
switch query.Item.Role { switch item.Role {
case "none": case "none":
status = telegram.ChatMemberStatusKicked status = telegram.ChatMemberStatusKicked
case "visitor": case "visitor":
@ -1651,7 +1743,7 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
status = telegram.ChatMemberStatusPromoted status = telegram.ChatMemberStatusPromoted
} }
// affiliations have a higher priority over roles // affiliations have a higher priority over roles
switch query.Item.Affiliation { switch item.Affiliation {
case "none": case "none":
status = telegram.ChatMemberStatusKicked status = telegram.ChatMemberStatusKicked
case "outcast": case "outcast":
@ -1660,9 +1752,6 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
status = telegram.ChatMemberStatusUnmuted status = telegram.ChatMemberStatusUnmuted
case "admin": case "admin":
status = telegram.ChatMemberStatusPromoted status = telegram.ChatMemberStatusPromoted
case "owner":
iqAnswerSetError(answer, 403)
return
} }
// nothing has been detected // nothing has been detected
@ -1671,7 +1760,15 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
return return
} }
err := session.SetChatMemberStatus(toID, userID, status, integerPayload, stringPayload, nick) items = append(items, Item{
UserID: userID,
Nick: nick,
Status: status,
})
}
for _, item := range items {
err := session.SetChatMemberStatus(toID, item.UserID, item.Status, 0, "", item.Nick)
if err != nil { if err != nil {
code, ok := telegram.GetErrorCode(err) code, ok := telegram.GetErrorCode(err)
if !ok { if !ok {
@ -1681,6 +1778,7 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
answer.Error.Text = err.Error() answer.Error.Text = err.Error()
return return
} }
}
} }
func iqAnswerSetError(answer *stanza.IQ, code int) { func iqAnswerSetError(answer *stanza.IQ, code int) {