matterbridge/matterhook/matterhook.go

165 lines
4.3 KiB
Go
Raw Normal View History

2015-10-23 14:31:14 -07:00
//Package matterhook provides interaction with mattermost incoming/outgoing webhooks
2015-10-23 08:09:08 -07:00
package matterhook
import (
"bytes"
"crypto/tls"
2015-10-23 08:09:08 -07:00
"encoding/json"
"fmt"
"github.com/gorilla/schema"
"io"
"io/ioutil"
"log"
2016-07-11 14:22:56 -07:00
"net"
2015-10-23 08:09:08 -07:00
"net/http"
"time"
2015-10-23 08:09:08 -07:00
)
// OMessage for mattermost incoming webhook. (send to mattermost)
type OMessage struct {
Channel string `json:"channel,omitempty"`
IconURL string `json:"icon_url,omitempty"`
IconEmoji string `json:"icon_emoji,omitempty"`
UserName string `json:"username,omitempty"`
Text string `json:"text"`
Attachments interface{} `json:"attachments,omitempty"`
Type string `json:"type,omitempty"`
2015-10-23 08:09:08 -07:00
}
// IMessage for mattermost outgoing webhook. (received from mattermost)
type IMessage struct {
2016-09-05 07:34:37 -07:00
BotID string `schema:"bot_id"`
BotName string `schema:"bot_name"`
2015-10-23 08:09:08 -07:00
Token string `schema:"token"`
TeamID string `schema:"team_id"`
TeamDomain string `schema:"team_domain"`
ChannelID string `schema:"channel_id"`
ChannelName string `schema:"channel_name"`
Timestamp string `schema:"timestamp"`
UserID string `schema:"user_id"`
UserName string `schema:"user_name"`
PostId string `schema:"post_id"`
2016-09-05 07:34:37 -07:00
RawText string `schema:"raw_text"`
ServiceId string `schema:"service_id"`
2015-10-23 08:09:08 -07:00
Text string `schema:"text"`
TriggerWord string `schema:"trigger_word"`
FileIDs string `schema:"file_ids"`
2015-10-23 08:09:08 -07:00
}
// Client for Mattermost.
type Client struct {
2015-10-28 09:18:05 -07:00
Url string // URL for incoming webhooks on mattermost.
In chan IMessage
Out chan OMessage
httpclient *http.Client
2015-10-23 08:09:08 -07:00
Config
}
2015-10-24 09:01:15 -07:00
// Config for client.
2015-10-23 08:09:08 -07:00
type Config struct {
2015-12-12 12:26:53 -08:00
BindAddress string // Address to listen on
Token string // Only allow this token from Mattermost. (Allow everything when empty)
InsecureSkipVerify bool // disable certificate checking
2015-10-28 09:18:05 -07:00
DisableServer bool // Do not start server for outgoing webhooks from Mattermost.
2015-10-23 08:09:08 -07:00
}
// New Mattermost client.
func New(url string, config Config) *Client {
2015-10-28 09:18:05 -07:00
c := &Client{Url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config}
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify},
}
c.httpclient = &http.Client{Transport: tr}
2015-10-28 09:18:05 -07:00
if !c.DisableServer {
_, _, err := net.SplitHostPort(c.BindAddress)
if err != nil {
log.Fatalf("incorrect bindaddress %s", c.BindAddress)
}
2015-10-28 09:18:05 -07:00
go c.StartServer()
}
2015-10-23 08:09:08 -07:00
return c
}
// StartServer starts a webserver listening for incoming mattermost POSTS.
func (c *Client) StartServer() {
mux := http.NewServeMux()
mux.Handle("/", c)
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
Handler: mux,
Addr: c.BindAddress,
}
2016-07-11 14:22:56 -07:00
log.Printf("Listening on http://%v...\n", c.BindAddress)
if err := srv.ListenAndServe(); err != nil {
2015-10-23 08:09:08 -07:00
log.Fatal(err)
}
}
// ServeHTTP implementation.
func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
log.Println("invalid " + r.Method + " connection from " + r.RemoteAddr)
http.NotFound(w, r)
return
}
2015-10-23 08:09:08 -07:00
msg := IMessage{}
err := r.ParseForm()
if err != nil {
log.Println(err)
http.NotFound(w, r)
return
}
defer r.Body.Close()
decoder := schema.NewDecoder()
err = decoder.Decode(&msg, r.PostForm)
if err != nil {
log.Println(err)
http.NotFound(w, r)
return
}
if msg.Token == "" {
log.Println("no token from " + r.RemoteAddr)
http.NotFound(w, r)
return
}
2015-10-24 09:01:15 -07:00
if c.Token != "" {
if msg.Token != c.Token {
log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr)
http.NotFound(w, r)
return
}
}
2015-10-23 08:09:08 -07:00
c.In <- msg
}
// Receive returns an incoming message from mattermost outgoing webhooks URL.
func (c *Client) Receive() IMessage {
2017-07-13 15:35:01 -07:00
var msg IMessage
for msg := range c.In {
return msg
2015-10-23 08:09:08 -07:00
}
2017-07-13 15:35:01 -07:00
return msg
2015-10-23 08:09:08 -07:00
}
// Send sends a msg to mattermost incoming webhooks URL.
func (c *Client) Send(msg OMessage) error {
buf, err := json.Marshal(msg)
if err != nil {
return err
}
2015-10-28 09:18:05 -07:00
resp, err := c.httpclient.Post(c.Url, "application/json", bytes.NewReader(buf))
2015-10-23 08:09:08 -07:00
if err != nil {
return err
}
defer resp.Body.Close()
// Read entire body to completion to re-use keep-alive connections.
io.Copy(ioutil.Discard, resp.Body)
if resp.StatusCode != 200 {
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
return nil
}