xmpp-webhook/main.go

155 wiersze
3.6 KiB
Go
Czysty Zwykły widok Historia

2017-09-24 18:44:39 +00:00
package main
import (
2019-07-30 10:50:07 +00:00
"context"
"crypto/tls"
2019-08-06 11:28:39 +00:00
"encoding/xml"
"io"
2017-09-24 18:44:39 +00:00
"log"
2019-07-30 10:50:07 +00:00
"mellium.im/sasl"
2019-08-06 11:28:39 +00:00
"mellium.im/xmlstream"
2019-07-30 10:50:07 +00:00
"mellium.im/xmpp"
"mellium.im/xmpp/dial"
"mellium.im/xmpp/jid"
2019-07-30 10:50:07 +00:00
"mellium.im/xmpp/stanza"
2019-11-01 15:59:54 +00:00
"net/http"
2017-09-24 18:44:39 +00:00
"os"
2019-11-01 15:59:54 +00:00
"strings"
2017-09-24 18:44:39 +00:00
)
2019-07-30 10:50:07 +00:00
func panicOnErr(err error) {
2017-09-25 12:59:08 +00:00
if err != nil {
2019-07-30 10:50:07 +00:00
panic(err)
2017-09-25 12:59:08 +00:00
}
2019-07-30 10:50:07 +00:00
}
2017-09-24 18:44:39 +00:00
2019-08-06 11:28:39 +00:00
type MessageBody struct {
stanza.Message
Body string `xml:"body"`
}
2019-11-02 08:40:25 +00:00
func initXMPP(address jid.JID, pass string, skipTLSVerify bool, useXMPPS bool) (*xmpp.Session, error) {
tlsConfig := tls.Config{InsecureSkipVerify: skipTLSVerify}
var dialer dial.Dialer
2019-11-01 15:59:54 +00:00
// only use the tls config for the dialer if necessary
if skipTLSVerify {
2019-11-02 08:40:25 +00:00
dialer = dial.Dialer{NoTLS: !useXMPPS, TLSConfig: &tlsConfig}
} else {
2019-11-02 08:40:25 +00:00
dialer = dial.Dialer{NoTLS: !useXMPPS}
}
conn, err := dialer.Dial(context.TODO(), "tcp", address)
2019-08-04 19:03:44 +00:00
if err != nil {
2017-09-25 12:59:08 +00:00
return nil, err
}
2019-11-01 15:59:54 +00:00
// we need the domain in the tls config if we want to verify the cert
if !skipTLSVerify {
tlsConfig.ServerName = address.Domainpart()
}
2019-07-30 10:50:07 +00:00
return xmpp.NegotiateSession(
context.TODO(),
address.Domain(),
address,
2019-07-30 10:50:07 +00:00
conn,
false,
xmpp.NewNegotiator(xmpp.StreamConfig{Features: []xmpp.StreamFeature{
xmpp.BindResource(),
2019-11-02 08:40:25 +00:00
xmpp.StartTLS(false, &tlsConfig),
2019-07-30 10:50:07 +00:00
xmpp.SASL("", pass, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain),
}}),
)
}
2017-09-24 18:44:39 +00:00
2019-07-30 10:50:07 +00:00
func closeXMPP(session *xmpp.Session) {
2020-01-05 12:30:06 +00:00
_ = session.Close()
_ = session.Conn().Close()
2017-09-25 08:54:06 +00:00
}
func main() {
// get xmpp credentials, message receivers
2017-09-26 12:39:19 +00:00
xi := os.Getenv("XMPP_ID")
xp := os.Getenv("XMPP_PASS")
xr := os.Getenv("XMPP_RECEIVERS")
// get tls settings from env
_, skipTLSVerify := os.LookupEnv("XMPP_SKIP_VERIFY")
2019-11-02 08:40:25 +00:00
_, useXMPPS := os.LookupEnv("XMPP_OVER_TLS")
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
2019-11-01 16:16:20 +00:00
if xi == "" || xp == "" || xr == "" {
2017-09-26 12:39:19 +00:00
log.Fatal("XMPP_ID, XMPP_PASS or XMPP_RECEIVERS not set")
2017-09-25 08:54:06 +00:00
}
address, err := jid.Parse(xi)
2019-07-30 10:50:07 +00:00
panicOnErr(err)
2017-09-26 12:39:19 +00:00
// connect to xmpp server
2019-11-02 08:40:25 +00:00
xmppSession, err := initXMPP(address, xp, skipTLSVerify, useXMPPS)
2019-07-30 10:50:07 +00:00
panicOnErr(err)
2019-11-01 15:59:54 +00:00
defer closeXMPP(xmppSession)
2019-07-30 10:50:07 +00:00
// send initial presence
2019-11-01 15:59:54 +00:00
panicOnErr(xmppSession.Send(context.TODO(), stanza.WrapPresence(jid.JID{}, stanza.AvailablePresence, nil)))
2019-07-30 10:50:07 +00:00
2019-11-01 15:59:54 +00:00
// listen for messages and echo them
go func() {
err = xmppSession.Serve(xmpp.HandlerFunc(func(t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
d := xml.NewTokenDecoder(t)
// ignore elements that aren't messages
if start.Name.Local != "message" {
return nil
}
2019-08-06 11:28:39 +00:00
2019-11-01 15:59:54 +00:00
// parse message into struct
msg := MessageBody{}
err = d.DecodeElement(&msg, start)
if err != nil && err != io.EOF {
return nil
}
2019-08-06 11:28:39 +00:00
2019-11-01 15:59:54 +00:00
// ignore empty messages and stanzas that aren't messages
if msg.Body == "" || msg.Type != stanza.ChatMessage {
return nil
}
2019-08-06 11:28:39 +00:00
2019-11-01 15:59:54 +00:00
// create reply with identical contents
reply := MessageBody{
Message: stanza.Message{
To: msg.From.Bare(),
},
Body: msg.Body,
}
2019-08-06 11:28:39 +00:00
2019-11-01 15:59:54 +00:00
// try to send reply, ignore errors
_ = t.Encode(reply)
return nil
}))
panicOnErr(err)
}()
2017-09-24 18:44:39 +00:00
2019-11-01 15:59:54 +00:00
// create chan for messages (webhooks -> xmpp)
2017-09-26 12:39:19 +00:00
messages := make(chan string)
2017-09-25 15:58:44 +00:00
2017-09-26 12:39:19 +00:00
// wait for messages from the webhooks and send them to all receivers
2017-09-25 15:58:44 +00:00
go func() {
2017-09-26 12:39:19 +00:00
for m := range messages {
2017-09-25 15:58:44 +00:00
for _, r := range strings.Split(xr, ",") {
2019-11-01 15:59:54 +00:00
recipient, err := jid.Parse(r)
panicOnErr(err)
// try to send message, ignore errors
2019-11-01 16:16:20 +00:00
_ = xmppSession.Encode(MessageBody{
Message: stanza.Message{
To: recipient,
},
Body: m,
})
2017-09-25 15:58:44 +00:00
}
}
}()
2017-09-26 12:39:19 +00:00
// initialize handler for grafana alerts
http.Handle("/grafana", newMessageHandler(messages, grafanaParserFunc))
2017-09-25 15:58:44 +00:00
// listen for requests
2020-01-05 12:30:06 +00:00
_ = http.ListenAndServe(":4321", nil)
2017-09-24 18:44:39 +00:00
}