telegabber/xmpp/component.go

204 lines
4.6 KiB
Go
Raw Normal View History

2019-10-22 16:36:54 +00:00
package xmpp
import (
"github.com/pkg/errors"
2022-01-05 21:04:22 +00:00
"sync"
"time"
2019-10-22 16:36:54 +00:00
"dev.narayana.im/narayana/telegabber/config"
"dev.narayana.im/narayana/telegabber/persistence"
"dev.narayana.im/narayana/telegabber/telegram"
2019-11-24 17:10:29 +00:00
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
2019-10-22 16:36:54 +00:00
log "github.com/sirupsen/logrus"
2019-10-22 16:36:54 +00:00
"gosrc.io/xmpp"
"gosrc.io/xmpp/stanza"
2019-10-22 16:36:54 +00:00
)
var tgConf config.TelegramConfig
2019-11-24 17:10:29 +00:00
var sessions map[string]*telegram.Client
2019-12-04 15:55:15 +00:00
var db *persistence.SessionsYamlDB
2022-01-05 21:04:22 +00:00
var sessionLock sync.Mutex
2019-10-29 01:23:57 +00:00
// NewComponent starts a new component and wraps it in
// a stream manager that you should start yourself
2019-11-19 20:25:14 +00:00
func NewComponent(conf config.XMPPConfig, tc config.TelegramConfig) (*xmpp.StreamManager, *xmpp.Component, error) {
var err error
gateway.Jid, err = stanza.NewJid(conf.Jid)
if err != nil {
2019-11-19 20:25:14 +00:00
return nil, nil, err
}
tgConf = tc
2019-10-22 16:36:54 +00:00
options := xmpp.ComponentOptions{
TransportConfiguration: xmpp.TransportConfiguration{
Address: conf.Host + ":" + conf.Port,
Domain: conf.Jid,
},
2022-02-08 20:25:58 +00:00
Domain: conf.Jid,
Secret: conf.Password,
Name: "telegabber",
2019-10-22 16:36:54 +00:00
}
router := xmpp.NewRouter()
router.HandleFunc("iq", HandleIq)
router.HandleFunc("presence", HandlePresence)
2019-10-22 16:36:54 +00:00
router.HandleFunc("message", HandleMessage)
component, err := xmpp.NewComponent(options, router, func(err error) {
log.Error(err)
})
2019-10-22 16:36:54 +00:00
if err != nil {
2019-11-19 20:25:14 +00:00
return nil, nil, err
2019-10-22 16:36:54 +00:00
}
// probe all known sessions
2019-11-24 17:10:29 +00:00
err = loadSessions(conf.Db, component)
if err != nil {
return nil, nil, err
}
2019-12-04 15:55:15 +00:00
sm := xmpp.NewStreamManager(component, func(s xmpp.Sender) {
go heartbeat(component)
})
2019-11-19 20:25:14 +00:00
return sm, component, nil
2019-10-22 16:36:54 +00:00
}
func heartbeat(component *xmpp.Component) {
var err error
probeType := gateway.SPType("probe")
2022-01-05 21:04:22 +00:00
sessionLock.Lock()
2019-11-14 20:11:04 +00:00
for jid := range sessions {
2019-12-04 15:55:15 +00:00
err = gateway.SendPresence(component, jid, probeType)
if err != nil {
log.Error(err)
}
2019-11-14 20:11:04 +00:00
}
2022-01-05 21:04:22 +00:00
sessionLock.Unlock()
log.Info("Starting heartbeat queue")
// status updater thread
for {
time.Sleep(60e9)
2022-01-31 14:31:05 +00:00
now := time.Now().Unix()
sessionLock.Lock()
for _, session := range sessions {
session.DelayedStatusesLock.Lock()
for chatID, delayedStatus := range session.DelayedStatuses {
if delayedStatus.TimestampExpired <= now {
go session.ProcessStatusUpdate(
chatID,
session.LastSeenStatus(delayedStatus.TimestampOnline),
"away",
)
delete(session.DelayedStatuses, chatID)
}
}
session.DelayedStatusesLock.Unlock()
}
sessionLock.Unlock()
2019-11-29 00:51:41 +00:00
for key, presence := range gateway.Queue {
err = gateway.ResumableSend(component, presence)
if err != nil {
gateway.LogBadPresence(presence)
} else {
gateway.QueueLock.Lock()
2019-11-29 00:51:41 +00:00
delete(gateway.Queue, key)
gateway.QueueLock.Unlock()
}
}
2022-01-05 21:04:22 +00:00
if gateway.DirtySessions {
gateway.DirtySessions = false
// no problem if a dirty flag gets set again here,
// it would be resolved on the next iteration
SaveSessions()
}
}
}
2019-11-24 17:10:29 +00:00
func loadSessions(dbPath string, component *xmpp.Component) error {
var err error
2019-11-24 17:10:29 +00:00
sessions = make(map[string]*telegram.Client)
db, err = persistence.LoadSessions(dbPath)
if err != nil {
return err
}
db.Transaction(func() bool {
for jid, session := range db.Data.Sessions {
2022-01-05 21:04:22 +00:00
// copy the session struct, otherwise all of them would reference
// the same temporary range variable
currentSession := session
getTelegramInstance(jid, &currentSession, component)
}
return false
}, persistence.SessionMarshaller)
return nil
}
2019-11-24 17:10:29 +00:00
func getTelegramInstance(jid string, savedSession *persistence.Session, component *xmpp.Component) (*telegram.Client, bool) {
var err error
session, ok := sessions[jid]
if !ok {
2019-11-24 17:10:29 +00:00
session, err = telegram.NewClient(tgConf, jid, component, savedSession)
if err != nil {
log.Error(errors.Wrap(err, "TDlib initialization failure"))
return session, false
}
2022-01-05 21:04:22 +00:00
if savedSession.KeepOnline {
if err = session.Connect(""); err != nil {
log.Error(err)
return session, false
}
}
sessionLock.Lock()
sessions[jid] = session
2022-01-05 21:04:22 +00:00
sessionLock.Unlock()
}
return session, true
}
2019-11-14 20:11:04 +00:00
2022-01-05 21:04:22 +00:00
// SaveSessions dumps current sessions to the file
func SaveSessions() {
sessionLock.Lock()
defer sessionLock.Unlock()
db.Transaction(func() bool {
for jid, session := range sessions {
db.Data.Sessions[jid] = *session.Session
}
return true
}, persistence.SessionMarshaller)
}
2019-11-19 20:25:14 +00:00
// Close gracefully terminates the component and saves active sessions
func Close(component *xmpp.Component) {
log.Error("Disconnecting...")
2022-01-05 21:04:22 +00:00
sessionLock.Lock()
// close all sessions
2019-11-19 20:25:14 +00:00
for _, session := range sessions {
2022-01-03 03:54:13 +00:00
session.Disconnect("", true)
2019-11-19 20:25:14 +00:00
}
2022-01-05 21:04:22 +00:00
sessionLock.Unlock()
2019-11-19 20:25:14 +00:00
// save sessions
2022-01-05 21:04:22 +00:00
SaveSessions()
2019-11-19 20:25:14 +00:00
// close stream
2019-11-19 20:25:14 +00:00
component.Disconnect()
}