Respond to urn:xmpp:time (XEP-0202) queries

This commit is contained in:
Bohdan Horbeshko 2025-10-18 16:42:40 -04:00
parent aaca93e66d
commit 78a4305872
4 changed files with 84 additions and 5 deletions

View file

@ -667,11 +667,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool,
state = &client.MessageSchedulingStateSendWhenOnline{}
result = due
} else {
if c.Session.Timezone == "" {
due += "Z"
} else {
due += c.Session.Timezone
}
due += c.GetTZD()
switch 0 {
default:

View file

@ -1792,6 +1792,13 @@ func (c *Client) usernamesToString(usernames []string) string {
return strings.Join(atUsernames, ", ")
}
func (c *Client) GetTZD() string {
if c.Session.Timezone == "" {
return "Z"
}
return c.Session.Timezone
}
// GetChatMembers retrieves a list of chat members. "Limited" mode works only if there are no more than 20 members at all
func (c *Client) GetChatMembers(chatID int64, limited bool, query string, membersList MembersList) ([]*client.ChatMember, error) {
var filters []client.ChatMembersFilter

View file

@ -213,6 +213,14 @@ type QueryRegisterRemove struct {
XMLName xml.Name `xml:"remove"`
}
// EntityTime is from XEP-0202
type EntityTime struct {
XMLName xml.Name `xml:"urn:xmpp:time time"`
Tzo string `xml:"tzo"`
Utc string `xml:"utc"`
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
}
// Namespace is a namespace!
func (c PresenceNickExtension) Namespace() string {
return c.XMLName.Space
@ -278,6 +286,16 @@ func (c QueryRegister) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// Namespace is a namespace!
func (c EntityTime) Namespace() string {
return c.XMLName.Space
}
// GetSet getsets!
func (c EntityTime) GetSet() *stanza.ResultSet {
return c.ResultSet
}
// Name is a packet name
func (ClientMessage) Name() string {
return "message"
@ -362,4 +380,10 @@ func init() {
"jabber:iq:register",
"query",
}, QueryRegister{})
// entity time
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:time",
"time",
}, EntityTime{})
}

View file

@ -8,6 +8,7 @@ import (
"strconv"
"strings"
"sync"
"time"
"dev.narayana.im/narayana/telegabber/persistence"
"dev.narayana.im/narayana/telegabber/telegram"
@ -80,6 +81,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
go handleGetVersion(s, iq)
return
}
_, ok = iq.Payload.(*extensions.EntityTime)
if ok {
go handleGetEntityTime(s, iq)
return
}
} else if iq.Type == stanza.IQTypeSet {
query, ok := iq.Payload.(*extensions.QueryRegister)
if ok {
@ -649,6 +655,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
}
disco.AddFeatures(gateway.NSCommand)
disco.AddFeatures("jabber:iq:version")
disco.AddFeatures("urn:xmpp:time")
} else {
chatType, chatTypeErr := getTelegramChatType(iq.From, iq.To)
@ -833,6 +840,51 @@ func handleGetVersion(s xmpp.Sender, iq *stanza.IQ) {
_ = gateway.ResumableSend(component, answer)
}
func handleGetEntityTime(s xmpp.Sender, iq *stanza.IQ) {
component, ok := s.(*xmpp.Component)
if !ok {
log.Error("Not a component")
return
}
// separate declaration is crucial for passing as pointer to defer
var answer *stanza.IQ
var err error
answer, err = stanza.NewIQ(stanza.Attrs{
Type: stanza.IQTypeResult,
From: iq.To,
To: iq.From,
Id: iq.Id,
Lang: "en",
})
if err != nil {
log.Errorf("Failed to create answer IQ: %v", err)
return
}
defer gateway.ResumableSend(component, answer)
fromJid, err := stanza.NewJid(iq.From)
if err != nil {
log.Error("Invalid from JID!")
return
}
session, ok := sessions[fromJid.Bare()]
if !ok {
log.Error("IQ from stranger")
return
}
entityTime := extensions.EntityTime{
Tzo: session.GetTZD(),
Utc: time.Now().UTC().Format(time.RFC3339),
}
answer.Payload = &entityTime
log.Debugf("%#v", entityTime)
}
func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryRegister) {
component, ok := s.(*xmpp.Component)
if !ok {