2021-10-06 18:59:11 +02:00
package main
import (
2021-10-13 14:38:31 +02:00
"bytes"
"embed"
2021-11-05 08:16:36 +01:00
"errors"
2021-10-06 18:59:11 +02:00
"fmt"
2021-10-13 14:38:31 +02:00
"os"
"path"
2021-10-06 18:59:11 +02:00
"strings"
2021-10-13 14:38:31 +02:00
"text/template"
2021-10-06 18:59:11 +02:00
telegram "github.com/go-telegram-bot-api/telegram-bot-api/v5"
log "github.com/sirupsen/logrus"
)
2021-10-13 14:38:31 +02:00
//go:embed templates
var templateFiles embed . FS
// Attachment is used to attach objects to templates
type Attachment struct {
Miner Miner
Payment Payment
Pool Pool
Block Block
Worker Worker
}
2021-10-06 18:59:11 +02:00
// Notifier interface to define how to send all kind of notifications
type Notifier interface {
NotifyBalance ( miner Miner , difference float64 ) error
NotifyPayment ( miner Miner , payment Payment ) error
NotifyBlock ( pool Pool , block Block ) error
2021-10-10 20:56:11 +02:00
NotifyOfflineWorker ( worker Worker ) error
2022-02-27 20:13:11 +01:00
NotifyTest ( client FlexpoolClient ) error
2021-10-06 18:59:11 +02:00
}
// TelegramNotifier to send notifications using Telegram
// Implements the Notifier interface
type TelegramNotifier struct {
2022-02-27 20:13:11 +01:00
bot * telegram . BotAPI
chatID int64
channelName string
configurations * NotificationsConfig
2021-10-06 18:59:11 +02:00
}
// NewTelegramNotifier to create a TelegramNotifier
2022-02-27 20:13:11 +01:00
func NewTelegramNotifier ( config * TelegramConfig , configurations * NotificationsConfig ) ( * TelegramNotifier , error ) {
2021-10-06 18:59:11 +02:00
bot , err := telegram . NewBotAPI ( config . Token )
if err != nil {
return nil , err
}
log . Debugf ( "Connected to Telegram as %s" , bot . Self . UserName )
return & TelegramNotifier {
2022-02-27 20:13:11 +01:00
bot : bot ,
chatID : config . ChatID ,
channelName : config . ChannelName ,
configurations : configurations ,
2021-10-06 18:59:11 +02:00
} , nil
}
// sendMessage to send a generic message on Telegram
func ( t * TelegramNotifier ) sendMessage ( message string ) error {
var request telegram . MessageConfig
if t . chatID != 0 {
request = telegram . NewMessage ( t . chatID , message )
} else {
request = telegram . NewMessageToChannel ( t . channelName , message )
}
request . DisableWebPagePreview = true
request . ParseMode = telegram . ModeMarkdown
response , err := t . bot . Send ( request )
if err != nil {
return err
}
log . Debugf ( "Message %d sent to Telegram" , response . MessageID )
return nil
}
2021-10-13 14:38:31 +02:00
// formatMessage to create a message with a template file name (either embeded or on disk)
func ( t * TelegramNotifier ) formatMessage ( templateFileName string , attachment interface { } ) ( message string , err error ) {
// Create template
templateName := path . Base ( templateFileName )
templateFunctions := template . FuncMap {
"upper" : strings . ToUpper ,
"lower" : strings . ToLower ,
"convertCurrency" : ConvertCurrency ,
"formatBlockURL" : FormatBlockURL ,
"formatTransactionURL" : FormatTransactionURL ,
}
tmpl := template . New ( templateName ) . Funcs ( templateFunctions )
// Parse template
2021-11-05 08:16:36 +01:00
if fileExists ( templateFileName ) {
2021-10-13 14:38:31 +02:00
log . Debugf ( "Parsing template file %s" , templateFileName )
tmpl , err = tmpl . ParseFiles ( templateFileName )
2021-11-05 08:16:36 +01:00
} else {
log . Debugf ( "Parsing embeded template file %s" , templateFileName )
tmpl , err = tmpl . ParseFS ( templateFiles , templateFileName )
2021-10-13 14:38:31 +02:00
}
if err != nil {
return "" , fmt . Errorf ( "parse failed: %v" , err )
}
// Execute template
var buffer bytes . Buffer
err = tmpl . Execute ( & buffer , attachment )
if err != nil {
return "" , fmt . Errorf ( "execute failed: %v" , err )
}
// Extract and return the formatted message
message = buffer . String ( )
return message , nil
}
2021-11-05 08:16:36 +01:00
// isFileTemplate returns true when the filename is a real file on the filesystem
func fileExists ( filename string ) bool {
_ , err := os . Stat ( filename )
return ! errors . Is ( err , os . ErrNotExist )
}
2021-10-06 18:59:11 +02:00
// NotifyBalance to format and send a notification when the unpaid balance has changed
// Implements the Notifier interface
2021-10-13 14:38:31 +02:00
func ( t * TelegramNotifier ) NotifyBalance ( miner Miner ) ( err error ) {
templateName := "templates/balance.tmpl"
2022-02-27 20:13:11 +01:00
if t . configurations . Balance . Template != "" {
templateName = t . configurations . Balance . Template
2021-10-06 18:59:11 +02:00
}
2021-10-13 14:38:31 +02:00
message , err := t . formatMessage ( templateName , Attachment { Miner : miner } )
2021-10-06 18:59:11 +02:00
if err != nil {
return err
}
return t . sendMessage ( message )
}
2022-02-27 20:13:11 +01:00
// testNotifyBalance sends a fake balance notification
func ( t * TelegramNotifier ) testNotifyBalance ( client FlexpoolClient ) error {
log . Debug ( "Testing balance notification" )
randomPool , err := client . RandomPool ( )
if err != nil {
return err
}
randomMiner , err := client . RandomMiner ( randomPool )
if err != nil {
return err
}
return t . NotifyBalance ( * randomMiner )
}
2021-10-06 18:59:11 +02:00
// NotifyPayment to format and send a notification when a new payment has been detected
// Implements the Notifier interface
func ( t * TelegramNotifier ) NotifyPayment ( miner Miner , payment Payment ) error {
2021-10-13 14:38:31 +02:00
templateName := "templates/payment.tmpl"
2022-02-27 20:13:11 +01:00
if t . configurations . Payment . Template != "" {
templateName = t . configurations . Payment . Template
2021-10-06 18:59:11 +02:00
}
2021-10-13 14:38:31 +02:00
message , err := t . formatMessage ( templateName , Attachment { Miner : miner , Payment : payment } )
2021-10-06 18:59:11 +02:00
if err != nil {
return err
}
return t . sendMessage ( message )
}
2022-02-27 20:13:11 +01:00
// testNotifyPayment sends a fake payment notification
func ( t * TelegramNotifier ) testNotifyPayment ( client FlexpoolClient ) error {
log . Debug ( "Testing payment notification" )
randomPool , err := client . RandomPool ( )
if err != nil {
return err
}
randomMiner , err := client . RandomMiner ( randomPool )
if err != nil {
return err
}
randomPayment , err := client . LastMinerPayment ( randomMiner )
if err != nil {
return err
}
return t . NotifyPayment ( * randomMiner , * randomPayment )
}
2021-10-06 18:59:11 +02:00
// NotifyBlock to format and send a notification when a new block has been detected
// Implements the Notifier interface
func ( t * TelegramNotifier ) NotifyBlock ( pool Pool , block Block ) error {
2021-10-13 14:38:31 +02:00
templateName := "templates/block.tmpl"
2022-02-27 20:13:11 +01:00
if t . configurations . Block . Template != "" {
templateName = t . configurations . Block . Template
2021-10-06 18:59:11 +02:00
}
2021-10-13 14:38:31 +02:00
message , err := t . formatMessage ( templateName , Attachment { Pool : pool , Block : block } )
2021-10-06 18:59:11 +02:00
if err != nil {
return err
}
return t . sendMessage ( message )
}
2021-10-10 20:56:11 +02:00
2022-02-27 20:13:11 +01:00
// testNotifyBlock sends a random block notification
func ( t * TelegramNotifier ) testNotifyBlock ( client FlexpoolClient ) error {
log . Debug ( "Testing block notification" )
randomPool , err := client . RandomPool ( )
if err != nil {
return err
}
randomBlock , err := client . LastPoolBlock ( randomPool )
if err != nil {
return err
}
return t . NotifyBlock ( * randomPool , * randomBlock )
}
2021-10-10 20:56:11 +02:00
// NotifyOfflineWorker sends a message when a worker is online or offline
func ( t * TelegramNotifier ) NotifyOfflineWorker ( worker Worker ) error {
2021-10-13 14:38:31 +02:00
templateName := "templates/offline-worker.tmpl"
2022-02-27 20:13:11 +01:00
if t . configurations . OfflineWorker . Template != "" {
templateName = t . configurations . OfflineWorker . Template
2021-10-13 14:38:31 +02:00
}
message , err := t . formatMessage ( templateName , Attachment { Worker : worker } )
if err != nil {
return err
2021-10-10 20:56:11 +02:00
}
return t . sendMessage ( message )
}
2022-02-27 20:13:11 +01:00
// testNotifyOfflineWorker sends a fake worker offline notification
func ( t * TelegramNotifier ) testNotifyOfflineWorker ( client FlexpoolClient ) error {
log . Debug ( "Testing offline worker notification" )
randomBlock , err := client . RandomPool ( )
if err != nil {
return err
}
randomMiner , err := client . RandomMiner ( randomBlock )
if err != nil {
return err
}
randomWorker , err := client . RandomWorker ( randomMiner )
if err != nil {
return err
}
log . Debugf ( "%s" , randomWorker )
return t . NotifyOfflineWorker ( * randomWorker )
}
// NotifyTest sends fake notifications
func ( t * TelegramNotifier ) NotifyTest ( client FlexpoolClient ) ( executed bool , err error ) {
if t . configurations . Balance . Test {
if err = t . testNotifyBalance ( client ) ; err != nil {
return false , err
} else {
executed = true
}
}
if t . configurations . Payment . Test {
if err = t . testNotifyPayment ( client ) ; err != nil {
return false , err
} else {
executed = true
}
}
if t . configurations . Block . Test {
if err = t . testNotifyBlock ( client ) ; err != nil {
return false , err
} else {
executed = true
}
}
if t . configurations . OfflineWorker . Test {
if err = t . testNotifyOfflineWorker ( client ) ; err != nil {
return false , err
} else {
executed = true
}
}
return executed , nil
}