AJ ONeal
5 years ago
6 changed files with 358 additions and 41 deletions
@ -1,13 +1,161 @@ |
|||
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 |
|||
} |
|||
|
Loading…
Reference in new issue