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{} state = &client.MessageSchedulingStateSendWhenOnline{}
result = due result = due
} else { } else {
if c.Session.Timezone == "" { due += c.GetTZD()
due += "Z"
} else {
due += c.Session.Timezone
}
switch 0 { switch 0 {
default: default:

View file

@ -1792,6 +1792,13 @@ func (c *Client) usernamesToString(usernames []string) string {
return strings.Join(atUsernames, ", ") 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 // 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) { func (c *Client) GetChatMembers(chatID int64, limited bool, query string, membersList MembersList) ([]*client.ChatMember, error) {
var filters []client.ChatMembersFilter var filters []client.ChatMembersFilter

View file

@ -213,6 +213,14 @@ type QueryRegisterRemove struct {
XMLName xml.Name `xml:"remove"` 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! // Namespace is a namespace!
func (c PresenceNickExtension) Namespace() string { func (c PresenceNickExtension) Namespace() string {
return c.XMLName.Space return c.XMLName.Space
@ -278,6 +286,16 @@ func (c QueryRegister) GetSet() *stanza.ResultSet {
return c.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 // Name is a packet name
func (ClientMessage) Name() string { func (ClientMessage) Name() string {
return "message" return "message"
@ -362,4 +380,10 @@ func init() {
"jabber:iq:register", "jabber:iq:register",
"query", "query",
}, QueryRegister{}) }, QueryRegister{})
// entity time
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:time",
"time",
}, EntityTime{})
} }

View file

@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"dev.narayana.im/narayana/telegabber/persistence" "dev.narayana.im/narayana/telegabber/persistence"
"dev.narayana.im/narayana/telegabber/telegram" "dev.narayana.im/narayana/telegabber/telegram"
@ -80,6 +81,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
go handleGetVersion(s, iq) go handleGetVersion(s, iq)
return return
} }
_, ok = iq.Payload.(*extensions.EntityTime)
if ok {
go handleGetEntityTime(s, iq)
return
}
} else if iq.Type == stanza.IQTypeSet { } else if iq.Type == stanza.IQTypeSet {
query, ok := iq.Payload.(*extensions.QueryRegister) query, ok := iq.Payload.(*extensions.QueryRegister)
if ok { if ok {
@ -649,6 +655,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
} }
disco.AddFeatures(gateway.NSCommand) disco.AddFeatures(gateway.NSCommand)
disco.AddFeatures("jabber:iq:version") disco.AddFeatures("jabber:iq:version")
disco.AddFeatures("urn:xmpp:time")
} else { } else {
chatType, chatTypeErr := getTelegramChatType(iq.From, iq.To) chatType, chatTypeErr := getTelegramChatType(iq.From, iq.To)
@ -833,6 +840,51 @@ func handleGetVersion(s xmpp.Sender, iq *stanza.IQ) {
_ = gateway.ResumableSend(component, answer) _ = 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) { func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryRegister) {
component, ok := s.(*xmpp.Component) component, ok := s.(*xmpp.Component)
if !ok { if !ok {