Skip to content

Commit

Permalink
Webhooks: templates!
Browse files Browse the repository at this point in the history
  • Loading branch information
toxuin committed Sep 21, 2022
1 parent c17b7ce commit b154b93
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 29 deletions.
2 changes: 2 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ _A_: Some IP cameras have ONVIF, and sometimes that even includes motion alarms,

- Misecu 5MP Wifi AI Cam (Hisilicon server)

- Lorex N8428-Z 4K NVR (Dahua server)

- HomeViz OB10, K4W10, OW10 (Hisilicon server, tested by [acburnett](https://github.com/acburnett))

If your camera works with Alarm Server - create an issue with some details about it and a picture, and we'll post it here.
Expand Down
66 changes: 59 additions & 7 deletions buses/webhooks/webhooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import (
"encoding/json"
"fmt"
"github.com/toxuin/alarmserver/config"
"io/ioutil"
"io"
"net/http"
"strings"
"text/template"
)

type Bus struct {
Expand All @@ -17,8 +18,9 @@ type Bus struct {
}

type WebhookPayload struct {
Topic string `json:"topic"`
Data string `json:"data"`
CameraName string `json:"cameraName"`
EventType string `json:"eventType"`
Extra string `json:"extra"`
}

func (webhooks *Bus) Initialize(conf config.WebhooksConfig) {
Expand All @@ -42,9 +44,9 @@ func (webhooks *Bus) Initialize(conf config.WebhooksConfig) {
}
}

func (webhooks *Bus) SendMessage(topic string, data string) {
func (webhooks *Bus) SendMessage(cameraName string, eventType string, extra string) {
for _, webhook := range webhooks.webhooks {
payload := WebhookPayload{Topic: topic, Data: data}
payload := WebhookPayload{CameraName: cameraName, EventType: eventType, Extra: extra}
go webhooks.send(webhook, payload)
}
}
Expand All @@ -56,7 +58,57 @@ func (webhooks *Bus) send(webhook config.WebhookConfig, payload WebhookPayload)
return
}

request, err := http.NewRequest(webhook.Method, webhook.Url, bytes.NewBuffer(payloadJson))
var templateVars = map[string]interface{}{
"Camera": payload.CameraName,
"Event": payload.EventType,
"Extra": payload.Extra,
}

// PARSE WEBHOOK URL AS TEMPLATE
urlTemplate, err := template.New("webhookUrl").Parse(webhook.Url)
if err != nil {
fmt.Printf("WEBHOOKS: Error parsing webhook URL as template: %s\n", webhook.Url)
if webhooks.Debug {
fmt.Println("Webhooks: Error", err)
}
return
}
var urlBuffer bytes.Buffer
err = urlTemplate.Execute(&urlBuffer, templateVars)
if err != nil {
fmt.Printf("WEBHOOKS: Error rendering webhook URL as template: %s\n", webhook.Url)
if webhooks.Debug {
fmt.Println("Webhooks: Error", err)
}
return
}
url := urlBuffer.String()

// PARSE BODY AS TEMPLATE
body := bytes.NewBuffer(payloadJson)
if webhook.BodyTemplate != "" {
bodyTemplate, err := template.New("payload").Parse(webhook.BodyTemplate)
if err != nil {
fmt.Printf("WEBHOOKS: Error parsing webhook body as template: %s\n", webhook.Url)
if webhooks.Debug {
fmt.Println("Webhooks: Error", err)
}
return
}

var bodyBuffer bytes.Buffer
err = bodyTemplate.Execute(&bodyBuffer, templateVars)
if err != nil {
fmt.Printf("WEBHOOKS: Error rendering webhook body as template: %s\n", webhook.Url)
if webhooks.Debug {
fmt.Println("Webhooks: Error", err)
}
return
}
body = &bodyBuffer
}

request, err := http.NewRequest(webhook.Method, url, body)
if err != nil {
fmt.Printf("WEBHOOKS: Error creating %s request to %s\n", webhook.Method, webhook.Url)
if webhooks.Debug {
Expand Down Expand Up @@ -93,7 +145,7 @@ func (webhooks *Bus) send(webhook config.WebhookConfig, payload WebhookPayload)
)
}
if webhooks.Debug {
bodyBytes, _ := ioutil.ReadAll(response.Body)
bodyBytes, _ := io.ReadAll(response.Body)
bodyStr := string(bodyBytes)
if len(bodyStr) == 0 {
bodyStr = "*empty*"
Expand Down
7 changes: 4 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ type WebhooksConfig struct {
}

type WebhookConfig struct {
Url string `json:"url"`
Method string `json:"method"`
Headers []string `json:"headers"`
Url string `json:"url"`
Method string `json:"method"`
Headers []string `json:"headers"`
BodyTemplate string `json:"bodyTemplate"`
}

type HisiliconConfig struct {
Expand Down
7 changes: 7 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ webhooks:
method: "GET" # DEFAULTS TO POST
headers:
- "X-Beep: boop"

# YOU CAN USE TEMPLATE VARIABLES TO FORM THE URL: .Camera, .Event, .Extra
- url: "https://example.com/webhooks/{{ .Camera }}/events/{{ .Event }}"
# YOU CAN ALSO USE TEMPLATE VARIABLES IN THE PAYLOAD BODY!
# BELOW EXAMPLE DELIVERS RAW EVENT TO THE ENDPOINT
bodyTemplate: '{{ .Extra }}'

- url: "https://api.telegram.org/bot121212121:token/sendMessage?chat_id=43434343434&text=hello"

# SIMPLE SHORTHAND FORM FOR THE SAME STUFF AS ABOVE, WILL PERFORM A POST TO EACH URL
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ func main() {
}
}

messageHandler := func(topic string, data string) {
messageHandler := func(cameraName string, eventType string, extra string) {
if config.Mqtt.Enabled {
mqttBus.SendMessage(config.Mqtt.TopicRoot+"/"+topic, data)
mqttBus.SendMessage(config.Mqtt.TopicRoot+"/"+cameraName+"/"+eventType, extra)
}
if config.Webhooks.Enabled {
webhookBus.SendMessage(topic, data)
webhookBus.SendMessage(cameraName, eventType, extra)
}
}

Expand Down
8 changes: 4 additions & 4 deletions servers/dahua/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Server struct {
Debug bool
WaitGroup *sync.WaitGroup
Cameras *[]DhCamera
MessageHandler func(topic string, data string)
MessageHandler func(cameraName string, eventType string, extra string)
}

type DhEvent struct {
Expand Down Expand Up @@ -254,8 +254,8 @@ func (server *Server) Start() {

if server.MessageHandler == nil {
fmt.Println("DAHUA: Message handler is not set for Dahua cams - that's probably not what you want")
server.MessageHandler = func(topic string, data string) {
fmt.Printf("DAHUA: Lost alarm: %s: %s\n", topic, data)
server.MessageHandler = func(cameraName string, eventType string, extra string) {
fmt.Printf("DAHUA: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}

Expand All @@ -277,7 +277,7 @@ func (server *Server) Start() {

for {
event := <-channel
go server.MessageHandler(event.Camera.Name+"/"+event.Type, event.Message)
go server.MessageHandler(event.Camera.Name, event.Type, event.Message)
}
}(&waitGroup, eventChannel)

Expand Down
8 changes: 4 additions & 4 deletions servers/ftp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Server struct {
AllowFiles bool
RootPath string
Password string
MessageHandler func(topic string, data string)
MessageHandler func(cameraName string, eventType string, extra string)
}

type Event struct {
Expand All @@ -25,8 +25,8 @@ type Event struct {
func (serv *Server) Start() {
if serv.MessageHandler == nil {
fmt.Println("FTP: Message handler is not set for FTP server - that's probably not what you want")
serv.MessageHandler = func(topic string, data string) {
fmt.Printf("FTP: Lost alarm: %s: %s\n", topic, data)
serv.MessageHandler = func(cameraName string, eventType string, extra string) {
fmt.Printf("FTP: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}
// DEFAULT FTP PASSWORD
Expand All @@ -44,7 +44,7 @@ func (serv *Server) Start() {
go func(channel <-chan Event) {
for {
event := <-channel
go serv.MessageHandler(event.CameraName+"/"+event.Type, event.Message)
go serv.MessageHandler(event.CameraName, event.Type, event.Message)
}
}(eventChannel)

Expand Down
8 changes: 4 additions & 4 deletions servers/hikvision/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type Server struct {
Debug bool
WaitGroup *sync.WaitGroup
Cameras *[]HikCamera
MessageHandler func(topic string, data string)
MessageHandler func(cameraName string, eventType string, extra string)
}

type XmlEvent struct {
Expand Down Expand Up @@ -153,8 +153,8 @@ func (server *Server) Start() {

if server.MessageHandler == nil {
fmt.Println("HIK: Message handler is not set for Hikvision cams - that's probably not what you want")
server.MessageHandler = func(topic string, data string) {
fmt.Printf("HIK: Lost alarm: %s: %s\n", topic, data)
server.MessageHandler = func(cameraName string, eventType string, extra string) {
fmt.Printf("HIK: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}

Expand All @@ -176,7 +176,7 @@ func (server *Server) Start() {
server.WaitGroup.Add(1)
for {
event := <-channel
go server.MessageHandler(event.Camera.Name+"/"+event.Type, event.Message)
go server.MessageHandler(event.Camera.Name, event.Type, event.Message)
}
}(&cameraWaitGroup, eventChannel)

Expand Down
8 changes: 4 additions & 4 deletions servers/hisilicon/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Server struct {
Debug bool
WaitGroup *sync.WaitGroup
Port string
MessageHandler func(topic string, data string)
MessageHandler func(cameraName string, eventType string, extra string)
}

func (server *Server) handleTcpConnection(conn net.Conn) {
Expand Down Expand Up @@ -93,7 +93,7 @@ func (server *Server) handleTcpConnection(conn net.Conn) {
serialId := fmt.Sprintf("%v", dataMap["SerialID"])
event := fmt.Sprintf("%v", dataMap["Event"])

server.MessageHandler(serialId+"/"+event, string(jsonBytes))
server.MessageHandler(serialId, event, string(jsonBytes))
}

func (server *Server) Start() {
Expand All @@ -102,8 +102,8 @@ func (server *Server) Start() {
}
if server.MessageHandler == nil {
fmt.Println("HISI: Message handler is not set for HiSilicon cams - that's probably not what you want")
server.MessageHandler = func(topic string, data string) {
fmt.Printf("HISI: Lost alarm: %s: %s\n", topic, data)
server.MessageHandler = func(cameraName string, eventType string, extra string) {
fmt.Printf("HISI: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}

Expand Down

0 comments on commit b154b93

Please sign in to comment.