From d6f6207ebb3d5256256a7810a3d3d0bdc8ba04a5 Mon Sep 17 00:00:00 2001 From: bodqhrohro Date: Mon, 25 Nov 2019 00:20:07 +0200 Subject: Refactoring: move SendPresence to xmpp/gateway package --- Makefile | 2 +- xmpp/component.go | 119 +++--------------------------------------- xmpp/component_test.go | 54 ------------------- xmpp/extensions.go | 49 ----------------- xmpp/extensions/extensions.go | 49 +++++++++++++++++ xmpp/gateway/gateway.go | 115 ++++++++++++++++++++++++++++++++++++++++ xmpp/gateway/gateway_test.go | 54 +++++++++++++++++++ 7 files changed, 225 insertions(+), 217 deletions(-) delete mode 100644 xmpp/component_test.go delete mode 100644 xmpp/extensions.go create mode 100644 xmpp/extensions/extensions.go create mode 100644 xmpp/gateway/gateway_test.go diff --git a/Makefile b/Makefile index d53a133..662ce26 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: go build -o telegabber test: - go test -v ./config ./ ./telegram ./xmpp + go test -v ./config ./ ./telegram ./xmpp/gateway lint: $(GOPATH)/bin/golint ./... diff --git a/xmpp/component.go b/xmpp/component.go index 7e2d16e..c0b4a23 100644 --- a/xmpp/component.go +++ b/xmpp/component.go @@ -1,7 +1,6 @@ package xmpp import ( - "encoding/xml" "github.com/pkg/errors" "time" @@ -11,16 +10,14 @@ import ( "dev.narayana.im/narayana/telegabber/xmpp/gateway" log "github.com/sirupsen/logrus" - "github.com/soheilhy/args" "gosrc.io/xmpp" - "gosrc.io/xmpp/stanza" ) const pollingInterval time.Duration = 1e7 var tgConf config.TelegramConfig var sessions map[string]*telegram.Client -var queue map[string]*stanza.Presence +var queue gateway.Queue var db persistence.SessionsYamlDB // NewComponent starts a new component and wraps it in @@ -33,6 +30,8 @@ func NewComponent(conf config.XMPPConfig, tc config.TelegramConfig) (*xmpp.Strea return nil, nil, err } + queue = make(gateway.Queue) + tgConf = tc options := xmpp.ComponentOptions{ @@ -64,19 +63,13 @@ func NewComponent(conf config.XMPPConfig, tc config.TelegramConfig) (*xmpp.Strea return sm, component, nil } -func logPresence(err error, presence *stanza.Presence) { - log.WithFields(log.Fields{ - "presence": *presence, - }).Error(errors.Wrap(err, "Couldn't send presence")) -} - func heartbeat(component *xmpp.Component) { var err error - probeType := SPType("probe") + probeType := gateway.SPType("probe") for jid := range sessions { for { - err = sendPresence(component, jid, probeType) + err = gateway.SendPresence(component, queue, jid, probeType) if err == nil { break } @@ -90,7 +83,7 @@ func heartbeat(component *xmpp.Component) { for key, presence := range queue { err = component.Send(presence) if err != nil { - logPresence(err, presence) + gateway.LogBadPresence(err, presence) } else { delete(queue, key) } @@ -135,106 +128,6 @@ func getTelegramInstance(jid string, savedSession *persistence.Session, componen return session, true } -// SPFrom is a Telegram user id -var SPFrom = args.NewString() - -// SPType is a presence type -var SPType = args.NewString() - -// SPShow is a availability status -var SPShow = args.NewString() - -// SPStatus is a verbose status -var SPStatus = args.NewString() - -// SPNickname is a XEP-0172 nickname -var SPNickname = args.NewString() - -// SPPhoto is a XEP-0153 hash of avatar in vCard -var SPPhoto = args.NewString() - -// SPImmed skips queueing -var SPImmed = args.NewBool(args.Default(true)) - -func newPresence(bareJid string, to string, args ...args.V) stanza.Presence { - var presenceFrom string - if SPFrom.IsSet(args) { - presenceFrom = SPFrom.Get(args) + "@" + bareJid - } else { - presenceFrom = bareJid - } - - presence := stanza.Presence{Attrs: stanza.Attrs{ - From: presenceFrom, - To: to, - }} - - if SPType.IsSet(args) { - presence.Attrs.Type = stanza.StanzaType(SPType.Get(args)) - } - if SPShow.IsSet(args) { - presence.Show = stanza.PresenceShow(SPShow.Get(args)) - } - if SPStatus.IsSet(args) { - presence.Status = SPStatus.Get(args) - } - if SPNickname.IsSet(args) { - presence.Extensions = append(presence.Extensions, PresenceNickExtension{ - Text: SPNickname.Get(args), - }) - } - if SPPhoto.IsSet(args) { - presence.Extensions = append(presence.Extensions, PresenceXVCardUpdateExtension{ - Photo: PresenceXVCardUpdatePhoto{ - Text: SPPhoto.Get(args), - }, - }) - } - - return presence -} - -func sendPresence(component *xmpp.Component, to string, args ...args.V) error { - var logFrom string - bareJid := gateway.Jid.Bare() - if SPFrom.IsSet(args) { - logFrom = SPFrom.Get(args) - } else { - logFrom = bareJid - } - - log.WithFields(log.Fields{ - "type": SPType.Get(args), - "from": logFrom, - "to": to, - }).Info("Got presence") - - presence := newPresence(bareJid, to, args...) - - // explicit check, as marshalling is expensive - if log.GetLevel() == log.DebugLevel { - xmlPresence, err := xml.Marshal(presence) - if err == nil { - log.Debug(string(xmlPresence)) - } else { - log.Debugf("%#v", presence) - } - } - - immed := SPImmed.Get(args) - if immed { - err := component.Send(presence) - if err != nil { - logPresence(err, &presence) - return err - } - } else { - queue[presence.From+presence.To] = &presence - } - - return nil -} - // Close gracefully terminates the component and saves active sessions func Close(component *xmpp.Component) { log.Error("Disconnecting...") diff --git a/xmpp/component_test.go b/xmpp/component_test.go deleted file mode 100644 index b540ad4..0000000 --- a/xmpp/component_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package xmpp - -import ( - "encoding/xml" - "testing" - - "gosrc.io/xmpp/stanza" -) - -func testPresence(t *testing.T, presence stanza.Presence, reference string) { - byteXML, err := xml.Marshal(presence) - if err != nil { - t.Errorf("XML parse error: %v", err) - } - xmlText := string(byteXML) - if xmlText != reference { - t.Errorf("%v does not match %v", xmlText, reference) - } -} - -func TestPresenceFrom(t *testing.T) { - presence := newPresence("from@test", "to@test", SPFrom("test")) - testPresence(t, presence, "") -} - -func TestPresenceNoFrom(t *testing.T) { - presence := newPresence("from@test", "to@test") - testPresence(t, presence, "") -} - -func TestPresenceType(t *testing.T) { - presence := newPresence("from@test", "to@test", SPType("subscribe")) - testPresence(t, presence, "") -} - -func TestPresenceShow(t *testing.T) { - presence := newPresence("from@test", "to@test", SPShow("dnd")) - testPresence(t, presence, "dnd") -} - -func TestPresenceStatus(t *testing.T) { - presence := newPresence("from@test", "to@test", SPStatus("cooking")) - testPresence(t, presence, "cooking") -} - -func TestPresenceNickname(t *testing.T) { - presence := newPresence("from@test", "to@test", SPNickname("Ishmael")) - testPresence(t, presence, "Ishmael") -} - -func TestPresencePhoto(t *testing.T) { - presence := newPresence("from@test", "to@test", SPPhoto("01b87fcd030b72895ff8e88db57ec525450f000d")) - testPresence(t, presence, "01b87fcd030b72895ff8e88db57ec525450f000d") -} diff --git a/xmpp/extensions.go b/xmpp/extensions.go deleted file mode 100644 index 2270431..0000000 --- a/xmpp/extensions.go +++ /dev/null @@ -1,49 +0,0 @@ -package xmpp - -import ( - "encoding/xml" - - "gosrc.io/xmpp/stanza" -) - -// PresenceNickExtension is from XEP-0172 -type PresenceNickExtension struct { - XMLName xml.Name `xml:"http://jabber.org/protocol/nick nick"` - Text string `xml:",chardata"` -} - -// PresenceXVCardUpdateExtension is from XEP-0153 -type PresenceXVCardUpdateExtension struct { - XMLName xml.Name `xml:"vcard-temp:x:update x"` - Photo PresenceXVCardUpdatePhoto -} - -// PresenceXVCardUpdatePhoto is from XEP-0153 -type PresenceXVCardUpdatePhoto struct { - XMLName xml.Name `xml:"photo"` - Text string `xml:",chardata"` -} - -// Namespace is a namespace! -func (c PresenceNickExtension) Namespace() string { - return c.XMLName.Space -} - -// Namespace is a namespace! -func (c PresenceXVCardUpdateExtension) Namespace() string { - return c.XMLName.Space -} - -func init() { - // presence nick - stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ - "http://jabber.org/protocol/nick", - "nick", - }, PresenceNickExtension{}) - - // presence vcard update - stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ - "vcard-temp:x:update", - "x", - }, PresenceXVCardUpdateExtension{}) -} diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go new file mode 100644 index 0000000..ec94837 --- /dev/null +++ b/xmpp/extensions/extensions.go @@ -0,0 +1,49 @@ +package extensions + +import ( + "encoding/xml" + + "gosrc.io/xmpp/stanza" +) + +// PresenceNickExtension is from XEP-0172 +type PresenceNickExtension struct { + XMLName xml.Name `xml:"http://jabber.org/protocol/nick nick"` + Text string `xml:",chardata"` +} + +// PresenceXVCardUpdateExtension is from XEP-0153 +type PresenceXVCardUpdateExtension struct { + XMLName xml.Name `xml:"vcard-temp:x:update x"` + Photo PresenceXVCardUpdatePhoto +} + +// PresenceXVCardUpdatePhoto is from XEP-0153 +type PresenceXVCardUpdatePhoto struct { + XMLName xml.Name `xml:"photo"` + Text string `xml:",chardata"` +} + +// Namespace is a namespace! +func (c PresenceNickExtension) Namespace() string { + return c.XMLName.Space +} + +// Namespace is a namespace! +func (c PresenceXVCardUpdateExtension) Namespace() string { + return c.XMLName.Space +} + +func init() { + // presence nick + stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ + "http://jabber.org/protocol/nick", + "nick", + }, PresenceNickExtension{}) + + // presence vcard update + stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ + "vcard-temp:x:update", + "x", + }, PresenceXVCardUpdateExtension{}) +} diff --git a/xmpp/gateway/gateway.go b/xmpp/gateway/gateway.go index 0c22677..11f68c1 100644 --- a/xmpp/gateway/gateway.go +++ b/xmpp/gateway/gateway.go @@ -2,12 +2,19 @@ package gateway import ( "encoding/xml" + "github.com/pkg/errors" + + "dev.narayana.im/narayana/telegabber/xmpp/extensions" log "github.com/sirupsen/logrus" + "github.com/soheilhy/args" "gosrc.io/xmpp" "gosrc.io/xmpp/stanza" ) +// Queue stores presences to send later +type Queue map[string]*stanza.Presence + // Jid stores the component's JID object var Jid *xmpp.Jid @@ -51,3 +58,111 @@ func SendMessage(to string, from string, body string, component *xmpp.Component) _ = component.Send(message) } + +// LogBadPresence verbosely logs a presence +func LogBadPresence(err error, presence *stanza.Presence) { + log.WithFields(log.Fields{ + "presence": *presence, + }).Error(errors.Wrap(err, "Couldn't send presence")) +} + +// SPFrom is a Telegram user id +var SPFrom = args.NewString() + +// SPType is a presence type +var SPType = args.NewString() + +// SPShow is a availability status +var SPShow = args.NewString() + +// SPStatus is a verbose status +var SPStatus = args.NewString() + +// SPNickname is a XEP-0172 nickname +var SPNickname = args.NewString() + +// SPPhoto is a XEP-0153 hash of avatar in vCard +var SPPhoto = args.NewString() + +// SPImmed skips queueing +var SPImmed = args.NewBool(args.Default(true)) + +func newPresence(bareJid string, to string, args ...args.V) stanza.Presence { + var presenceFrom string + if SPFrom.IsSet(args) { + presenceFrom = SPFrom.Get(args) + "@" + bareJid + } else { + presenceFrom = bareJid + } + + presence := stanza.Presence{Attrs: stanza.Attrs{ + From: presenceFrom, + To: to, + }} + + if SPType.IsSet(args) { + presence.Attrs.Type = stanza.StanzaType(SPType.Get(args)) + } + if SPShow.IsSet(args) { + presence.Show = stanza.PresenceShow(SPShow.Get(args)) + } + if SPStatus.IsSet(args) { + presence.Status = SPStatus.Get(args) + } + if SPNickname.IsSet(args) { + presence.Extensions = append(presence.Extensions, extensions.PresenceNickExtension{ + Text: SPNickname.Get(args), + }) + } + if SPPhoto.IsSet(args) { + presence.Extensions = append(presence.Extensions, extensions.PresenceXVCardUpdateExtension{ + Photo: extensions.PresenceXVCardUpdatePhoto{ + Text: SPPhoto.Get(args), + }, + }) + } + + return presence +} + +// SendPresence creates and sends a presence stanza +func SendPresence(component *xmpp.Component, queue Queue, to string, args ...args.V) error { + var logFrom string + bareJid := Jid.Bare() + if SPFrom.IsSet(args) { + logFrom = SPFrom.Get(args) + } else { + logFrom = bareJid + } + + log.WithFields(log.Fields{ + "type": SPType.Get(args), + "from": logFrom, + "to": to, + }).Info("Got presence") + + presence := newPresence(bareJid, to, args...) + + // explicit check, as marshalling is expensive + if log.GetLevel() == log.DebugLevel { + xmlPresence, err := xml.Marshal(presence) + if err == nil { + log.Debug(string(xmlPresence)) + } else { + log.Debugf("%#v", presence) + } + } + + immed := SPImmed.Get(args) + if immed { + err := component.Send(presence) + if err != nil { + LogBadPresence(err, &presence) + return err + } + } else { + queue[presence.From+presence.To] = &presence + } + + return nil +} diff --git a/xmpp/gateway/gateway_test.go b/xmpp/gateway/gateway_test.go new file mode 100644 index 0000000..6191844 --- /dev/null +++ b/xmpp/gateway/gateway_test.go @@ -0,0 +1,54 @@ +package gateway + +import ( + "encoding/xml" + "testing" + + "gosrc.io/xmpp/stanza" +) + +func testPresence(t *testing.T, presence stanza.Presence, reference string) { + byteXML, err := xml.Marshal(presence) + if err != nil { + t.Errorf("XML parse error: %v", err) + } + xmlText := string(byteXML) + if xmlText != reference { + t.Errorf("%v does not match %v", xmlText, reference) + } +} + +func TestPresenceFrom(t *testing.T) { + presence := newPresence("from@test", "to@test", SPFrom("test")) + testPresence(t, presence, "") +} + +func TestPresenceNoFrom(t *testing.T) { + presence := newPresence("from@test", "to@test") + testPresence(t, presence, "") +} + +func TestPresenceType(t *testing.T) { + presence := newPresence("from@test", "to@test", SPType("subscribe")) + testPresence(t, presence, "") +} + +func TestPresenceShow(t *testing.T) { + presence := newPresence("from@test", "to@test", SPShow("dnd")) + testPresence(t, presence, "dnd") +} + +func TestPresenceStatus(t *testing.T) { + presence := newPresence("from@test", "to@test", SPStatus("cooking")) + testPresence(t, presence, "cooking") +} + +func TestPresenceNickname(t *testing.T) { + presence := newPresence("from@test", "to@test", SPNickname("Ishmael")) + testPresence(t, presence, "Ishmael") +} + +func TestPresencePhoto(t *testing.T) { + presence := newPresence("from@test", "to@test", SPPhoto("01b87fcd030b72895ff8e88db57ec525450f000d")) + testPresence(t, presence, "01b87fcd030b72895ff8e88db57ec525450f000d") +} -- cgit v1.2.3