2024-02-08 18:23:46 +00:00
package main
import (
2024-02-09 22:32:32 +00:00
"flag"
2024-02-08 18:23:46 +00:00
"fmt"
2024-02-19 18:52:40 +00:00
"html/template"
2024-02-11 21:48:05 +00:00
"log"
2024-02-08 18:23:46 +00:00
"net"
2024-02-09 22:32:32 +00:00
"os"
2024-03-03 12:54:42 +00:00
"path/filepath"
2024-02-08 19:57:16 +00:00
"strconv"
2024-02-09 22:32:32 +00:00
"strings"
"sync"
"time"
2024-02-08 18:23:46 +00:00
2024-03-04 20:11:34 +00:00
"git.sr.ht/~michalr/go-satel"
2024-02-08 18:23:46 +00:00
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
2024-02-09 22:32:32 +00:00
const (
2024-02-10 23:13:31 +00:00
MessageNotMoreOftenThanSeconds = 15
2024-02-28 20:26:20 +00:00
PersistenceFilename = "hs_wro_last_seen.bin"
2024-02-09 22:32:32 +00:00
)
2024-02-10 23:13:31 +00:00
type TgSender struct {
2024-03-04 20:11:34 +00:00
bot * tgbotapi . BotAPI
s SatelNameGetter
logger * log . Logger
2024-02-09 22:32:32 +00:00
}
2024-02-19 18:52:40 +00:00
func ( self TgSender ) Send ( msg GenericMessage , tpl * template . Template ) error {
chatIds := msg . ChatIds . GetTgIds ( )
2024-02-18 08:42:27 +00:00
if chatIds == nil {
return nil
}
2024-03-04 20:11:34 +00:00
message := msg . Format ( tpl , self . s , self . logger )
2024-02-18 08:42:27 +00:00
for _ , chatId := range * chatIds {
toSend := tgbotapi . NewMessage ( chatId , message )
2024-02-18 16:28:32 +00:00
toSend . ParseMode = "HTML"
2024-02-18 08:42:27 +00:00
_ , err := self . bot . Send ( toSend )
if err != nil {
return err
}
}
return nil
2024-02-10 06:51:36 +00:00
}
2024-03-05 21:30:48 +00:00
func sendTgMessage ( tgEvents chan GenericMessage , msg [ ] satel . BasicEventElement , chatId int64 ) {
tgEvents <- GenericMessage { TgChatId { chatId } , msg }
2024-02-10 06:51:36 +00:00
}
2024-02-11 10:51:41 +00:00
type RealSleeper struct {
duration time . Duration
}
func ( self RealSleeper ) Sleep ( ch chan <- interface { } ) {
2024-02-11 21:51:33 +00:00
go func ( ) {
time . Sleep ( self . duration )
ch <- nil
} ( )
2024-02-11 10:51:41 +00:00
}
2024-03-06 07:00:39 +00:00
func getCmdLineParams ( logger * log . Logger ) ( string , [ ] int64 , [ ] satel . ChangeType , [ ] int , time . Duration ) {
2024-02-11 13:56:03 +00:00
satelApiAddr := flag . String ( "satel-addr" , "" , "Address that should be used to connect to the SATEL device" )
satelApiPort := flag . String ( "satel-port" , "7094" , "Port that should be used to connect to the SATEL device" )
chatIdRaw := flag . String ( "tg-chat-id" , "" , "Telegram Chat ID where to send updates. Use \",\" to specify multiple IDs." )
2024-02-18 17:44:08 +00:00
allowedTypesRaw := flag . String ( "allowed-types" , "" , "Satel change types that are allowed. All other types will be discarded. By default all are allowed. Use \",\" to specify multiple types." )
2024-02-18 18:00:49 +00:00
allowedIndexesRaw := flag . String ( "allowed-indexes" , "" , "Satel indexes (zones?) that are allowed. All other indexes will be discarded. By default all are allowed. Use \",\" to specify multiple indexes." )
2024-03-06 07:00:39 +00:00
satelPoolInterval := flag . Duration ( "pool-interval" , 5 * time . Second , "How often should the SATEL device be pooled for changes? Default: 5 seconds." )
2024-02-08 18:23:46 +00:00
flag . Parse ( )
2024-02-11 13:56:03 +00:00
if len ( * satelApiAddr ) == 0 || len ( * satelApiPort ) == 0 || len ( * chatIdRaw ) == 0 {
2024-02-11 21:48:05 +00:00
logger . Fatal ( "Use --satel-addr=ADDR, --satel-port=PORT and --tg-chat-id=CHAT_ID command line flags to continue." )
2024-02-08 18:23:46 +00:00
}
2024-02-11 13:56:03 +00:00
chatIdsStrings := strings . Split ( * chatIdRaw , "," )
var chatIds [ ] int64
for _ , chatIdStr := range chatIdsStrings {
chatId , err := strconv . ParseInt ( chatIdStr , 10 , 64 )
2024-02-08 19:57:16 +00:00
if err != nil {
2024-02-11 21:48:05 +00:00
logger . Fatalf ( "Tried to use a non-int value for one of tg_chatIds: %s. That's bad." , chatIdStr )
2024-02-08 19:57:16 +00:00
}
2024-02-11 13:56:03 +00:00
chatIds = append ( chatIds , chatId )
2024-02-08 19:57:16 +00:00
}
2024-02-18 17:44:08 +00:00
allowedTypesStrings := strings . Split ( * allowedTypesRaw , "," )
var allowedTypes [ ] satel . ChangeType
for _ , allowedTypeStr := range allowedTypesStrings {
2024-02-18 18:19:10 +00:00
if len ( allowedTypeStr ) == 0 {
continue
}
2024-02-18 17:44:08 +00:00
allowedType , err := StringToSatelChangeType ( allowedTypeStr )
if err != nil {
logger . Fatalf ( "Error trying to understand an allowed type: %s." , err )
}
allowedTypes = append ( allowedTypes , allowedType )
}
2024-02-18 18:00:49 +00:00
allowedIndexesStrings := strings . Split ( * allowedIndexesRaw , "," )
var allowedIndexes [ ] int
for _ , allowedIndexStr := range allowedIndexesStrings {
2024-02-18 18:19:10 +00:00
if len ( allowedIndexStr ) == 0 {
continue
}
2024-02-18 18:00:49 +00:00
allowedIndex , err := strconv . ParseInt ( allowedIndexStr , 10 , 0 )
if err != nil {
logger . Fatalf ( "Tried to use a non-int value for one of allowed indexes: %s. That's bad." , allowedIndexStr )
}
allowedIndexes = append ( allowedIndexes , int ( allowedIndex ) )
}
2024-02-08 18:23:46 +00:00
2024-02-11 13:56:03 +00:00
satelAddr := fmt . Sprintf ( "%s:%s" , * satelApiAddr , * satelApiPort )
2024-03-06 07:00:39 +00:00
return satelAddr , chatIds , allowedTypes , allowedIndexes , * satelPoolInterval
2024-02-11 13:56:03 +00:00
}
2024-03-06 07:00:39 +00:00
func makeSatel ( satelAddr string , poolInterval time . Duration ) * satel . Satel {
2024-02-11 13:56:03 +00:00
satelConn , err := net . Dial ( "tcp" , satelAddr )
2024-02-08 18:23:46 +00:00
if err != nil {
panic ( err )
}
2024-03-06 07:00:39 +00:00
return satel . NewConfig ( satelConn , satel . Config { EventsQueueSize : 10 , PoolingInterval : poolInterval } )
2024-02-11 13:56:03 +00:00
}
2024-03-03 12:54:42 +00:00
func getPersistenceFilePath ( ) string {
var stateDir = os . Getenv ( "STATE_DIRECTORY" )
if len ( stateDir ) != 0 {
return filepath . Join ( stateDir , PersistenceFilename )
}
return PersistenceFilename
}
2024-02-11 13:56:03 +00:00
func main ( ) {
var (
wg sync . WaitGroup
2024-03-05 21:30:48 +00:00
tgEvents = make ( chan GenericMessage , 5 )
2024-02-11 21:48:05 +00:00
logger = log . New ( os . Stderr , "Main" , log . Lmicroseconds )
2024-02-11 13:56:03 +00:00
)
2024-03-06 07:00:39 +00:00
satelAddr , chatIds , allowedTypes , allowedIndexes , poolInterval := getCmdLineParams ( logger )
2024-02-11 13:56:03 +00:00
2024-03-06 07:00:39 +00:00
s := makeSatel ( satelAddr , poolInterval )
2024-02-11 21:48:05 +00:00
logger . Printf ( "Connected to Satel: %s" , satelAddr )
2024-02-11 13:56:03 +00:00
2024-02-08 18:23:46 +00:00
bot , err := tgbotapi . NewBotAPI ( os . Getenv ( "TELEGRAM_APITOKEN" ) )
2024-02-09 22:32:32 +00:00
if err != nil {
panic ( err )
}
2024-02-11 21:48:05 +00:00
logger . Print ( "Created Telegram Bot API client" )
2024-02-09 22:32:32 +00:00
2024-03-04 20:11:34 +00:00
tgSender := TgSender { bot , s , log . New ( os . Stderr , "TgFormatter" , log . Lmicroseconds ) }
2024-02-18 16:28:32 +00:00
2024-02-19 18:52:40 +00:00
tpl := template . Must ( template . New ( "TelegramMessage" ) . Parse ( TelegramMessageTemplate ) )
2024-03-03 12:54:42 +00:00
dataStore := MakeDataStore ( log . New ( os . Stderr , "DataStore" , log . Lmicroseconds ) , getPersistenceFilePath ( ) )
2024-02-28 20:26:20 +00:00
2024-03-06 17:45:12 +00:00
Consume ( NotifyViaHTTP (
SendToTg ( tgEvents , tgSender , & wg , log . New ( os . Stderr , "SendToTg" , log . Lmicroseconds ) , tpl ) ,
& wg ,
logger ,
) )
2024-02-18 16:28:32 +00:00
2024-02-18 18:36:54 +00:00
go CloseSatelOnCtrlC ( s )
2024-03-05 22:01:38 +00:00
for e := range FilterByTypeOrIndex (
FilterByLastSeen ( s . Events , & dataStore , log . New ( os . Stderr , "FilterByLastSeen" , log . Lmicroseconds ) ) ,
allowedTypes , allowedIndexes ) {
2024-02-11 21:56:58 +00:00
logger . Print ( "Received change from SATEL: " , e )
2024-02-18 16:28:32 +00:00
for _ , chatId := range chatIds {
2024-03-05 21:30:48 +00:00
sendTgMessage ( tgEvents , e . BasicEvents , chatId )
2024-02-18 16:28:32 +00:00
}
2024-02-08 18:23:46 +00:00
}
2024-02-09 22:32:32 +00:00
2024-02-11 13:56:03 +00:00
close ( tgEvents )
2024-02-09 22:32:32 +00:00
wg . Wait ( )
2024-02-08 18:23:46 +00:00
}