2017-09-24 18:44:39 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
2017-09-25 15:58:44 +00:00
|
|
|
"net/http"
|
2017-09-24 18:44:39 +00:00
|
|
|
"os"
|
2017-09-25 15:58:44 +00:00
|
|
|
"strings"
|
2017-09-25 12:59:08 +00:00
|
|
|
"time"
|
2017-09-24 18:44:39 +00:00
|
|
|
|
|
|
|
"github.com/emgee/go-xmpp/src/xmpp"
|
|
|
|
)
|
|
|
|
|
2017-09-25 11:33:43 +00:00
|
|
|
const (
|
2017-09-25 15:58:44 +00:00
|
|
|
envXMPPID = "XMPP_ID"
|
|
|
|
envXMPPPASS = "XMPP_PASS"
|
|
|
|
envXMPPReceivers = "XMPP_RECEIVERS"
|
|
|
|
errWrongArgs = "XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set"
|
|
|
|
xmppBotAnswer = "im a dumb bot"
|
|
|
|
xmppConnErr = "failed to connect "
|
|
|
|
xmppOfflineErr = "not connected to XMPP server, dropped message"
|
|
|
|
xmppFailedPause = 30
|
|
|
|
webHookAddr = ":4321"
|
2017-09-25 11:33:43 +00:00
|
|
|
)
|
|
|
|
|
2017-09-25 12:59:08 +00:00
|
|
|
// starts xmpp session and returns the xmpp client
|
|
|
|
func xmppLogin(id string, pass string) (*xmpp.XMPP, error) {
|
2017-09-25 11:33:43 +00:00
|
|
|
// parse jid structure
|
2017-09-25 08:54:06 +00:00
|
|
|
jid, err := xmpp.ParseJID(id)
|
2017-09-25 12:59:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-24 18:44:39 +00:00
|
|
|
|
2017-09-25 11:33:43 +00:00
|
|
|
// extract/generate address:port from jid
|
2017-09-24 18:44:39 +00:00
|
|
|
addr, err := xmpp.HomeServerAddrs(jid)
|
2017-09-25 12:59:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-24 18:44:39 +00:00
|
|
|
|
2017-09-25 11:33:43 +00:00
|
|
|
// create xml stream to address
|
2017-09-24 18:44:39 +00:00
|
|
|
stream, err := xmpp.NewStream(addr[0], nil)
|
2017-09-25 12:59:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-24 18:44:39 +00:00
|
|
|
|
2017-09-25 11:33:43 +00:00
|
|
|
// create client (login)
|
2017-09-25 08:54:06 +00:00
|
|
|
client, err := xmpp.NewClientXMPP(stream, jid, pass, nil)
|
2017-09-25 12:59:08 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-09-24 18:44:39 +00:00
|
|
|
|
2017-09-25 12:59:08 +00:00
|
|
|
return client, nil
|
2017-09-25 08:54:06 +00:00
|
|
|
}
|
|
|
|
|
2017-09-25 11:33:43 +00:00
|
|
|
// creates MessageBody slice suitable for xmpp.Message
|
|
|
|
func xmppBodyCreate(message string) []xmpp.MessageBody {
|
|
|
|
return []xmpp.MessageBody{
|
|
|
|
xmpp.MessageBody{
|
|
|
|
Value: message,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handles incoming stanzas
|
|
|
|
func handleXMPPStanza(in <-chan interface{}, out chan<- interface{}) {
|
|
|
|
for stanza := range in {
|
|
|
|
// check if stanza is a message
|
|
|
|
message, ok := stanza.(*xmpp.Message)
|
|
|
|
if ok && len(message.Body) > 0 {
|
|
|
|
// send constant as answer
|
|
|
|
out <- xmpp.Message{
|
|
|
|
To: message.From,
|
|
|
|
Body: xmppBodyCreate(xmppBotAnswer),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-25 12:59:08 +00:00
|
|
|
// func returns when in chan is closed (server terminated stream)
|
2017-09-25 11:33:43 +00:00
|
|
|
}
|
|
|
|
|
2017-09-25 08:54:06 +00:00
|
|
|
func main() {
|
2017-09-25 11:33:43 +00:00
|
|
|
// get xmpp credentials from ENV
|
2017-09-25 12:59:08 +00:00
|
|
|
xi := os.Getenv(envXMPPID)
|
|
|
|
xp := os.Getenv(envXMPPPASS)
|
2017-09-25 15:58:44 +00:00
|
|
|
xr := os.Getenv(envXMPPReceivers)
|
2017-09-25 08:54:06 +00:00
|
|
|
|
2017-09-25 15:58:44 +00:00
|
|
|
// check if xmpp credentials and receiver list are supplied
|
|
|
|
if len(xi) < 1 || len(xp) < 1 || len(xr) < 1 {
|
2017-09-25 12:59:08 +00:00
|
|
|
log.Fatal(errWrongArgs)
|
2017-09-25 08:54:06 +00:00
|
|
|
}
|
|
|
|
|
2017-09-25 12:59:08 +00:00
|
|
|
// connect xmpp client and observe connection - reconnect if needed
|
|
|
|
var xc *xmpp.XMPP
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
// try to connect to xmpp server
|
|
|
|
var err error
|
|
|
|
xc, err = xmppLogin(xi, xp)
|
|
|
|
if err != nil {
|
|
|
|
// report failure and wait
|
|
|
|
log.Print(xmppConnErr, err)
|
|
|
|
time.Sleep(time.Second * time.Duration(xmppFailedPause))
|
|
|
|
} else {
|
2017-09-25 15:58:44 +00:00
|
|
|
// send initial presence and dispatch channels to handler for incoming messages
|
2017-09-25 12:59:08 +00:00
|
|
|
xc.Out <- xmpp.Presence{}
|
|
|
|
handleXMPPStanza(xc.In, xc.Out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2017-09-24 18:44:39 +00:00
|
|
|
|
2017-09-25 15:58:44 +00:00
|
|
|
// create channel for alerts (Webhook -> XMPP)
|
|
|
|
alertChan := make(chan string)
|
|
|
|
|
|
|
|
// create handler for outgoing XMPP messages
|
|
|
|
go func() {
|
|
|
|
for message := range alertChan {
|
|
|
|
for _, r := range strings.Split(xr, ",") {
|
|
|
|
if xc != nil {
|
|
|
|
xc.Out <- xmpp.Message{
|
|
|
|
To: r,
|
|
|
|
Body: xmppBodyCreate(message),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Print(xmppOfflineErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// initialize HTTP handlers with chan for alerts
|
|
|
|
grafanaHandler := makeGrafanaHandler(alertChan)
|
|
|
|
http.HandleFunc("/grafana", grafanaHandler)
|
|
|
|
|
|
|
|
// listen for requests
|
|
|
|
http.ListenAndServe(webHookAddr, nil)
|
2017-09-24 18:44:39 +00:00
|
|
|
}
|