More reorganizing, new throttling
This commit is contained in:
parent
7f3b5a4abe
commit
6f0f78907c
|
@ -4,7 +4,7 @@ packages:
|
||||||
tasks:
|
tasks:
|
||||||
- go-get: |
|
- go-get: |
|
||||||
cd hswro-alarm-bot
|
cd hswro-alarm-bot
|
||||||
go get
|
go get -t
|
||||||
- build-x86_64: |
|
- build-x86_64: |
|
||||||
cd hswro-alarm-bot
|
cd hswro-alarm-bot
|
||||||
env GOOS=linux GOARCH=amd64 go build -o alarm_bot.x86-64
|
env GOOS=linux GOARCH=amd64 go build -o alarm_bot.x86-64
|
||||||
|
|
|
@ -12,4 +12,4 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: go-fmt
|
- id: go-fmt
|
||||||
args: [ -w ]
|
args: [ -w ]
|
||||||
- id: go-vet
|
- id: go-vet-mod
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -5,4 +5,11 @@ go 1.19
|
||||||
require (
|
require (
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||||
github.com/probakowski/go-satel v0.0.0-20211120120346-bed9818777ce
|
github.com/probakowski/go-satel v0.0.0-20211120120346-bed9818777ce
|
||||||
|
github.com/stretchr/testify v1.8.4
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
10
go.sum
10
go.sum
|
@ -1,10 +1,20 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/probakowski/go-satel v0.0.0-20211120120346-bed9818777ce h1:V21PmRMDowz+5pA7zn0YTVLnoGEEFqj14UN6/f3zRiY=
|
github.com/probakowski/go-satel v0.0.0-20211120120346-bed9818777ce h1:V21PmRMDowz+5pA7zn0YTVLnoGEEFqj14UN6/f3zRiY=
|
||||||
github.com/probakowski/go-satel v0.0.0-20211120120346-bed9818777ce/go.mod h1:q3DquDWRcoFWZ61dGZFg3snucolljixMoAzJIiCjWoY=
|
github.com/probakowski/go-satel v0.0.0-20211120120346-bed9818777ce/go.mod h1:q3DquDWRcoFWZ61dGZFg3snucolljixMoAzJIiCjWoY=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
71
main.go
71
main.go
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -16,68 +15,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaximumMessagesPerMinute = 2
|
MessageNotMoreOftenThanSeconds = 15
|
||||||
NanosecondsPerMinute = 6e10
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TgEvent struct {
|
type TgSender struct {
|
||||||
msg tgbotapi.MessageConfig
|
bot *tgbotapi.BotAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
type SecurityEvent interface {
|
func (self TgSender) Send(msg GenericMessage) error {
|
||||||
Execute(chat_ids []int64, tg_events chan TgEvent)
|
to_send := tgbotapi.NewMessage(msg.chat_id, msg.msg)
|
||||||
|
_, err := self.bot.Send(to_send)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
type SatelEvent struct {
|
func send_tg_message(tg_events chan GenericMessage, msg string, chat_ids []int64) {
|
||||||
ev satel.Event
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SatelEvent) Execute(chat_ids []int64, tg_events chan TgEvent) {
|
|
||||||
fmt.Println("Change from SATEL: ", "type", s.ev.Type, "index", s.ev.Index, "value", s.ev.Value)
|
|
||||||
for _, chat_id := range chat_ids {
|
for _, chat_id := range chat_ids {
|
||||||
msg := tgbotapi.NewMessage(chat_id, fmt.Sprintf("Change from SATEL: Zone: %d Type: %s Value: %t",
|
tg_events <- GenericMessage{chat_id, msg}
|
||||||
s.ev.Index, s.ev.Type, s.ev.Value))
|
|
||||||
|
|
||||||
send_tg_message(tg_events, msg)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmptyEvent struct{}
|
|
||||||
|
|
||||||
func (s EmptyEvent) Execute(chat_ids []int64, tg_events chan TgEvent) {}
|
|
||||||
|
|
||||||
func tg_sender_worker(tg_events <-chan TgEvent, bot *tgbotapi.BotAPI, wg *sync.WaitGroup) {
|
|
||||||
wg.Add(1)
|
|
||||||
defer wg.Done()
|
|
||||||
lastExecutionTime := time.Now()
|
|
||||||
minimumTimeBetweenEachExecution := time.Duration(math.Ceil(NanosecondsPerMinute / MaximumMessagesPerMinute))
|
|
||||||
_, shouldOmitTg := os.LookupEnv("OMIT_TG")
|
|
||||||
|
|
||||||
for ev := range tg_events {
|
|
||||||
defer func() { lastExecutionTime = time.Now() }()
|
|
||||||
|
|
||||||
time.Sleep(minimumTimeBetweenEachExecution - time.Since(lastExecutionTime))
|
|
||||||
fmt.Println("Will send to TG: ", ev.msg.Text)
|
|
||||||
if shouldOmitTg {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err := bot.Send(ev.msg)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: handle it better
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func send_tg_message(tg_events chan TgEvent, msg tgbotapi.MessageConfig) {
|
|
||||||
tg_events <- TgEvent{msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
tg_events = make(chan TgEvent)
|
tg_events = make(chan GenericMessage)
|
||||||
sec_events = make(chan SecurityEvent)
|
|
||||||
)
|
)
|
||||||
satel_api_addr := flag.String("satel-addr", "", "Address that should be used to connect to the SATEL device")
|
satel_api_addr := flag.String("satel-addr", "", "Address that should be used to connect to the SATEL device")
|
||||||
satel_api_port := flag.String("satel-port", "7094", "Port that should be used to connect to the SATEL device")
|
satel_api_port := flag.String("satel-port", "7094", "Port that should be used to connect to the SATEL device")
|
||||||
|
@ -110,13 +70,10 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go tg_sender_worker(tg_events, bot, &wg)
|
tgSender := TgSender{bot}
|
||||||
|
go tg_sender_worker(tg_events, tgSender, &wg, time.Second*15)
|
||||||
for e, ok := <-s.Events; ok; e, ok = <-s.Events {
|
for e, ok := <-s.Events; ok; e, ok = <-s.Events {
|
||||||
sec_events <- SatelEvent{e}
|
send_tg_message(tg_events, fmt.Sprintln("Change from SATEL: ", "type", e.Type, "index", e.Index, "value", e.Value), chat_ids)
|
||||||
}
|
|
||||||
|
|
||||||
for ev := range sec_events {
|
|
||||||
ev.Execute(chat_ids, tg_events)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(tg_events)
|
close(tg_events)
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GenericMessage struct {
|
||||||
|
chat_id int64
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sender interface {
|
||||||
|
Send(msg GenericMessage) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func tg_sender_worker(tg_events <-chan GenericMessage, s Sender, wg *sync.WaitGroup, messageNotMoreOftenThan time.Duration) {
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
messagesToSend := make(map[int64]*strings.Builder)
|
||||||
|
waitingStarted := false
|
||||||
|
timeoutEvents := make(chan interface{})
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case ev, ok := <-tg_events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Collect all messages to send them at once
|
||||||
|
_, messageBuilderExists := messagesToSend[ev.chat_id]
|
||||||
|
if !messageBuilderExists {
|
||||||
|
messagesToSend[ev.chat_id] = &strings.Builder{}
|
||||||
|
}
|
||||||
|
messagesToSend[ev.chat_id].WriteString(ev.msg)
|
||||||
|
messagesToSend[ev.chat_id].WriteRune('\n')
|
||||||
|
if !waitingStarted {
|
||||||
|
waitingStarted = true
|
||||||
|
go func() {
|
||||||
|
time.Sleep(messageNotMoreOftenThan)
|
||||||
|
timeoutEvents <- nil
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
case <-timeoutEvents:
|
||||||
|
waitingStarted = false
|
||||||
|
for chat_id, msgBuilder := range messagesToSend {
|
||||||
|
err := s.Send(GenericMessage{chat_id, msgBuilder.String()})
|
||||||
|
if err != nil {
|
||||||
|
// TODO: handle it better
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
delete(messagesToSend, chat_id)
|
||||||
|
}
|
||||||
|
if tg_events == nil {
|
||||||
|
close(timeoutEvents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tg_events == nil && timeoutEvents == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockSender struct {
|
||||||
|
messages []GenericMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *MockSender) Send(msg GenericMessage) error {
|
||||||
|
self.messages = append(self.messages, msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMessageThrottling(t *testing.T) {
|
||||||
|
testEvents := make(chan GenericMessage)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
mockSender := MockSender{make([]GenericMessage, 0)}
|
||||||
|
go tg_sender_worker(testEvents, &mockSender, &wg, time.Millisecond)
|
||||||
|
testEvents <- GenericMessage{123, "test1"}
|
||||||
|
testEvents <- GenericMessage{124, "test3"}
|
||||||
|
testEvents <- GenericMessage{123, "test2"}
|
||||||
|
testEvents <- GenericMessage{124, "test4"}
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
testEvents <- GenericMessage{123, "test5"}
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
|
||||||
|
close(testEvents)
|
||||||
|
|
||||||
|
assert.Len(t, mockSender.messages, 3)
|
||||||
|
assert.Contains(t, mockSender.messages, GenericMessage{123, "test1\ntest2\n"})
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
Loading…
Reference in New Issue