From 1f72ca4c4ecb5ab3ef831b8e70c74909f5720fb8 Mon Sep 17 00:00:00 2001 From: Wim Date: Thu, 14 Jul 2016 00:23:50 +0200 Subject: [PATCH] Add initial XMPP support --- bridge/bridge.go | 167 +++++++++++++++++++++++++++++++++------ bridge/config.go | 10 +++ matterbridge.conf.sample | 30 +++++++ 3 files changed, 182 insertions(+), 25 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 034aed6f..76654c40 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -5,6 +5,7 @@ import ( "github.com/42wim/matterbridge/matterclient" "github.com/42wim/matterbridge/matterhook" log "github.com/Sirupsen/logrus" + "github.com/mattn/go-xmpp" "github.com/peterhellberg/giphy" ircm "github.com/sorcix/irc" "github.com/thoj/go-ircevent" @@ -26,6 +27,10 @@ type MMapi struct { mmIgnoreNicks []string } +type MMxmpp struct { + xc *xmpp.Client + xmppMap map[string]string +} type MMirc struct { i *irc.Connection ircNick string @@ -44,13 +49,15 @@ type Bridge struct { MMhook MMapi MMirc + MMxmpp *Config kind string } type FancyLog struct { - irc *log.Entry - mm *log.Entry + irc *log.Entry + mm *log.Entry + xmpp *log.Entry } var flog FancyLog @@ -60,6 +67,7 @@ const Legacy = "legacy" func initFLog() { flog.irc = log.WithFields(log.Fields{"module": "irc"}) flog.mm = log.WithFields(log.Fields{"module": "mattermost"}) + flog.xmpp = log.WithFields(log.Fields{"module": "xmpp"}) } func NewBridge(name string, config *Config, kind string) *Bridge { @@ -67,16 +75,26 @@ func NewBridge(name string, config *Config, kind string) *Bridge { b := &Bridge{} b.Config = config b.kind = kind - b.ircNick = b.Config.IRC.Nick - b.ircMap = make(map[string]string) b.mmMap = make(map[string]string) - b.MMirc.names = make(map[string][]string) - b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks) - b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks) - for _, val := range b.Config.Channel { - b.ircMap[val.IRC] = val.Mattermost - b.mmMap[val.Mattermost] = val.IRC + if b.Config.General.Irc { + b.ircNick = b.Config.IRC.Nick + b.ircMap = make(map[string]string) + b.MMirc.names = make(map[string][]string) + b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks) + b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks) + for _, val := range b.Config.Channel { + b.ircMap[val.IRC] = val.Mattermost + b.mmMap[val.Mattermost] = val.IRC + } } + if b.Config.General.Xmpp { + b.xmppMap = make(map[string]string) + for _, val := range b.Config.Channel { + b.xmppMap[val.Xmpp] = val.Mattermost + b.mmMap[val.Mattermost] = val.Xmpp + } + } + if kind == Legacy { b.mh = matterhook.New(b.Config.Mattermost.URL, matterhook.Config{InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify, @@ -98,9 +116,25 @@ func NewBridge(name string, config *Config, kind string) *Bridge { } go b.mc.WsReceiver() } - flog.irc.Info("Trying IRC connection") - b.i = b.createIRC(name) - flog.irc.Info("Connection succeeded") + + if b.Config.General.Irc { + flog.irc.Info("Trying IRC connection") + b.i = b.createIRC(name) + flog.irc.Info("Connection succeeded") + } + if b.Config.General.Xmpp { + var err error + flog.xmpp.Info("Trying XMPP connection") + b.xc, err = b.createXMPP() + if err != nil { + flog.xmpp.Debugf("%#v", err) + panic("xmpp failure") + } + flog.xmpp.Info("Connection succeeded") + b.setupChannels() + go b.handleXmpp() + } + go b.handleMatter() return b } @@ -123,6 +157,25 @@ func (b *Bridge) createIRC(name string) *irc.Connection { return i } +func (b *Bridge) createXMPP() (*xmpp.Client, error) { + options := xmpp.Options{ + Host: b.Config.Xmpp.Server, + User: b.Config.Xmpp.Jid, + Password: b.Config.Xmpp.Password, + NoTLS: true, + StartTLS: true, + Debug: true, + Session: true, + Status: "", + StatusMessage: "", + Resource: "", + InsecureAllowUnencryptedAuth: false, + } + var err error + b.xc, err = options.NewClient() + return b.xc, err +} + func (b *Bridge) handleNewConnection(event *irc.Event) { flog.irc.Info("Registering callbacks") i := b.i @@ -147,11 +200,19 @@ func (b *Bridge) handleNewConnection(event *irc.Event) { } func (b *Bridge) setupChannels() { - i := b.i - for _, val := range b.Config.Channel { - flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick) - i.Join(val.IRC) + if b.Config.General.Irc { + for _, val := range b.Config.Channel { + flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick) + b.i.Join(val.IRC) + } } + if b.Config.General.Xmpp { + for _, val := range b.Config.Channel { + flog.xmpp.Infof("Joining %s as %s", val.Xmpp, b.Xmpp.Nick) + b.xc.JoinMUCNoHistory(val.Xmpp+"@"+b.Xmpp.Muc, b.Xmpp.Nick) + } + } + } func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool { @@ -340,9 +401,11 @@ func (b *Bridge) handleMatter() { if b.ignoreMessage(message.Username, message.Text, "mattermost") { continue } - username = message.Username + ": " - if b.Config.IRC.RemoteNickFormat != "" { - username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1) + if b.Config.General.Irc { + username = message.Username + ": " + if b.Config.IRC.RemoteNickFormat != "" { + username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1) + } } cmds := strings.Fields(message.Text) // empty message @@ -353,17 +416,27 @@ func (b *Bridge) handleMatter() { switch cmd { case "!users": flog.mm.Info("Received !users from ", message.Username) - b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel)) + if b.Config.General.Irc { + b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel)) + } continue case "!gif": message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1))) - b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel)) + if b.Config.General.Irc { + b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel)) + } continue } texts := strings.Split(message.Text, "\n") for _, text := range texts { flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel) - b.i.Privmsg(b.getIRCChannel(message.Channel), username+text) + if b.Config.General.Irc { + b.i.Privmsg(b.getIRCChannel(message.Channel), username+text) + } + if b.Config.General.Xmpp { + b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: "testje@c.sw.be", Text: username + text}) + + } } } } @@ -380,8 +453,14 @@ func (b *Bridge) giphyRandom(query []string) string { return res.Data.FixedHeightDownsampledURL } -func (b *Bridge) getMMChannel(ircChannel string) string { - mmChannel := b.ircMap[ircChannel] +func (b *Bridge) getMMChannel(channel string) string { + var mmChannel string + if b.Config.General.Irc { + mmChannel = b.ircMap[channel] + } + if b.Config.General.Xmpp { + mmChannel = b.xmppMap[channel] + } if b.kind == Legacy { return mmChannel } @@ -405,3 +484,41 @@ func (b *Bridge) ignoreMessage(nick string, message string, protocol string) boo } return false } + +func (b *Bridge) xmppKeepAlive() { + go func() { + ticker := time.NewTicker(90 * time.Second) + for { + select { + case <-ticker.C: + b.xc.Send(xmpp.Chat{}) + } + } + }() +} + +func (b *Bridge) handleXmpp() error { + for { + m, err := b.xc.Recv() + if err != nil { + return err + } + switch v := m.(type) { + case xmpp.Chat: + var channel, nick string + if v.Type == "groupchat" { + s := strings.Split(v.Remote, "@") + if len(s) == 2 { + channel = s[0] + } + s = strings.Split(s[1], "/") + if len(s) == 2 { + nick = s[1] + } + b.Send(nick, v.Text, b.getMMChannel(channel)) + } + case xmpp.Presence: + // do nothing + } + } +} diff --git a/bridge/config.go b/bridge/config.go index 3750a1a0..274d4261 100644 --- a/bridge/config.go +++ b/bridge/config.go @@ -38,12 +38,22 @@ type Config struct { IgnoreNicks string NoTLS bool } + Xmpp struct { + Jid string + Password string + Server string + Muc string + Nick string + } Channel map[string]*struct { IRC string Mattermost string + Xmpp string } General struct { GiphyAPIKey string + Xmpp bool + Irc bool } } diff --git a/matterbridge.conf.sample b/matterbridge.conf.sample index 55746539..a8afc48c 100644 --- a/matterbridge.conf.sample +++ b/matterbridge.conf.sample @@ -41,6 +41,31 @@ RemoteNickFormat="{NICK}: " #OPTIONAL IgnoreNicks="ircspammer1 ircspammer2" +################################################################### +#XMPP section +################################################################### +[XMPP] +#xmpp server to connect to. +#REQUIRED +Server="jabber.example.com:5222" + +#Jid +#REQUIRED +Jid="user@example.com" + +#Password +#REQUIRED +Password="yourpass" + +#MUC +#REQUIRED +Muc="conference.jabber.example.com" + +#Your nick in the rooms +#REQUIRED +Nick="xmppbot" + + ################################################################### #mattermost section ################################################################### @@ -146,3 +171,8 @@ mattermost="testing" #request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key. #OPTIONAL GiphyApiKey="dc6zaTOxFJmzC" + +#Choose only one protocol to bridge, do not set both to true! +#REQUIRED +Irc=true +Xmpp=false