Merge branch 'master' into muc

This commit is contained in:
Bohdan Horbeshko 2025-10-19 13:36:56 -04:00
commit cd71818577
8 changed files with 135 additions and 11 deletions

View file

@ -68,7 +68,7 @@ func main() {
log.Infof("Starting telegabber version %v", version) log.Infof("Starting telegabber version %v", version)
sm, component, err = xmpp.NewComponent(config.XMPP, config.Telegram, *idsPath) sm, component, err = xmpp.NewComponent(config.XMPP, config.Telegram, *idsPath, version)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -54,6 +54,7 @@ var transportCommands = map[string]command{
"help": command{0, []string{}, "help", false, nil}, "help": command{0, []string{}, "help", false, nil},
"login": command{1, []string{"phone"}, "sign in", false, nil}, "login": command{1, []string{"phone"}, "sign in", false, nil},
"logout": command{0, []string{}, "sign out", true, nil}, "logout": command{0, []string{}, "sign out", true, nil},
"cleanup": command{0, []string{}, "unsubscribe from all known chats", false, nil},
"cancelauth": command{0, []string{}, "quit the signin wizard", false, nil}, "cancelauth": command{0, []string{}, "quit the signin wizard", false, nil},
"code": command{1, []string{"xxxxx"}, "check one-time code", false, nil}, "code": command{1, []string{"xxxxx"}, "check one-time code", false, nil},
"password": command{1, []string{"********"}, "check 2fa password", false, nil}, "password": command{1, []string{"********"}, "check 2fa password", false, nil},
@ -358,13 +359,14 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
return errors.Wrap(err, "Logout error").Error(), false return errors.Wrap(err, "Logout error").Error(), false
} }
for _, id := range c.cache.OwnChatsKeys() { c.unsubscribeFromAll()
c.leaveChat(id)
}
c.Session.Login = "" c.Session.Login = ""
c.wizardStageOrPrompt(LoginStageCancel, "") c.wizardStageOrPrompt(LoginStageCancel, "")
c.online = false c.online = false
// cleanup
case "cleanup":
c.unsubscribeFromAll()
// cancel auth // cancel auth
case "cancelauth": case "cancelauth":
if c.Online() { if c.Online() {
@ -687,11 +689,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

@ -155,11 +155,13 @@ func (c *Client) Connect(resource string) error {
c.addResource(resource) c.addResource(resource)
go func() { go func() {
_, err = c.client.GetChats(&client.GetChatsRequest{ chats, err := c.client.GetChats(&client.GetChatsRequest{
Limit: chatsLimit, Limit: chatsLimit,
}) })
if err != nil { if err != nil {
log.Errorf("Could not retrieve chats: %v", err) log.Errorf("Could not retrieve chats: %v", err)
} else {
log.Infof("Obtained ≈%v chats for initialization", chats.TotalCount)
} }
gateway.SubscribeToTransport(c.xmpp, c.jid) gateway.SubscribeToTransport(c.xmpp, c.jid)

View file

@ -2948,6 +2948,13 @@ func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, p
} }
} }
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) {
if membersList == MembersListCreators { if membersList == MembersListCreators {
@ -3084,6 +3091,12 @@ func (c *Client) unsubscribe(chatID int64) error {
return c.sendPresence(args...) return c.sendPresence(args...)
} }
func (c *Client) unsubscribeFromAll() {
for _, id := range c.cache.ChatsKeys() {
c.leaveChat(id)
}
}
func (c *Client) leaveChat(chatID int64) error { func (c *Client) leaveChat(chatID int64) error {
chat, err := c.GetChatByID(chatID, nil, true) chat, err := c.GetChatByID(chatID, nil, true)

View file

@ -39,10 +39,11 @@ var sizeRegex = regexp.MustCompile("\\A([0-9]+) ?([KMGTPE]?B?)\\z")
// NewComponent starts a new component and wraps it in // NewComponent starts a new component and wraps it in
// a stream manager that you should start yourself // a stream manager that you should start yourself
func NewComponent(conf config.XMPPConfig, tc config.TelegramConfig, idsPath string) (*xmpp.StreamManager, *xmpp.Component, error) { func NewComponent(conf config.XMPPConfig, tc config.TelegramConfig, idsPath string, version string) (*xmpp.StreamManager, *xmpp.Component, error) {
var err error var err error
gateway.Jid, err = stanza.NewJid(conf.Jid) gateway.Jid, err = stanza.NewJid(conf.Jid)
gateway.Version = version
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View file

@ -482,6 +482,14 @@ type MAM2MetadataEnd struct {
Timestamp string `xml:"timestamp,attr,omitempty"` Timestamp string `xml:"timestamp,attr,omitempty"`
} }
// 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
@ -562,6 +570,16 @@ func (c MessageDelayLegacy) Namespace() string {
return c.XMLName.Space return c.XMLName.Space
} }
// 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"
@ -895,4 +913,10 @@ func init() {
"urn:xmpp:mam:2", "urn:xmpp:mam:2",
"metadata", "metadata",
}, MAM2Metadata{}) }, MAM2Metadata{})
// entity time
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
"urn:xmpp:time",
"time",
}, EntityTime{})
} }

View file

@ -61,6 +61,9 @@ var QueueLock = sync.Mutex{}
// Jid stores the component's JID object // Jid stores the component's JID object
var Jid *stanza.Jid var Jid *stanza.Jid
// Version stores this software's version
var Version string
// IdsDB provides a disk-backed bidirectional dictionary of Telegram and XMPP ids // IdsDB provides a disk-backed bidirectional dictionary of Telegram and XMPP ids
var IdsDB badger.IdsDB var IdsDB badger.IdsDB

View file

@ -108,6 +108,16 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
go handleGetMetadataMAM2(s, iq) go handleGetMetadataMAM2(s, iq)
return return
} }
_, ok = iq.Payload.(*stanza.Version)
if ok {
go handleGetVersion(s, iq)
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 {
queryRegister, ok := iq.Payload.(*extensions.QueryRegister) queryRegister, ok := iq.Payload.(*extensions.QueryRegister)
if ok { if ok {
@ -1072,6 +1082,8 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
disco.AddFeatures("jabber:iq:register") disco.AddFeatures("jabber:iq:register")
} }
disco.AddFeatures(gateway.NSCommand) disco.AddFeatures(gateway.NSCommand)
disco.AddFeatures("jabber:iq:version")
disco.AddFeatures("urn:xmpp:time")
} else if di.Node == "x-roomuser-item" { } else if di.Node == "x-roomuser-item" {
bare, _, fromOk := gateway.SplitJID(iq.From) bare, _, fromOk := gateway.SplitJID(iq.From)
if fromOk { if fromOk {
@ -1254,6 +1266,77 @@ func handleGetQueryRegister(s xmpp.Sender, iq *stanza.IQ) {
} }
} }
func handleGetVersion(s xmpp.Sender, iq *stanza.IQ) {
component, ok := s.(*xmpp.Component)
if !ok {
log.Error("Not a component")
return
}
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
}
answer.Version().SetInfo(gateway.Jid.Resource, gateway.Version, "")
log.Debugf("%#v", answer.Payload)
_ = 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 handleGetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryMucAdmin) { func handleGetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryMucAdmin) {
component, answer, ok := iqResultStub(s, iq) component, answer, ok := iqResultStub(s, iq)
if !ok { if !ok {