Process MUC deletion

This commit is contained in:
Bohdan Horbeshko 2025-06-22 10:37:20 -04:00
parent f18fded3e9
commit 0b1e204912
5 changed files with 152 additions and 78 deletions

View file

@ -980,9 +980,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool,
} }
// leave current chat (for owners) // leave current chat (for owners)
case "leave!": case "leave!":
_, err := c.client.DeleteChat(&client.DeleteChatRequest{ err := c.DeleteChat(chatID)
ChatId: chatID,
})
if err != nil { if err != nil {
return err.Error(), true, false return err.Error(), true, false
} }

View file

@ -588,6 +588,39 @@ func (c *Client) LeaveMUC(chatId int64, resource string) {
} }
} }
// DestroyMUC removes everyone from the MUC
func (c *Client) DestroyMUC(chatId int64) error {
err := c.DeleteChat(chatId)
if err != nil {
return err
}
c.locks.mucCacheLock.Lock()
defer c.locks.mucCacheLock.Unlock()
mucState, ok := c.mucCache[chatId]
if !ok || mucState == nil {
return nil
}
var toResources []string
for resource := range mucState.Resources {
toResources = append(toResources, resource)
}
c.sendPresence(
gateway.SPFrom(gateway.MUCNODE(chatId)),
gateway.SPResource(c.GetMUCNickname(0)),
gateway.SPToResources(toResources),
gateway.SPMUCAffiliation("none"),
gateway.SPMUCRole("none"),
gateway.SPMUCDestroy(""),
)
delete(c.mucCache, chatId)
return nil
}
func (c *Client) getFullName(user *client.User) string { func (c *Client) getFullName(user *client.User) string {
fullName := user.FirstName fullName := user.FirstName
if user.LastName != "" { if user.LastName != "" {
@ -2710,6 +2743,14 @@ func (c *Client) SetChatPermissions(chatID int64, permissions *client.ChatPermis
return err return err
} }
// DeleteChat is a handy wrapper for the following TDLib method
func (c *Client) DeleteChat(chatID int64) error {
_, err := c.client.DeleteChat(&client.DeleteChatRequest{
ChatId: chatID,
})
return err
}
// CloneChatPermissions makes a copy of ChatPermissions structure // CloneChatPermissions makes a copy of ChatPermissions structure
func CloneChatPermissions(permissions *client.ChatPermissions) *client.ChatPermissions { func CloneChatPermissions(permissions *client.ChatPermissions) *client.ChatPermissions {
return &client.ChatPermissions{ return &client.ChatPermissions{

View file

@ -244,6 +244,7 @@ type MessageXMucUserInviteContinue struct {
type PresenceXMucUserExtension struct { type PresenceXMucUserExtension struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"` XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
Item PresenceXMucUserItem Item PresenceXMucUserItem
Destroy *MucDestroy
Statuses []PresenceXMucUserStatus Statuses []PresenceXMucUserStatus
} }
@ -327,10 +328,18 @@ type QueryMucAdminItem struct {
// QueryMucOwner is from XEP-0045 // QueryMucOwner is from XEP-0045
type QueryMucOwner struct { type QueryMucOwner struct {
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#owner query"` XMLName xml.Name `xml:"http://jabber.org/protocol/muc#owner query"`
Form *stanza.Form `xml:"jabber:x:data x` Form *stanza.Form `xml:"jabber:x:data x,omitempty"`
Destroy *MucDestroy `xml:"destroy,omitempty"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"` ResultSet *stanza.ResultSet `xml:"set,omitempty"`
} }
// MucDestroy is a child element from XEP-0045
type MucDestroy struct {
XMLName xml.Name `xml:"destroy"`
Jid string `xml:"jid,attr,omitempty"`
Reason string `xml:"reason,omitempty"`
}
// Namespace is a namespace! // Namespace is a namespace!
func (c PresenceNickExtension) Namespace() string { func (c PresenceNickExtension) Namespace() string {
return c.XMLName.Space return c.XMLName.Space

View file

@ -450,6 +450,12 @@ var SPMUCJid = args.NewString()
// SPMUCStatusCodes is a set of XEP-0045 MUC status codes // SPMUCStatusCodes is a set of XEP-0045 MUC status codes
var SPMUCStatusCodes = args.New() var SPMUCStatusCodes = args.New()
// SPMUCDestroy is a XEP-0045 room destruction element
var SPMUCDestroy = args.NewString()
// SPToResources achieves to send the presence to certain resources only
var SPToResources = args.New()
func newPresence(bareJid string, to string, args ...args.V) stanza.Presence { func newPresence(bareJid string, to string, args ...args.V) stanza.Presence {
var presenceFrom string var presenceFrom string
if SPFrom.IsSet(args) { if SPFrom.IsSet(args) {
@ -534,6 +540,12 @@ func newPresence(bareJid string, to string, args ...args.V) stanza.Presence {
}) })
} }
} }
if SPMUCDestroy.IsSet(args) {
userExt.Destroy = &extensions.MucDestroy{
Jid: SPMUCDestroy.Get(args),
Reason: "Group was deleted",
}
}
presence.Extensions = append(presence.Extensions, userExt) presence.Extensions = append(presence.Extensions, userExt)
} }
} }
@ -557,29 +569,39 @@ func SendPresence(component *xmpp.Component, to string, args ...args.V) error {
"to": to, "to": to,
}).Info("Got presence") }).Info("Got presence")
presence := newPresence(bareJid, to, args...) var tos []string
if SPToResources.IsSet(args) {
// explicit check, as marshalling is expensive for _, toResource := range SPToResources.Get(args).([]string) {
if log.GetLevel() == log.DebugLevel { tos = append(tos, to + "/" + toResource)
xmlPresence, err := xml.Marshal(presence)
if err == nil {
log.Debug(string(xmlPresence))
} else {
log.Debugf("%#v", presence)
}
}
immed := SPImmed.Get(args)
if immed {
err := ResumableSend(component, presence)
if err != nil {
LogBadPresence(&presence)
return err
} }
} else { } else {
QueueLock.Lock() tos = []string{to}
Queue[presence.From+presence.To] = &presence }
QueueLock.Unlock() for _, to := range tos {
presence := newPresence(bareJid, to, args...)
// explicit check, as marshalling is expensive
if log.GetLevel() == log.DebugLevel {
xmlPresence, err := xml.Marshal(presence)
if err == nil {
log.Debug(string(xmlPresence))
} else {
log.Debugf("%#v", presence)
}
}
immed := SPImmed.Get(args)
if immed {
err := ResumableSend(component, presence)
if err != nil {
LogBadPresence(&presence)
return err
}
} else {
QueueLock.Lock()
Queue[presence.From+presence.To] = &presence
QueueLock.Unlock()
}
} }
return nil return nil

View file

@ -1899,69 +1899,72 @@ func handleSetQueryMucOwner(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
return return
} }
if query.Form == nil { if query.Form != nil {
iqAnswerSetError(answer, 400) switch query.Form.Type {
return case stanza.FormTypeSubmit:
} // okay, noop
case stanza.FormTypeCancel:
switch query.Form.Type { return
case stanza.FormTypeSubmit: default:
// okay, noop iqAnswerSetError(answer, 400)
case stanza.FormTypeCancel: return
return }
default:
iqAnswerSetError(answer, 400)
return
}
err = nil
for _, field := range query.Form.Fields {
switch field.Var {
case "muc#roomconfig_roomname", "muc#roomconfig_roomdesc", "muc#roomconfig_changesubject":
if len(field.ValuesList) != 1 {
iqAnswerSetError(answer, 400)
return
}
value := field.ValuesList[0]
err = nil
for _, field := range query.Form.Fields {
switch field.Var { switch field.Var {
case "muc#roomconfig_roomname": case "muc#roomconfig_roomname", "muc#roomconfig_roomdesc", "muc#roomconfig_changesubject":
if value == "" { if len(field.ValuesList) != 1 {
iqAnswerSetError(answer, 400) iqAnswerSetError(answer, 400)
return return
} }
if value != chat.Title {
err = session.SetChatTitle(toID, value)
if err != nil {
break
}
}
case "muc#roomconfig_roomdesc":
if value != session.GetChatDescription(chat) {
err = session.SetChatDescription(toID, value)
if err != nil {
break
}
}
case "muc#roomconfig_changesubject":
b, ok := ToBool(value)
if !ok {
iqAnswerSetError(answer, 400)
return
}
permissions := chat.Permissions
if permissions != nil && b != permissions.CanPinMessages {
newPermissions := telegram.CloneChatPermissions(permissions)
newPermissions.CanPinMessages = b
err = session.SetChatPermissions(toID, newPermissions) value := field.ValuesList[0]
if err != nil {
break switch field.Var {
case "muc#roomconfig_roomname":
if value == "" {
iqAnswerSetError(answer, 400)
return
}
if value != chat.Title {
err = session.SetChatTitle(toID, value)
if err != nil {
break
}
}
case "muc#roomconfig_roomdesc":
if value != session.GetChatDescription(chat) {
err = session.SetChatDescription(toID, value)
if err != nil {
break
}
}
case "muc#roomconfig_changesubject":
b, ok := ToBool(value)
if !ok {
iqAnswerSetError(answer, 400)
return
}
permissions := chat.Permissions
if permissions != nil && b != permissions.CanPinMessages {
newPermissions := telegram.CloneChatPermissions(permissions)
newPermissions.CanPinMessages = b
err = session.SetChatPermissions(toID, newPermissions)
if err != nil {
break
}
} }
} }
} }
} }
} else if query.Destroy != nil {
err = session.DestroyMUC(toID)
} else {
// per 1.0 spec version, it also could be a destruction, but too dangerous to cover probably
iqAnswerSetError(answer, 400)
return
} }
if err != nil { if err != nil {
@ -1973,6 +1976,7 @@ func handleSetQueryMucOwner(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) {