Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

Viewing file on branch master

1// Package matterhook provides interaction with mattermost incoming/outgoing webhooks
2package matterhook
3
4import (
5 "bytes"
6 "crypto/tls"
7 "encoding/json"
8 "fmt"
9 "io"
10 "log"
11 "net"
12 "net/http"
13 "time"
14
15 "github.com/gorilla/schema"
16 "github.com/slack-go/slack"
17)
18
19// OMessage for mattermost incoming webhook. (send to mattermost)
20type OMessage struct {
21 Channel string `json:"channel,omitempty"`
22 IconURL string `json:"icon_url,omitempty"`
23 IconEmoji string `json:"icon_emoji,omitempty"`
24 UserName string `json:"username,omitempty"`
25 Text string `json:"text"`
26 Attachments []slack.Attachment `json:"attachments,omitempty"`
27 Type string `json:"type,omitempty"`
28 Props map[string]interface{} `json:"props"`
29}
30
31// IMessage for mattermost outgoing webhook. (received from mattermost)
32type IMessage struct {
33 BotID string `schema:"bot_id"`
34 BotName string `schema:"bot_name"`
35 Token string `schema:"token"`
36 TeamID string `schema:"team_id"`
37 TeamDomain string `schema:"team_domain"`
38 ChannelID string `schema:"channel_id"`
39 ChannelName string `schema:"channel_name"`
40 Timestamp string `schema:"timestamp"`
41 UserID string `schema:"user_id"`
42 UserName string `schema:"user_name"`
43 PostId string `schema:"post_id"`
44 RawText string `schema:"raw_text"`
45 ServiceId string `schema:"service_id"`
46 Text string `schema:"text"`
47 TriggerWord string `schema:"trigger_word"`
48 FileIDs string `schema:"file_ids"`
49}
50
51// Client for Mattermost.
52type Client struct {
53 // URL for incoming webhooks on mattermost.
54 Url string
55 In chan IMessage
56 Out chan OMessage
57 httpclient *http.Client
58 Config
59}
60
61// Config for client.
62type Config struct {
63 BindAddress string // Address to listen on
64 Token string // Only allow this token from Mattermost. (Allow everything when empty)
65 InsecureSkipVerify bool // disable certificate checking
66 DisableServer bool // Do not start server for outgoing webhooks from Mattermost.
67}
68
69// New Mattermost client.
70func New(url string, config Config) *Client {
71 c := &Client{Url: url, In: make(chan IMessage), Out: make(chan OMessage), Config: config}
72 tr := &http.Transport{
73 TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}, //nolint:gosec
74 }
75 c.httpclient = &http.Client{Transport: tr}
76 if !c.DisableServer {
77 _, _, err := net.SplitHostPort(c.BindAddress)
78 if err != nil {
79 log.Fatalf("incorrect bindaddress %s", c.BindAddress)
80 }
81 go c.StartServer()
82 }
83 return c
84}
85
86// StartServer starts a webserver listening for incoming mattermost POSTS.
87func (c *Client) StartServer() {
88 mux := http.NewServeMux()
89 mux.Handle("/", c)
90 srv := &http.Server{
91 ReadTimeout: 5 * time.Second,
92 WriteTimeout: 10 * time.Second,
93 Handler: mux,
94 Addr: c.BindAddress,
95 }
96 log.Printf("Listening on http://%v...\n", c.BindAddress)
97 if err := srv.ListenAndServe(); err != nil {
98 log.Fatal(err)
99 }
100}
101
102// ServeHTTP implementation.
103func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
104 if r.Method != "POST" {
105 log.Println("invalid " + r.Method + " connection from " + r.RemoteAddr)
106 http.NotFound(w, r)
107 return
108 }
109 msg := IMessage{}
110 err := r.ParseForm()
111 if err != nil {
112 log.Println(err)
113 http.NotFound(w, r)
114 return
115 }
116 defer r.Body.Close()
117 decoder := schema.NewDecoder()
118 err = decoder.Decode(&msg, r.PostForm)
119 if err != nil {
120 log.Println(err)
121 http.NotFound(w, r)
122 return
123 }
124 if msg.Token == "" {
125 log.Println("no token from " + r.RemoteAddr)
126 http.NotFound(w, r)
127 return
128 }
129 if c.Token != "" {
130 if msg.Token != c.Token {
131 log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr)
132 http.NotFound(w, r)
133 return
134 }
135 }
136 c.In <- msg
137}
138
139// Receive returns an incoming message from mattermost outgoing webhooks URL.
140func (c *Client) Receive() IMessage {
141 var msg IMessage
142 for msg := range c.In {
143 return msg
144 }
145 return msg
146}
147
148// Send sends a msg to mattermost incoming webhooks URL.
149func (c *Client) Send(msg OMessage) error {
150 buf, err := json.Marshal(msg)
151 if err != nil {
152 return err
153 }
154 resp, err := c.httpclient.Post(c.Url, "application/json", bytes.NewReader(buf))
155 if err != nil {
156 return err
157 }
158 defer resp.Body.Close()
159
160 // Read entire body to completion to re-use keep-alive connections.
161 _, err = io.Copy(io.Discard, resp.Body)
162 if err != nil {
163 return err
164 }
165
166 if resp.StatusCode != 200 {
167 return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
168 }
169 return nil
170}
171