162 lines
4.0 KiB
Go
162 lines
4.0 KiB
Go
package webooks
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var logger chan string
|
|
|
|
func init() {
|
|
logger = make(chan string, 10)
|
|
go func() {
|
|
for {
|
|
msg := <-logger
|
|
log.Println(msg)
|
|
}
|
|
}()
|
|
}
|
|
|
|
type Webhook struct {
|
|
ID string `json:"id,omitempty"`
|
|
Comment string `json:"comment"`
|
|
Method string `json:"method"`
|
|
URL string `json:"url"`
|
|
TZ string `json:"-"`
|
|
Auth map[string]string `json:"auth,omitempty"`
|
|
Headers map[string]string `json:"headers,omitempty"`
|
|
Form map[string]string `json:"form,omitempty"`
|
|
JSON map[string]string `json:"json,omitempty"`
|
|
Config map[string]string `json:"config,omitempty"`
|
|
}
|
|
|
|
func Log(str string, args ...interface{}) {
|
|
logger <- fmt.Sprintf(str, args...)
|
|
}
|
|
|
|
func Run(h Webhook) {
|
|
// TODO do this in main on config init
|
|
if "" == h.Method {
|
|
h.Method = "POST"
|
|
}
|
|
|
|
var body *strings.Reader
|
|
var err error
|
|
// TODO real templates
|
|
loc, err := time.LoadLocation(h.TZ)
|
|
if nil != err {
|
|
Log("Bad timezone", h.TZ)
|
|
loc, _ = time.LoadLocation("UTC")
|
|
}
|
|
t := time.Now().In(loc)
|
|
z, _ := t.Zone()
|
|
if 0 != len(h.Form) {
|
|
form := url.Values{}
|
|
for k := range h.Form {
|
|
v := h.Form[k]
|
|
// because `{{` gets urlencoded
|
|
//v = strings.Replace(v, "{{ .Name }}", d.Name, -1)
|
|
v = strings.Replace(v, "{{ .Datetime }}", t.Format("2006-01-02 3:04:05 MST"), -1)
|
|
v = strings.Replace(v, "{{ .Date }}", t.Format("2006-01-02"), -1)
|
|
v = strings.Replace(v, "{{ .Time }}", t.Format(time.Kitchen), -1)
|
|
v = strings.Replace(v, "{{ .Zone }}", z, -1)
|
|
Log("[HEADER] %s: %s", k, v)
|
|
form.Set(k, v)
|
|
}
|
|
body = strings.NewReader(form.Encode())
|
|
} else if 0 != len(h.JSON) {
|
|
bodyBuf, err := json.Marshal(h.JSON)
|
|
if nil != err {
|
|
Log("[Notify] JSON Marshal Error for '%s': %s", h.Comment, err)
|
|
return
|
|
}
|
|
// `{{` is left alone in the body
|
|
bodyStr := string(bodyBuf)
|
|
bodyStr = strings.Replace(bodyStr, "{{ .Datetime }}", t.Format("2006-01-02 3:04:05 MST"), -1)
|
|
bodyStr = strings.Replace(bodyStr, "{{ .Date }}", t.Format("2006-01-02"), -1)
|
|
bodyStr = strings.Replace(bodyStr, "{{ .Time }}", t.Format("3:04:05PM"), -1)
|
|
bodyStr = strings.Replace(bodyStr, "{{ .Zone }}", z, -1)
|
|
body = strings.NewReader(bodyStr)
|
|
//body = strings.NewReader(string(bodyBuf))
|
|
}
|
|
if nil == body {
|
|
body = strings.NewReader("")
|
|
}
|
|
|
|
client := NewHTTPClient()
|
|
fmt.Println("bd?", h.Method, h.URL, body)
|
|
req, err := http.NewRequest(h.Method, h.URL, body)
|
|
if nil != err {
|
|
Log("[Notify] HTTP Client Network Error for '%s': %s", h.Comment, err)
|
|
return
|
|
}
|
|
|
|
if 0 != len(h.Form) {
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
} else if 0 != len(h.JSON) {
|
|
req.Header.Set("Content-Type", "application/json")
|
|
}
|
|
|
|
if 0 != len(h.Auth) {
|
|
user := h.Auth["user"]
|
|
if "" == user {
|
|
user = h.Auth["username"]
|
|
}
|
|
pass := h.Auth["pass"]
|
|
if "" == user {
|
|
pass = h.Auth["password"]
|
|
}
|
|
req.SetBasicAuth(user, pass)
|
|
}
|
|
|
|
req.Header.Set("User-Agent", "Watchdog/1.0")
|
|
for k := range h.Headers {
|
|
req.Header.Set(k, h.Headers[k])
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if nil != err {
|
|
Log("[Notify] HTTP Client Error for '%s': %s", h.Comment, err)
|
|
return
|
|
}
|
|
|
|
if !(resp.StatusCode >= 200 && resp.StatusCode < 300) {
|
|
Log("[Notify] Response Error for '%s': %s", h.Comment, resp.Status)
|
|
return
|
|
}
|
|
|
|
// TODO json vs xml vs txt
|
|
var data map[string]interface{}
|
|
req.Header.Add("Accept", "application/json")
|
|
decoder := json.NewDecoder(resp.Body)
|
|
err = decoder.Decode(&data)
|
|
if err != nil {
|
|
Log("[Notify] Response Body Error for '%s': %s", h.Comment, resp.Status)
|
|
return
|
|
}
|
|
|
|
// TODO some sort of way to determine if data is successful (keywords)
|
|
Log("[Notify] Success? %#v", data)
|
|
}
|
|
|
|
// The default http client uses unsafe defaults
|
|
func NewHTTPClient() *http.Client {
|
|
transport := &http.Transport{
|
|
Dial: (&net.Dialer{
|
|
Timeout: 10 * time.Second,
|
|
}).Dial,
|
|
TLSHandshakeTimeout: 5 * time.Second,
|
|
}
|
|
client := &http.Client{
|
|
Timeout: time.Second * 5,
|
|
Transport: transport,
|
|
}
|
|
return client
|
|
}
|