Add initial XMPP support

This commit is contained in:
Wim 2016-07-14 00:23:50 +02:00
parent 46faad8b57
commit 1f72ca4c4e
3 changed files with 182 additions and 25 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/42wim/matterbridge/matterclient" "github.com/42wim/matterbridge/matterclient"
"github.com/42wim/matterbridge/matterhook" "github.com/42wim/matterbridge/matterhook"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/mattn/go-xmpp"
"github.com/peterhellberg/giphy" "github.com/peterhellberg/giphy"
ircm "github.com/sorcix/irc" ircm "github.com/sorcix/irc"
"github.com/thoj/go-ircevent" "github.com/thoj/go-ircevent"
@ -26,6 +27,10 @@ type MMapi struct {
mmIgnoreNicks []string mmIgnoreNicks []string
} }
type MMxmpp struct {
xc *xmpp.Client
xmppMap map[string]string
}
type MMirc struct { type MMirc struct {
i *irc.Connection i *irc.Connection
ircNick string ircNick string
@ -44,6 +49,7 @@ type Bridge struct {
MMhook MMhook
MMapi MMapi
MMirc MMirc
MMxmpp
*Config *Config
kind string kind string
} }
@ -51,6 +57,7 @@ type Bridge struct {
type FancyLog struct { type FancyLog struct {
irc *log.Entry irc *log.Entry
mm *log.Entry mm *log.Entry
xmpp *log.Entry
} }
var flog FancyLog var flog FancyLog
@ -60,6 +67,7 @@ const Legacy = "legacy"
func initFLog() { func initFLog() {
flog.irc = log.WithFields(log.Fields{"module": "irc"}) flog.irc = log.WithFields(log.Fields{"module": "irc"})
flog.mm = log.WithFields(log.Fields{"module": "mattermost"}) 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 { func NewBridge(name string, config *Config, kind string) *Bridge {
@ -67,9 +75,10 @@ func NewBridge(name string, config *Config, kind string) *Bridge {
b := &Bridge{} b := &Bridge{}
b.Config = config b.Config = config
b.kind = kind b.kind = kind
b.mmMap = make(map[string]string)
if b.Config.General.Irc {
b.ircNick = b.Config.IRC.Nick b.ircNick = b.Config.IRC.Nick
b.ircMap = make(map[string]string) b.ircMap = make(map[string]string)
b.mmMap = make(map[string]string)
b.MMirc.names = make(map[string][]string) b.MMirc.names = make(map[string][]string)
b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks) b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks)
b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks) b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks)
@ -77,6 +86,15 @@ func NewBridge(name string, config *Config, kind string) *Bridge {
b.ircMap[val.IRC] = val.Mattermost b.ircMap[val.IRC] = val.Mattermost
b.mmMap[val.Mattermost] = val.IRC 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 { if kind == Legacy {
b.mh = matterhook.New(b.Config.Mattermost.URL, b.mh = matterhook.New(b.Config.Mattermost.URL,
matterhook.Config{InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify, matterhook.Config{InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify,
@ -98,9 +116,25 @@ func NewBridge(name string, config *Config, kind string) *Bridge {
} }
go b.mc.WsReceiver() go b.mc.WsReceiver()
} }
if b.Config.General.Irc {
flog.irc.Info("Trying IRC connection") flog.irc.Info("Trying IRC connection")
b.i = b.createIRC(name) b.i = b.createIRC(name)
flog.irc.Info("Connection succeeded") 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() go b.handleMatter()
return b return b
} }
@ -123,6 +157,25 @@ func (b *Bridge) createIRC(name string) *irc.Connection {
return i 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) { func (b *Bridge) handleNewConnection(event *irc.Event) {
flog.irc.Info("Registering callbacks") flog.irc.Info("Registering callbacks")
i := b.i i := b.i
@ -147,12 +200,20 @@ func (b *Bridge) handleNewConnection(event *irc.Event) {
} }
func (b *Bridge) setupChannels() { func (b *Bridge) setupChannels() {
i := b.i if b.Config.General.Irc {
for _, val := range b.Config.Channel { for _, val := range b.Config.Channel {
flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick) flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick)
i.Join(val.IRC) 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 { func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool {
parts := strings.Fields(event.Message()) parts := strings.Fields(event.Message())
@ -340,10 +401,12 @@ func (b *Bridge) handleMatter() {
if b.ignoreMessage(message.Username, message.Text, "mattermost") { if b.ignoreMessage(message.Username, message.Text, "mattermost") {
continue continue
} }
if b.Config.General.Irc {
username = message.Username + ": " username = message.Username + ": "
if b.Config.IRC.RemoteNickFormat != "" { if b.Config.IRC.RemoteNickFormat != "" {
username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1) username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1)
} }
}
cmds := strings.Fields(message.Text) cmds := strings.Fields(message.Text)
// empty message // empty message
if len(cmds) == 0 { if len(cmds) == 0 {
@ -353,18 +416,28 @@ func (b *Bridge) handleMatter() {
switch cmd { switch cmd {
case "!users": case "!users":
flog.mm.Info("Received !users from ", message.Username) flog.mm.Info("Received !users from ", message.Username)
if b.Config.General.Irc {
b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel)) b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel))
}
continue continue
case "!gif": case "!gif":
message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1))) message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1)))
if b.Config.General.Irc {
b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel)) b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel))
}
continue continue
} }
texts := strings.Split(message.Text, "\n") texts := strings.Split(message.Text, "\n")
for _, text := range texts { for _, text := range texts {
flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel) flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel)
if b.Config.General.Irc {
b.i.Privmsg(b.getIRCChannel(message.Channel), username+text) 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 return res.Data.FixedHeightDownsampledURL
} }
func (b *Bridge) getMMChannel(ircChannel string) string { func (b *Bridge) getMMChannel(channel string) string {
mmChannel := b.ircMap[ircChannel] 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 { if b.kind == Legacy {
return mmChannel return mmChannel
} }
@ -405,3 +484,41 @@ func (b *Bridge) ignoreMessage(nick string, message string, protocol string) boo
} }
return false 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
}
}
}

View File

@ -38,12 +38,22 @@ type Config struct {
IgnoreNicks string IgnoreNicks string
NoTLS bool NoTLS bool
} }
Xmpp struct {
Jid string
Password string
Server string
Muc string
Nick string
}
Channel map[string]*struct { Channel map[string]*struct {
IRC string IRC string
Mattermost string Mattermost string
Xmpp string
} }
General struct { General struct {
GiphyAPIKey string GiphyAPIKey string
Xmpp bool
Irc bool
} }
} }

View File

@ -41,6 +41,31 @@ RemoteNickFormat="{NICK}: "
#OPTIONAL #OPTIONAL
IgnoreNicks="ircspammer1 ircspammer2" 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 #mattermost section
################################################################### ###################################################################
@ -146,3 +171,8 @@ mattermost="testing"
#request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key. #request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key.
#OPTIONAL #OPTIONAL
GiphyApiKey="dc6zaTOxFJmzC" GiphyApiKey="dc6zaTOxFJmzC"
#Choose only one protocol to bridge, do not set both to true!
#REQUIRED
Irc=true
Xmpp=false