Room configuration form

This commit is contained in:
Bohdan Horbeshko 2025-06-18 01:46:53 -04:00
parent cef56ca484
commit f18fded3e9
3 changed files with 244 additions and 5 deletions

View file

@ -2683,6 +2683,53 @@ func (c *Client) SetChatMemberStatus(chatID, userID int64, status ChatMemberStat
return err return err
} }
// SetChatTitle is a handy wrapper for the following TDLib method
func (c *Client) SetChatTitle(chatID int64, title string) error {
_, err := c.client.SetChatTitle(&client.SetChatTitleRequest{
ChatId: chatID,
Title: title,
})
return err
}
// SetChatDescription is a handy wrapper for the following TDLib method
func (c *Client) SetChatDescription(chatID int64, description string) error {
_, err := c.client.SetChatDescription(&client.SetChatDescriptionRequest{
ChatId: chatID,
Description: description,
})
return err
}
// SetChatPermissions is a handy wrapper for the following TDLib method
func (c *Client) SetChatPermissions(chatID int64, permissions *client.ChatPermissions) error {
_, err := c.client.SetChatPermissions(&client.SetChatPermissionsRequest{
ChatId: chatID,
Permissions: permissions,
})
return err
}
// CloneChatPermissions makes a copy of ChatPermissions structure
func CloneChatPermissions(permissions *client.ChatPermissions) *client.ChatPermissions {
return &client.ChatPermissions{
CanSendBasicMessages: permissions.CanSendBasicMessages,
CanSendAudios: permissions.CanSendAudios,
CanSendDocuments: permissions.CanSendDocuments,
CanSendPhotos: permissions.CanSendPhotos,
CanSendVideos: permissions.CanSendVideos,
CanSendVideoNotes: permissions.CanSendVideoNotes,
CanSendVoiceNotes: permissions.CanSendVoiceNotes,
CanSendPolls: permissions.CanSendPolls,
CanSendOtherMessages: permissions.CanSendOtherMessages,
CanAddWebPagePreviews: permissions.CanAddWebPagePreviews,
CanChangeInfo: permissions.CanChangeInfo,
CanInviteUsers: permissions.CanInviteUsers,
CanPinMessages: permissions.CanPinMessages,
CanManageTopics: permissions.CanManageTopics,
}
}
func (c *Client) mucOccupantRolePresence(chatID, userID int64, status ChatMemberStatus, nickname string) { func (c *Client) mucOccupantRolePresence(chatID, userID int64, status ChatMemberStatus, nickname string) {
args := []args.V{ args := []args.V{
gateway.SPFrom(gateway.MUCNODE(chatID)), gateway.SPFrom(gateway.MUCNODE(chatID)),

View file

@ -327,6 +327,7 @@ 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`
ResultSet *stanza.ResultSet `xml:"set,omitempty"` ResultSet *stanza.ResultSet `xml:"set,omitempty"`
} }

View file

@ -26,6 +26,8 @@ const (
TypeVCard4 TypeVCard4
) )
const MUC_DEFAULT_HISTORY_LIMIT int32 = 20
func logPacketType(p stanza.Packet) { func logPacketType(p stanza.Packet) {
log.Warnf("Ignoring packet: %T\n", p) log.Warnf("Ignoring packet: %T\n", p)
} }
@ -97,6 +99,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
go handleSetQueryMucAdmin(s, iq, queryMucAdmin) go handleSetQueryMucAdmin(s, iq, queryMucAdmin)
return return
} }
queryMucOwner, ok := iq.Payload.(*extensions.QueryMucOwner)
if ok {
go handleSetQueryMucOwner(s, iq, queryMucOwner)
return
}
} else if iq.Type == stanza.IQTypeResult { } else if iq.Type == stanza.IQTypeResult {
discoInfo, ok := iq.Payload.(*stanza.DiscoInfo) discoInfo, ok := iq.Payload.(*stanza.DiscoInfo)
if ok { if ok {
@ -603,7 +610,7 @@ func handleMUCPresence(s xmpp.Sender, p stanza.Presence, mucExt stanza.MucPresen
} else if !mucExt.History.Since.IsZero() { } else if !mucExt.History.Since.IsZero() {
limit = telegram.NewMessageLimitSince(mucExt.History.Since.Unix()) limit = telegram.NewMessageLimitSince(mucExt.History.Since.Unix())
} else { } else {
limit = telegram.NewMessageLimitMessages(20) limit = telegram.NewMessageLimitMessages(MUC_DEFAULT_HISTORY_LIMIT)
} }
session.JoinMUC(chatId, fromResource, limit) session.JoinMUC(chatId, fromResource, limit)
} }
@ -1181,6 +1188,12 @@ func handleGetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
return return
} }
chat, _, err := session.GetContactByID(toID, nil)
if err != nil || !session.IsGroup(chat) {
iqAnswerSetError(answer, 405)
return
}
if len(query.Items) != 1 { if len(query.Items) != 1 {
iqAnswerSetError(answer, 400) iqAnswerSetError(answer, 400)
return return
@ -1216,8 +1229,6 @@ func handleGetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
members, err := session.GetChatMembers(toID, false, "", membersList) members, err := session.GetChatMembers(toID, false, "", membersList)
if err == nil { if err == nil {
chat, _, _ := session.GetContactByID(toID, nil)
for _, member := range members { for _, member := range members {
senderId, nickname, affiliation, role := session.TgMemberToMUCOccupant(member, chat) senderId, nickname, affiliation, role := session.TgMemberToMUCOccupant(member, chat)
if item.Role != "" && role != item.Role { if item.Role != "" && role != item.Role {
@ -1241,8 +1252,69 @@ func handleGetQueryMucOwner(s xmpp.Sender, iq *stanza.IQ) {
if !ok { if !ok {
return 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, 405) iqAnswerSetError(answer, 405)
gateway.ResumableSend(component, answer) return
}
chat, _, err := session.GetContactByID(toID, nil)
if err != nil || chat == nil || !session.IsGroup(chat) {
iqAnswerSetError(answer, 405)
return
}
payload := &extensions.QueryMucOwner{}
answer.Payload = payload
dummyString := ""
changeSubject := "1"
if chat.Permissions != nil && !chat.Permissions.CanPinMessages {
changeSubject = "0"
}
payload.Form = &stanza.Form{
Type: stanza.FormTypeForm,
Title: fmt.Sprintf("Configuration for \"%v\" room", chat.Title),
Fields: []*stanza.Field{
&stanza.Field{
Var: "FORM_TYPE",
Type: stanza.FieldTypeHidden,
ValuesList: []string{"http://jabber.org/protocol/muc#roominfo"},
},
&stanza.Field{
Var: "muc#roomconfig_roomname",
Label: "Group name",
Required: &dummyString,
ValuesList: []string{chat.Title},
},
&stanza.Field{
Var: "muc#roomconfig_roomdesc",
Label: "Description (optional)",
ValuesList: []string{session.GetChatDescription(chat)},
},
&stanza.Field{
Var: "muc#roomconfig_changesubject",
Label: "Allow Occupants to Change Subject?",
Type: stanza.FieldTypeBool,
ValuesList: []string{changeSubject},
},
},
}
} }
func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryRegister) { func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryRegister) {
@ -1796,6 +1868,113 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
} }
} }
func handleSetQueryMucOwner(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryMucOwner) {
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, 405)
return
}
chat, _, err := session.GetContactByID(toID, nil)
if err != nil || chat == nil || !session.IsGroup(chat) {
iqAnswerSetError(answer, 405)
return
}
if query.Form == nil {
iqAnswerSetError(answer, 400)
return
}
switch query.Form.Type {
case stanza.FormTypeSubmit:
// okay, noop
case stanza.FormTypeCancel:
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]
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
}
}
}
}
}
if err != nil {
code, ok := telegram.GetErrorCode(err)
if !ok {
code = 500
}
iqAnswerSetError(answer, int(code))
answer.Error.Text = err.Error()
return
}
}
func iqAnswerSetError(answer *stanza.IQ, code int) { func iqAnswerSetError(answer *stanza.IQ, code int) {
iqAnswerSetErrorInternal(answer, code, false) iqAnswerSetErrorInternal(answer, code, false)
} }
@ -2124,3 +2303,15 @@ func makeVCardPayload(typ byte, id string, info telegram.VCardInfo, session *tel
return nil return nil
} }
// ToBool returns bool, ok
func ToBool(b string) (bool, bool) {
switch b {
case "0", "false":
return false, true
case "1", "true":
return true, true
}
return false, false
}