2021-10-06 18:59:11 +02:00
package main
import (
"flag"
"fmt"
2022-02-27 20:13:11 +01:00
"math/rand"
2021-10-06 18:59:11 +02:00
"os"
2022-02-27 20:13:11 +01:00
"time"
2021-10-06 18:59:11 +02:00
log "github.com/sirupsen/logrus"
"gorm.io/gorm"
)
// AppName to store application name
var AppName string = "flexassistant"
// AppVersion to set version at compilation time
var AppVersion string = "9999"
// GitCommit to set git commit at compilation time (can be empty)
var GitCommit string
// GoVersion to set Go version at compilation time
var GoVersion string
// MaxPayments defaults
const MaxPayments = 10
// MaxBlocks defaults
const MaxBlocks = 50
// initialize logging
func init ( ) {
log . SetOutput ( os . Stdout )
2022-02-27 20:13:11 +01:00
rand . Seed ( time . Now ( ) . UnixNano ( ) )
2021-10-06 18:59:11 +02:00
}
func main ( ) {
config := NewConfig ( )
version := flag . Bool ( "version" , false , "Print version and exit" )
quiet := flag . Bool ( "quiet" , false , "Log errors only" )
verbose := flag . Bool ( "verbose" , false , "Print more logs" )
debug := flag . Bool ( "debug" , false , "Print even more logs" )
configFileName := flag . String ( "config" , AppName + ".yaml" , "Configuration file name" )
flag . Parse ( )
if * version {
showVersion ( )
return
}
// Logs and configuration
log . SetLevel ( log . WarnLevel )
if * debug {
log . SetLevel ( log . DebugLevel )
}
if * verbose {
log . SetLevel ( log . InfoLevel )
}
if * quiet {
log . SetLevel ( log . ErrorLevel )
}
if * configFileName != "" {
err := config . ReadFile ( * configFileName )
if err != nil {
log . Fatalf ( "Cannot parse configuration file: %v" , err )
}
}
// Database
var db * gorm . DB
db , err := NewDatabase ( config . DatabaseFile )
if err != nil {
log . Fatalf ( "Could not create database: %v" , err )
}
if err := CreateDatabaseObjects ( db ) ; err != nil {
log . Fatalf ( "Could not create objects: %v" , err )
}
2021-10-10 20:56:11 +02:00
if err := EnsureDatabaseRetention ( db ) ; err != nil {
log . Fatalf ( "Could not cleanup objects from database: %v" , err )
}
2021-10-06 18:59:11 +02:00
// API client
client := NewFlexpoolClient ( )
// Notifications
2022-02-27 20:13:11 +01:00
notifier , err := NewTelegramNotifier ( & config . TelegramConfig , & config . Notifications )
2021-10-06 18:59:11 +02:00
if err != nil {
log . Fatalf ( "Could not create notifier: %v" , err )
}
2022-02-27 20:13:11 +01:00
executed , err := notifier . NotifyTest ( * client )
if err != nil {
log . Fatalf ( "Could not send test notifications: %v" , err )
}
if executed {
log . Debug ( "Exit after sending test notifications" )
return
}
2021-10-06 18:59:11 +02:00
// Limits
var maxPayments int
if config . MaxPayments > 0 {
maxPayments = config . MaxPayments
} else {
maxPayments = MaxPayments
}
var maxBlocks int
if config . MaxBlocks > 0 {
maxBlocks = config . MaxBlocks
} else {
maxBlocks = MaxBlocks
}
// Handle miners
for _ , configuredMiner := range config . Miners {
2022-02-27 20:13:11 +01:00
miner , err := NewMiner ( configuredMiner . Address , configuredMiner . Coin )
2021-10-06 18:59:11 +02:00
if err != nil {
log . Warnf ( "Could not parse miner: %v" , err )
continue
}
var dbMiner Miner
trx := db . Where ( Miner { Address : miner . Address } ) . Attrs ( Miner { Address : miner . Address , Coin : miner . Coin } ) . FirstOrCreate ( & dbMiner )
if trx . Error != nil {
log . Warnf ( "Cannot fetch miner %s from database: %v" , miner , trx . Error )
}
// Balance management
if configuredMiner . EnableBalance {
// Balance have never been persisted, skip notifications
notify := true
if dbMiner . Balance == 0 {
notify = false
}
log . Debugf ( "Fetching balance for %s" , miner )
balance , err := client . MinerBalance ( miner . Coin , miner . Address )
if err != nil {
log . Warnf ( "Could not fetch unpaid balance: %v" , err )
continue
}
2021-10-15 18:20:38 +02:00
log . Debugf ( "Unpaid balance %.0f" , balance )
2021-10-06 18:59:11 +02:00
miner . Balance = balance
if miner . Balance != dbMiner . Balance {
dbMiner . Balance = balance
if trx = db . Save ( & dbMiner ) ; trx . Error != nil {
log . Warnf ( "Cannot update miner: %v" , trx . Error )
continue
}
if notify {
err = notifier . NotifyBalance ( * miner )
if err != nil {
log . Warnf ( "Cannot send notification: %v" , err )
continue
}
log . Infof ( "Balance notification sent for %s" , miner )
}
}
}
// Payments management
if configuredMiner . EnablePayments {
// Payments have never been persisted, skip notifications
notify := true
if dbMiner . LastPaymentTimestamp == 0 {
notify = false
}
log . Debugf ( "Fetching payments for %s" , miner )
payments , err := client . MinerPayments ( miner . Coin , miner . Address , maxPayments )
if err != nil {
log . Warnf ( "Could not fetch payments: %v" , err )
continue
}
for _ , payment := range payments {
log . Debugf ( "Fetched %s" , payment )
if dbMiner . LastPaymentTimestamp < payment . Timestamp {
dbMiner . LastPaymentTimestamp = payment . Timestamp
if trx = db . Save ( & dbMiner ) ; trx . Error != nil {
log . Warnf ( "Cannot update miner: %v" , trx . Error )
continue
}
if notify {
err = notifier . NotifyPayment ( * miner , * payment )
if err != nil {
log . Warnf ( "Cannot send notification: %v" , err )
continue
}
log . Infof ( "Payment notification sent for %s" , payment )
}
}
}
}
2021-10-10 20:56:11 +02:00
// Offline workers management
if configuredMiner . EnableOfflineWorkers {
log . Debugf ( "Fetching workers for %s" , miner )
workers , err := client . MinerWorkers ( miner . Coin , miner . Address )
if err != nil {
log . Warnf ( "Could not fetch workers: %v" , err )
continue
}
for _ , worker := range workers {
log . Debugf ( "Fetched %s" , worker )
var dbWorker Worker
trx := db . Where ( Worker { MinerAddress : miner . Address , Name : worker . Name } ) . Attrs ( Worker { MinerAddress : miner . Address , Name : worker . Name } ) . FirstOrCreate ( & dbWorker )
if trx . Error != nil {
log . Warnf ( "Cannot fetch worker %s from database: %v" , worker , trx . Error )
continue
}
if dbWorker . IsOnline != worker . IsOnline {
// Skip first notification
notify := true
if dbWorker . LastSeen . IsZero ( ) {
notify = false
}
dbWorker . IsOnline = worker . IsOnline
dbWorker . LastSeen = worker . LastSeen
if trx = db . Save ( & dbWorker ) ; trx . Error != nil {
log . Warnf ( "Cannot update worker: %v" , trx . Error )
continue
}
if notify {
err = notifier . NotifyOfflineWorker ( * worker )
if err != nil {
log . Warnf ( "Cannot send notification: %v" , err )
continue
}
log . Infof ( "Offline worker notification sent for %s" , worker )
}
}
}
}
2021-10-06 18:59:11 +02:00
}
// Handle pools
for _ , configuredPool := range config . Pools {
pool := NewPool ( configuredPool . Coin )
var dbPool Pool
trx := db . Where ( Pool { Coin : pool . Coin } ) . Attrs ( Pool { Coin : pool . Coin } ) . FirstOrCreate ( & dbPool )
if trx . Error != nil {
log . Warnf ( "Cannot fetch pool %s from database: %v" , pool , trx . Error )
}
// Blocks management
if configuredPool . EnableBlocks {
// Block number has never been persisted, skip notifications
notify := true
if dbPool . LastBlockNumber == 0 {
notify = false
}
log . Debugf ( "Fetching blocks for %s" , pool )
blocks , err := client . PoolBlocks ( pool . Coin , maxBlocks )
if err != nil {
log . Warnf ( "Could not fetch blocks: %v" , err )
} else {
for _ , block := range blocks {
log . Debugf ( "Fetched %s" , block )
if dbPool . LastBlockNumber < block . Number {
dbPool . LastBlockNumber = block . Number
if trx = db . Save ( & dbPool ) ; trx . Error != nil {
log . Warnf ( "Cannot update pool: %v" , trx . Error )
continue
}
2022-02-25 15:05:52 +01:00
convertedReward , err := ConvertCurrency ( pool . Coin , block . Reward )
if err != nil {
log . Warnf ( "Reward for block %d cannot be converted: %v" , block . Number , err )
}
if notify && convertedReward >= configuredPool . MinBlockReward {
2021-10-06 18:59:11 +02:00
err = notifier . NotifyBlock ( * pool , * block )
if err != nil {
log . Warnf ( "Cannot send notification: %v" , err )
continue
}
log . Infof ( "Block notification sent for %s" , block )
}
}
}
}
}
}
}
func showVersion ( ) {
if GitCommit != "" {
AppVersion = fmt . Sprintf ( "%s-%s" , AppVersion , GitCommit )
}
fmt . Printf ( "%s version %s (compiled with %s)\n" , AppName , AppVersion , GoVersion )
}