Skip to content

Commit b154b93

Browse files
committed
Webhooks: templates!
1 parent c17b7ce commit b154b93

File tree

9 files changed

+91
-29
lines changed

9 files changed

+91
-29
lines changed

Readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ _A_: Some IP cameras have ONVIF, and sometimes that even includes motion alarms,
101101

102102
- Misecu 5MP Wifi AI Cam (Hisilicon server)
103103

104+
- Lorex N8428-Z 4K NVR (Dahua server)
105+
104106
- HomeViz OB10, K4W10, OW10 (Hisilicon server, tested by [acburnett](https://github.com/acburnett))
105107

106108
If your camera works with Alarm Server - create an issue with some details about it and a picture, and we'll post it here.

buses/webhooks/webhooks.go

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import (
55
"encoding/json"
66
"fmt"
77
"github.com/toxuin/alarmserver/config"
8-
"io/ioutil"
8+
"io"
99
"net/http"
1010
"strings"
11+
"text/template"
1112
)
1213

1314
type Bus struct {
@@ -17,8 +18,9 @@ type Bus struct {
1718
}
1819

1920
type WebhookPayload struct {
20-
Topic string `json:"topic"`
21-
Data string `json:"data"`
21+
CameraName string `json:"cameraName"`
22+
EventType string `json:"eventType"`
23+
Extra string `json:"extra"`
2224
}
2325

2426
func (webhooks *Bus) Initialize(conf config.WebhooksConfig) {
@@ -42,9 +44,9 @@ func (webhooks *Bus) Initialize(conf config.WebhooksConfig) {
4244
}
4345
}
4446

45-
func (webhooks *Bus) SendMessage(topic string, data string) {
47+
func (webhooks *Bus) SendMessage(cameraName string, eventType string, extra string) {
4648
for _, webhook := range webhooks.webhooks {
47-
payload := WebhookPayload{Topic: topic, Data: data}
49+
payload := WebhookPayload{CameraName: cameraName, EventType: eventType, Extra: extra}
4850
go webhooks.send(webhook, payload)
4951
}
5052
}
@@ -56,7 +58,57 @@ func (webhooks *Bus) send(webhook config.WebhookConfig, payload WebhookPayload)
5658
return
5759
}
5860

59-
request, err := http.NewRequest(webhook.Method, webhook.Url, bytes.NewBuffer(payloadJson))
61+
var templateVars = map[string]interface{}{
62+
"Camera": payload.CameraName,
63+
"Event": payload.EventType,
64+
"Extra": payload.Extra,
65+
}
66+
67+
// PARSE WEBHOOK URL AS TEMPLATE
68+
urlTemplate, err := template.New("webhookUrl").Parse(webhook.Url)
69+
if err != nil {
70+
fmt.Printf("WEBHOOKS: Error parsing webhook URL as template: %s\n", webhook.Url)
71+
if webhooks.Debug {
72+
fmt.Println("Webhooks: Error", err)
73+
}
74+
return
75+
}
76+
var urlBuffer bytes.Buffer
77+
err = urlTemplate.Execute(&urlBuffer, templateVars)
78+
if err != nil {
79+
fmt.Printf("WEBHOOKS: Error rendering webhook URL as template: %s\n", webhook.Url)
80+
if webhooks.Debug {
81+
fmt.Println("Webhooks: Error", err)
82+
}
83+
return
84+
}
85+
url := urlBuffer.String()
86+
87+
// PARSE BODY AS TEMPLATE
88+
body := bytes.NewBuffer(payloadJson)
89+
if webhook.BodyTemplate != "" {
90+
bodyTemplate, err := template.New("payload").Parse(webhook.BodyTemplate)
91+
if err != nil {
92+
fmt.Printf("WEBHOOKS: Error parsing webhook body as template: %s\n", webhook.Url)
93+
if webhooks.Debug {
94+
fmt.Println("Webhooks: Error", err)
95+
}
96+
return
97+
}
98+
99+
var bodyBuffer bytes.Buffer
100+
err = bodyTemplate.Execute(&bodyBuffer, templateVars)
101+
if err != nil {
102+
fmt.Printf("WEBHOOKS: Error rendering webhook body as template: %s\n", webhook.Url)
103+
if webhooks.Debug {
104+
fmt.Println("Webhooks: Error", err)
105+
}
106+
return
107+
}
108+
body = &bodyBuffer
109+
}
110+
111+
request, err := http.NewRequest(webhook.Method, url, body)
60112
if err != nil {
61113
fmt.Printf("WEBHOOKS: Error creating %s request to %s\n", webhook.Method, webhook.Url)
62114
if webhooks.Debug {
@@ -93,7 +145,7 @@ func (webhooks *Bus) send(webhook config.WebhookConfig, payload WebhookPayload)
93145
)
94146
}
95147
if webhooks.Debug {
96-
bodyBytes, _ := ioutil.ReadAll(response.Body)
148+
bodyBytes, _ := io.ReadAll(response.Body)
97149
bodyStr := string(bodyBytes)
98150
if len(bodyStr) == 0 {
99151
bodyStr = "*empty*"

config/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ type WebhooksConfig struct {
3434
}
3535

3636
type WebhookConfig struct {
37-
Url string `json:"url"`
38-
Method string `json:"method"`
39-
Headers []string `json:"headers"`
37+
Url string `json:"url"`
38+
Method string `json:"method"`
39+
Headers []string `json:"headers"`
40+
BodyTemplate string `json:"bodyTemplate"`
4041
}
4142

4243
type HisiliconConfig struct {

docs/config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ webhooks:
5656
method: "GET" # DEFAULTS TO POST
5757
headers:
5858
- "X-Beep: boop"
59+
60+
# YOU CAN USE TEMPLATE VARIABLES TO FORM THE URL: .Camera, .Event, .Extra
61+
- url: "https://example.com/webhooks/{{ .Camera }}/events/{{ .Event }}"
62+
# YOU CAN ALSO USE TEMPLATE VARIABLES IN THE PAYLOAD BODY!
63+
# BELOW EXAMPLE DELIVERS RAW EVENT TO THE ENDPOINT
64+
bodyTemplate: '{{ .Extra }}'
65+
5966
- url: "https://api.telegram.org/bot121212121:token/sendMessage?chat_id=43434343434&text=hello"
6067

6168
# SIMPLE SHORTHAND FORM FOR THE SAME STUFF AS ABOVE, WILL PERFORM A POST TO EACH URL

main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ func main() {
4747
}
4848
}
4949

50-
messageHandler := func(topic string, data string) {
50+
messageHandler := func(cameraName string, eventType string, extra string) {
5151
if config.Mqtt.Enabled {
52-
mqttBus.SendMessage(config.Mqtt.TopicRoot+"/"+topic, data)
52+
mqttBus.SendMessage(config.Mqtt.TopicRoot+"/"+cameraName+"/"+eventType, extra)
5353
}
5454
if config.Webhooks.Enabled {
55-
webhookBus.SendMessage(topic, data)
55+
webhookBus.SendMessage(cameraName, eventType, extra)
5656
}
5757
}
5858

servers/dahua/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type Server struct {
2828
Debug bool
2929
WaitGroup *sync.WaitGroup
3030
Cameras *[]DhCamera
31-
MessageHandler func(topic string, data string)
31+
MessageHandler func(cameraName string, eventType string, extra string)
3232
}
3333

3434
type DhEvent struct {
@@ -254,8 +254,8 @@ func (server *Server) Start() {
254254

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

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

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

servers/ftp/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Server struct {
1313
AllowFiles bool
1414
RootPath string
1515
Password string
16-
MessageHandler func(topic string, data string)
16+
MessageHandler func(cameraName string, eventType string, extra string)
1717
}
1818

1919
type Event struct {
@@ -25,8 +25,8 @@ type Event struct {
2525
func (serv *Server) Start() {
2626
if serv.MessageHandler == nil {
2727
fmt.Println("FTP: Message handler is not set for FTP server - that's probably not what you want")
28-
serv.MessageHandler = func(topic string, data string) {
29-
fmt.Printf("FTP: Lost alarm: %s: %s\n", topic, data)
28+
serv.MessageHandler = func(cameraName string, eventType string, extra string) {
29+
fmt.Printf("FTP: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
3030
}
3131
}
3232
// DEFAULT FTP PASSWORD
@@ -44,7 +44,7 @@ func (serv *Server) Start() {
4444
go func(channel <-chan Event) {
4545
for {
4646
event := <-channel
47-
go serv.MessageHandler(event.CameraName+"/"+event.Type, event.Message)
47+
go serv.MessageHandler(event.CameraName, event.Type, event.Message)
4848
}
4949
}(eventChannel)
5050

servers/hikvision/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Server struct {
3737
Debug bool
3838
WaitGroup *sync.WaitGroup
3939
Cameras *[]HikCamera
40-
MessageHandler func(topic string, data string)
40+
MessageHandler func(cameraName string, eventType string, extra string)
4141
}
4242

4343
type XmlEvent struct {
@@ -153,8 +153,8 @@ func (server *Server) Start() {
153153

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

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

servers/hisilicon/server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type Server struct {
4141
Debug bool
4242
WaitGroup *sync.WaitGroup
4343
Port string
44-
MessageHandler func(topic string, data string)
44+
MessageHandler func(cameraName string, eventType string, extra string)
4545
}
4646

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

96-
server.MessageHandler(serialId+"/"+event, string(jsonBytes))
96+
server.MessageHandler(serialId, event, string(jsonBytes))
9797
}
9898

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

0 commit comments

Comments
 (0)