feat: Add notification templates (#2)
Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
parent
b9902f0623
commit
632da28954
11 changed files with 154 additions and 67 deletions
141
notification.go
141
notification.go
|
|
@ -1,15 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/leekchan/accounting"
|
||||
"text/template"
|
||||
|
||||
telegram "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
// Notifier interface to define how to send all kind of notifications
|
||||
type Notifier interface {
|
||||
NotifyBalance(miner Miner, difference float64) error
|
||||
|
|
@ -21,13 +36,14 @@ type Notifier interface {
|
|||
// TelegramNotifier to send notifications using Telegram
|
||||
// Implements the Notifier interface
|
||||
type TelegramNotifier struct {
|
||||
bot *telegram.BotAPI
|
||||
chatID int64
|
||||
channelName string
|
||||
bot *telegram.BotAPI
|
||||
chatID int64
|
||||
channelName string
|
||||
templatesConfig *NotificationTemplatesConfig
|
||||
}
|
||||
|
||||
// NewTelegramNotifier to create a TelegramNotifier
|
||||
func NewTelegramNotifier(config *TelegramConfig) (*TelegramNotifier, error) {
|
||||
func NewTelegramNotifier(config *TelegramConfig, templatesConfig *NotificationTemplatesConfig) (*TelegramNotifier, error) {
|
||||
bot, err := telegram.NewBotAPI(config.Token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -35,9 +51,10 @@ func NewTelegramNotifier(config *TelegramConfig) (*TelegramNotifier, error) {
|
|||
log.Debugf("Connected to Telegram as %s", bot.Self.UserName)
|
||||
|
||||
return &TelegramNotifier{
|
||||
bot: bot,
|
||||
chatID: config.ChatID,
|
||||
channelName: config.ChannelName,
|
||||
bot: bot,
|
||||
chatID: config.ChatID,
|
||||
channelName: config.ChannelName,
|
||||
templatesConfig: templatesConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -60,77 +77,103 @@ func (t *TelegramNotifier) sendMessage(message string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Deduce if template file is embeded or is a file on disk
|
||||
embeded := true
|
||||
if _, err = os.Stat(templateFileName); os.IsExist(err) {
|
||||
embeded = false
|
||||
}
|
||||
// Reinitialize the error because it was only used for the test
|
||||
err = nil
|
||||
|
||||
// Create template
|
||||
templateName := path.Base(templateFileName)
|
||||
templateFunctions := template.FuncMap{
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"convertCurrency": ConvertCurrency,
|
||||
"convertAction": ConvertAction,
|
||||
"formatBlockURL": FormatBlockURL,
|
||||
"formatTransactionURL": FormatTransactionURL,
|
||||
}
|
||||
tmpl := template.New(templateName).Funcs(templateFunctions)
|
||||
|
||||
// Parse template
|
||||
if embeded {
|
||||
log.Debugf("Parsing embeded template file %s", templateFileName)
|
||||
tmpl, err = tmpl.ParseFS(templateFiles, templateFileName)
|
||||
} else {
|
||||
log.Debugf("Parsing template file %s", templateFileName)
|
||||
tmpl, err = tmpl.ParseFiles(templateFileName)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// NotifyBalance to format and send a notification when the unpaid balance has changed
|
||||
// Implements the Notifier interface
|
||||
func (t *TelegramNotifier) NotifyBalance(miner Miner) error {
|
||||
ac := accounting.Accounting{
|
||||
Symbol: strings.ToUpper(miner.Coin),
|
||||
Precision: 6,
|
||||
Format: "%v %s",
|
||||
func (t *TelegramNotifier) NotifyBalance(miner Miner) (err error) {
|
||||
templateName := "templates/balance.tmpl"
|
||||
if t.templatesConfig.Balance != "" {
|
||||
templateName = t.templatesConfig.Balance
|
||||
}
|
||||
convertedBalance, err := ConvertCurrency(miner.Coin, miner.Balance)
|
||||
message, err := t.formatMessage(templateName, Attachment{Miner: miner})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message := fmt.Sprintf("💰 *Balance* _%s_", ac.FormatMoney(convertedBalance))
|
||||
return t.sendMessage(message)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ac := accounting.Accounting{
|
||||
Symbol: strings.ToUpper(miner.Coin),
|
||||
Precision: 6,
|
||||
Format: "%v %s",
|
||||
templateName := "templates/payment.tmpl"
|
||||
if t.templatesConfig.Payment != "" {
|
||||
templateName = t.templatesConfig.Payment
|
||||
}
|
||||
convertedValue, err := ConvertCurrency(miner.Coin, payment.Value)
|
||||
message, err := t.formatMessage(templateName, Attachment{Miner: miner, Payment: payment})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("💵 *Payment* _%s_", ac.FormatMoney(convertedValue))
|
||||
return t.sendMessage(message)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
precision := 6
|
||||
if pool.Coin == "xch" {
|
||||
precision = 2
|
||||
templateName := "templates/block.tmpl"
|
||||
if t.templatesConfig.Block != "" {
|
||||
templateName = t.templatesConfig.Block
|
||||
}
|
||||
ac := accounting.Accounting{
|
||||
Symbol: strings.ToUpper(pool.Coin),
|
||||
Precision: precision,
|
||||
Format: "%v %s",
|
||||
}
|
||||
|
||||
convertedValue, err := ConvertCurrency(pool.Coin, block.Reward)
|
||||
message, err := t.formatMessage(templateName, Attachment{Pool: pool, Block: block})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verb, err := ConvertAction(pool.Coin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
url, err := FormatBlockURL(pool.Coin, block.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("🎉 *%s* [#%d](%s) _%s_", verb, block.Number, url, ac.FormatMoney(convertedValue))
|
||||
return t.sendMessage(message)
|
||||
}
|
||||
|
||||
// NotifyOfflineWorker sends a message when a worker is online or offline
|
||||
func (t *TelegramNotifier) NotifyOfflineWorker(worker Worker) error {
|
||||
stateIcon := "🟢"
|
||||
stateMessage := "online"
|
||||
if !worker.IsOnline {
|
||||
stateIcon = "🔴"
|
||||
stateMessage = "offline"
|
||||
templateName := "templates/offline-worker.tmpl"
|
||||
if t.templatesConfig.OfflineWorker != "" {
|
||||
templateName = t.templatesConfig.OfflineWorker
|
||||
}
|
||||
message, err := t.formatMessage(templateName, Attachment{Worker: worker})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
message := fmt.Sprintf("%s *Worker* _%s_ is %s", stateIcon, worker.Name, stateMessage)
|
||||
return t.sendMessage(message)
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue