diff options
Diffstat (limited to 'teleirc/matterbridge/matterhook/matterhook.go')
| -rw-r--r-- | teleirc/matterbridge/matterhook/matterhook.go | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/teleirc/matterbridge/matterhook/matterhook.go b/teleirc/matterbridge/matterhook/matterhook.go new file mode 100644 index 0000000..6b03643 --- /dev/null +++ b/teleirc/matterbridge/matterhook/matterhook.go @@ -0,0 +1,168 @@ +//Package matterhook provides interaction with mattermost incoming/outgoing webhooks +package matterhook + +import ( + "bytes" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/http" + "time" + + "github.com/gorilla/schema" + "github.com/slack-go/slack" +) + +// 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 []slack.Attachment `json:"attachments,omitempty"` + Type string `json:"type,omitempty"` + Props map[string]interface{} `json:"props"` +} + +// IMessage for mattermost outgoing webhook. (received from mattermost) +type IMessage struct { + BotID string `schema:"bot_id"` + BotName string `schema:"bot_name"` + 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"` //nolint:golint + RawText string `schema:"raw_text"` + ServiceId string `schema:"service_id"` //nolint:golint + Text string `schema:"text"` + TriggerWord string `schema:"trigger_word"` + FileIDs string `schema:"file_ids"` +} + +// Client for Mattermost. +type Client struct { + // URL for incoming webhooks on mattermost. + Url string // nolint:golint + In chan IMessage + Out chan OMessage + httpclient *http.Client + Config +} + +// Config for client. +type Config struct { + BindAddress string // Address to listen on + Token string // Only allow this token from Mattermost. (Allow everything when empty) + InsecureSkipVerify bool // disable certificate checking + DisableServer bool // Do not start server for outgoing webhooks from Mattermost. +} + +// New Mattermost client. +func New(url string, config Config) *Client { + c := &Client{Url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config} + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}, //nolint:gosec + } + c.httpclient = &http.Client{Transport: tr} + if !c.DisableServer { + _, _, err := net.SplitHostPort(c.BindAddress) + if err != nil { + log.Fatalf("incorrect bindaddress %s", c.BindAddress) + } + go c.StartServer() + } + 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, + } + log.Printf("Listening on http://%v...\n", c.BindAddress) + if err := srv.ListenAndServe(); err != nil { + 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 + } + 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 + } + if c.Token != "" { + if msg.Token != c.Token { + log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr) + http.NotFound(w, r) + return + } + } + c.In <- msg +} + +// Receive returns an incoming message from mattermost outgoing webhooks URL. +func (c *Client) Receive() IMessage { + var msg IMessage + for msg := range c.In { + return msg + } + return msg +} + +// 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 + } + resp, err := c.httpclient.Post(c.Url, "application/json", bytes.NewReader(buf)) + 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 +} |
