diff --git a/main.go b/main.go
index 392c70b..3560cd6 100644
--- a/main.go
+++ b/main.go
@@ -3,6 +3,7 @@ package main
import (
"flag"
"fmt"
+ "html/template"
"log"
"net"
"os"
@@ -23,17 +24,12 @@ type TgSender struct {
bot *tgbotapi.BotAPI
}
-func (self TgSender) Send(msg GenericMessage) error {
- chatIds := msg.chatIds.GetTgIds()
+func (self TgSender) Send(msg GenericMessage, tpl *template.Template) error {
+ chatIds := msg.ChatIds.GetTgIds()
if chatIds == nil {
return nil
}
- b := strings.Builder{}
- for _, msg := range msg.msgs {
- b.WriteString(msg.TgString())
- b.WriteRune('\n')
- }
- message := b.String()
+ message := msg.Format(tpl)
for _, chatId := range *chatIds {
toSend := tgbotapi.NewMessage(chatId, message)
toSend.ParseMode = "HTML"
@@ -53,14 +49,6 @@ type RealSleeper struct {
duration time.Duration
}
-type SatelMsgContent struct {
- ev satel.Event
-}
-
-func (self SatelMsgContent) TgString() string {
- return fmt.Sprint("", self.ev.Type, ", index:", self.ev.Index, ", value:", self.ev.Value, "")
-}
-
func (self RealSleeper) Sleep(ch chan<- interface{}) {
go func() {
time.Sleep(self.duration)
@@ -146,10 +134,12 @@ func main() {
tgSender := TgSender{bot}
+ tpl := template.Must(template.New("TelegramMessage").Parse(TelegramMessageTemplate))
+
Consume(
SendToTg(
tgSenderWorker(tgEvents, &wg, sleeper, log.New(os.Stderr, "TgSender", log.Lmicroseconds)),
- tgSender, &wg, log.New(os.Stderr, "SendToTg", log.Lmicroseconds)))
+ tgSender, &wg, log.New(os.Stderr, "SendToTg", log.Lmicroseconds), tpl))
go CloseSatelOnCtrlC(s)
@@ -159,7 +149,7 @@ func main() {
allowedIndexes) {
logger.Print("Received change from SATEL: ", e)
for _, chatId := range chatIds {
- sendTgMessage(tgEvents, SatelMsgContent{e}, chatId)
+ sendTgMessage(tgEvents, MsgContent{e}, chatId)
}
}
diff --git a/message_contents.go b/message_contents.go
new file mode 100644
index 0000000..758b0ea
--- /dev/null
+++ b/message_contents.go
@@ -0,0 +1,124 @@
+package main
+
+import (
+ "fmt"
+ "html/template"
+ "strings"
+
+ "github.com/probakowski/go-satel"
+)
+
+type MsgContent struct {
+ SatelEvent satel.Event
+}
+
+type GenericMessage struct {
+ ChatIds ChatId
+ Messages []MsgContent
+}
+
+func (self GenericMessage) Format(template *template.Template) string {
+ b := strings.Builder{}
+ template.Execute(&b, self)
+ return b.String()
+}
+
+func getEmojiWhenTrueIsGood(v bool) string {
+ if v {
+ return "✅"
+ } else {
+ return "🔴"
+ }
+}
+
+func getEmojiWhenTrueIsBad(v bool) string {
+ if v {
+ return "🔴"
+ } else {
+ return "✅"
+ }
+}
+
+func (self MsgContent) FormatEvent() string {
+ switch self.SatelEvent.Type {
+ case satel.ZoneViolation:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsBad(self.SatelEvent.Value))
+ case satel.ZoneTamper:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsBad(self.SatelEvent.Value))
+ case satel.ZoneAlarm:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsBad(self.SatelEvent.Value))
+ case satel.ZoneTamperAlarm:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsBad(self.SatelEvent.Value))
+ case satel.ZoneAlarmMemory:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ZoneTamperAlarmMemory:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ZoneBypass:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ZoneNoViolationTrouble:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ZoneLongViolationTrouble:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ArmedPartitionSuppressed:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ArmedPartition:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsGood(self.SatelEvent.Value))
+ case satel.PartitionArmedInMode2:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionArmedInMode3:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionWith1stCodeEntered:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionEntryTime:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionExitTimeOver10s:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionExitTimeUnder10s:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionTemporaryBlocked:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionBlockedForGuardRound:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionAlarm:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsBad(self.SatelEvent.Value))
+ case satel.PartitionFireAlarm:
+ return fmt.Sprintf("%s: %s", self.SatelEvent.Type.String(), getEmojiWhenTrueIsBad(self.SatelEvent.Value))
+ case satel.PartitionAlarmMemory:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionFireAlarmMemory:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.Output:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.DoorOpened:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.DoorOpenedLong:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.StatusBit:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroublePart1:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroublePart2:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroublePart3:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroublePart4:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroublePart5:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroubleMemoryPart1:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroubleMemoryPart2:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroubleMemoryPart3:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroubleMemoryPart4:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.TroubleMemoryPart5:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.PartitionWithViolatedZones:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ case satel.ZoneIsolate:
+ return fmt.Sprintf("%s: %t", self.SatelEvent.Type.String(), self.SatelEvent.Value)
+ }
+ panic(fmt.Sprint("Unknown event received: ", self.SatelEvent))
+}
diff --git a/sender_worker.go b/sender_worker.go
index af41ce3..faafd64 100644
--- a/sender_worker.go
+++ b/sender_worker.go
@@ -1,12 +1,13 @@
package main
import (
+ "html/template"
"log"
"sync"
)
type Sender interface {
- Send(msg GenericMessage) error
+ Send(msg GenericMessage, tpl *template.Template) error
}
type Sleeper interface {
@@ -32,22 +33,7 @@ func Consume(events <-chan GenericMessage) {
}()
}
-type MsgContent interface {
- TgString() string
-}
-
-type StringMsgContent struct {
- msg string
-}
-
-func (self StringMsgContent) TgString() string { return self.msg }
-
-type GenericMessage struct {
- chatIds ChatId
- msgs []MsgContent
-}
-
-func SendToTg(events <-chan GenericMessage, s Sender, wg *sync.WaitGroup, logger *log.Logger) <-chan GenericMessage {
+func SendToTg(events <-chan GenericMessage, s Sender, wg *sync.WaitGroup, logger *log.Logger, tpl *template.Template) <-chan GenericMessage {
returnEvents := make(chan GenericMessage)
go func() {
@@ -55,7 +41,7 @@ func SendToTg(events <-chan GenericMessage, s Sender, wg *sync.WaitGroup, logger
defer wg.Done()
for e := range events {
returnEvents <- e
- err := s.Send(e)
+ err := s.Send(e, tpl)
if err != nil {
// TODO: handle it better
panic(err)
@@ -86,11 +72,11 @@ func tgSenderWorker(tgEvents <-chan GenericMessage, wg *sync.WaitGroup, sleeper
break loop
}
// Collect all messages to send them at once
- _, messageBuilderExists := messagesToSend[ev.chatIds]
+ _, messageBuilderExists := messagesToSend[ev.ChatIds]
if !messageBuilderExists {
- messagesToSend[ev.chatIds] = make([]MsgContent, 0)
+ messagesToSend[ev.ChatIds] = make([]MsgContent, 0)
}
- messagesToSend[ev.chatIds] = append(messagesToSend[ev.chatIds], ev.msgs...)
+ messagesToSend[ev.ChatIds] = append(messagesToSend[ev.ChatIds], ev.Messages...)
if !waitingStarted {
logger.Print("Waiting for more messages to arrive before sending...")
waitingStarted = true
diff --git a/sender_worker_test.go b/sender_worker_test.go
index c7922f1..784369a 100644
--- a/sender_worker_test.go
+++ b/sender_worker_test.go
@@ -1,11 +1,13 @@
package main
import (
+ "html/template"
"io"
"log"
"sync"
"testing"
+ "github.com/probakowski/go-satel"
"github.com/stretchr/testify/assert"
)
@@ -13,7 +15,7 @@ type MockSender struct {
messages []GenericMessage
}
-func (self *MockSender) Send(msg GenericMessage) error {
+func (self *MockSender) Send(msg GenericMessage, tpl *template.Template) error {
self.messages = append(self.messages, msg)
return nil
}
@@ -38,28 +40,37 @@ func (self FakeChatId) GetTgIds() *[]int64 {
return nil
}
+var (
+ messageTest1 = satel.Event{Type: satel.ArmedPartition, Index: 1, Value: true}
+ messageTest2 = satel.Event{Type: satel.ArmedPartition, Index: 2, Value: true}
+ messageTest3 = satel.Event{Type: satel.ArmedPartition, Index: 3, Value: true}
+ messageTest4 = satel.Event{Type: satel.ArmedPartition, Index: 4, Value: true}
+ messageTest5 = satel.Event{Type: satel.ArmedPartition, Index: 5, Value: true}
+ messageTest6 = satel.Event{Type: satel.ArmedPartition, Index: 6, Value: true}
+)
+
func TestMessageThrottling(t *testing.T) {
testEvents := make(chan GenericMessage)
wg := sync.WaitGroup{}
mockSender := MockSender{make([]GenericMessage, 0)}
mockSleeper := MockSleeper{nil, 0}
Consume(SendToTg(tgSenderWorker(testEvents, &wg, &mockSleeper, log.New(io.Discard, "", log.Ltime)),
- &mockSender, &wg, log.New(io.Discard, "", log.Ltime)))
- testEvents <- GenericMessage{TgChatId{123}, []MsgContent{StringMsgContent{"test1"}}}
- testEvents <- GenericMessage{TgChatId{124}, []MsgContent{StringMsgContent{"test3"}}}
- testEvents <- GenericMessage{TgChatId{123}, []MsgContent{StringMsgContent{"test2"}}}
- testEvents <- GenericMessage{TgChatId{124}, []MsgContent{StringMsgContent{"test4"}}}
- testEvents <- GenericMessage{FakeChatId{123}, []MsgContent{StringMsgContent{"testFake"}}}
+ &mockSender, &wg, log.New(io.Discard, "", log.Ltime), nil))
+ testEvents <- GenericMessage{TgChatId{123}, []MsgContent{{messageTest1}}}
+ testEvents <- GenericMessage{TgChatId{124}, []MsgContent{{messageTest3}}}
+ testEvents <- GenericMessage{TgChatId{123}, []MsgContent{{messageTest2}}}
+ testEvents <- GenericMessage{TgChatId{124}, []MsgContent{{messageTest4}}}
+ testEvents <- GenericMessage{FakeChatId{123}, []MsgContent{{messageTest6}}}
assert.Equal(t, 1, mockSleeper.callCount)
*mockSleeper.ch <- nil
assert.Equal(t, 1, mockSleeper.callCount)
- testEvents <- GenericMessage{TgChatId{123}, []MsgContent{StringMsgContent{"test5"}}}
+ testEvents <- GenericMessage{TgChatId{123}, []MsgContent{{messageTest5}}}
close(testEvents)
wg.Wait()
assert.Equal(t, 2, mockSleeper.callCount)
assert.Len(t, mockSender.messages, 4)
- assert.Contains(t, mockSender.messages, GenericMessage{TgChatId{123}, []MsgContent{StringMsgContent{"test1"}, StringMsgContent{"test2"}}})
- assert.Contains(t, mockSender.messages, GenericMessage{TgChatId{124}, []MsgContent{StringMsgContent{"test3"}, StringMsgContent{"test4"}}})
- assert.Contains(t, mockSender.messages, GenericMessage{FakeChatId{123}, []MsgContent{StringMsgContent{"testFake"}}})
+ assert.Contains(t, mockSender.messages, GenericMessage{TgChatId{123}, []MsgContent{{messageTest1}, {messageTest2}}})
+ assert.Contains(t, mockSender.messages, GenericMessage{TgChatId{124}, []MsgContent{{messageTest3}, {messageTest4}}})
+ assert.Contains(t, mockSender.messages, GenericMessage{FakeChatId{123}, []MsgContent{{messageTest6}}})
}
diff --git a/templates.go b/templates.go
new file mode 100644
index 0000000..4109bab
--- /dev/null
+++ b/templates.go
@@ -0,0 +1,8 @@
+package main
+
+const TelegramMessageTemplate = `Received following changes:
+{{- range .Messages}}
+:: {{.SatelEvent.Index}}: {{.FormatEvent}}
+{{- else -}}
+Huh, no messages - this is a bug
+{{- end}}`
diff --git a/templates_test.go b/templates_test.go
new file mode 100644
index 0000000..bb7a7fe
--- /dev/null
+++ b/templates_test.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "html/template"
+ "io"
+ "log"
+ "sync"
+ "testing"
+
+ "github.com/probakowski/go-satel"
+ "github.com/stretchr/testify/assert"
+)
+
+type MockTemplateSender struct {
+ message string
+}
+
+func (self *MockTemplateSender) Send(msg GenericMessage, tpl *template.Template) error {
+ self.message = msg.Format(tpl)
+ return nil
+}
+
+var (
+ tplMessageTest1 = satel.Event{Type: satel.ArmedPartition, Index: 1, Value: true}
+ tplMessageTest2 = satel.Event{Type: satel.ZoneViolation, Index: 2, Value: true}
+)
+
+func TestTelegramTemplate(t *testing.T) {
+ testEvents := make(chan GenericMessage)
+ wg := sync.WaitGroup{}
+ mockSender := MockTemplateSender{}
+ tpl, err := template.New("TestTemplate").Parse(TelegramMessageTemplate)
+ assert.NoError(t, err)
+ Consume(SendToTg(testEvents, &mockSender, &wg, log.New(io.Discard, "", log.Ltime), tpl))
+ testEvents <- GenericMessage{TgChatId{123}, []MsgContent{{tplMessageTest1}, {tplMessageTest2}}}
+ close(testEvents)
+ wg.Wait()
+
+ // assert.Equal(t, "siemka", mockSender.message)
+}