From e3ae989511cc346dfb8fc47e3c36a99822525fb0 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Fri, 5 Nov 2021 08:19:48 +0100 Subject: [PATCH 01/10] chore: Bump to version 1.4 Signed-off-by: Julien Riou --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7e32cd5..c068b24 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3 +1.4 From b51977092211827034a1e4443edf82be7b8f423a Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Fri, 25 Feb 2022 15:05:52 +0100 Subject: [PATCH 02/10] feat: Minimum block rewards to send notifications Flexpool is now finding more blocks than ever. There are tons of notifications all day long. This commit adds a new pool setting `min-block-reward` to send notifications for blocks reaching this minimum threshold, so we can focus on big blocks. Signed-off-by: Julien Riou --- .gitignore | 1 + README.md | 1 + VERSION | 2 +- config.go | 5 +++-- flexassistant.yaml.example | 2 ++ main.go | 6 +++++- 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 487e7a2..c696239 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +etc bin flexassistant.yaml flexassistant.db \ No newline at end of file diff --git a/README.md b/README.md index 7a8cced..c6e62cc 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Reference: * `pools` (optional): list of pools * `coin`: coin of the pool (ex: `eth`, `xch`) * `enable-blocks` (optional): enable block notifications for this pool (disabled by default) + * `min-block-reward` (optional): send notifications when block reward has reached this minimum threshold in crypto currency unit (ETH, XCH, etc) * `miners` (optional): list of miners and/or farmers * `address`: address of the miner or the farmer registered on the API * `enable-balance` (optional): enable balance notifications (disabled by default) diff --git a/VERSION b/VERSION index c068b24..c239c60 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4 +1.5 diff --git a/config.go b/config.go index 6d3e7ab..692e1f6 100644 --- a/config.go +++ b/config.go @@ -19,8 +19,9 @@ type Config struct { // PoolConfig to store Pool configuration type PoolConfig struct { - Coin string `yaml:"coin"` - EnableBlocks bool `yaml:"enable-blocks"` + Coin string `yaml:"coin"` + EnableBlocks bool `yaml:"enable-blocks"` + MinBlockReward float64 `yaml:"min-block-reward"` } // MinerConfig to store Miner configuration diff --git a/flexassistant.yaml.example b/flexassistant.yaml.example index a4586d4..ea16ae6 100644 --- a/flexassistant.yaml.example +++ b/flexassistant.yaml.example @@ -14,8 +14,10 @@ miners: pools: - coin: eth enable-blocks: true + min-block-reward: 10 - coin: xch enable-blocks: true + min-block-reward: 1.79 telegram: chat-id: 000000000 channel-name: MyTelegramChannel diff --git a/main.go b/main.go index 6d426a5..5cbe58f 100644 --- a/main.go +++ b/main.go @@ -263,7 +263,11 @@ func main() { log.Warnf("Cannot update pool: %v", trx.Error) continue } - if notify { + 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 { err = notifier.NotifyBlock(*pool, *block) if err != nil { log.Warnf("Cannot send notification: %v", err) From d2d1503779ed9dba9e990e68746a1c261788df25 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Sun, 27 Feb 2022 20:13:11 +0100 Subject: [PATCH 03/10] feat: ETC support and notifications tests - Add ETC to the list of supported coins. A new `coin` setting can be configured to avoid conflict with `eth`. Mind the lowercase. By default, flexassitant will try to deduce the coin from the miner's address (with eth by default, not etc). (#5) - Add `test` (true/false) to `notifications` section to test notifications with random values fetched from the Flexpool API - Fix typo in the configuration example (#6) BREAKING CHANGE: `notification-templates` configuration settings have been renamed to `notifications`, with sections to configure balance, payment, block and offline workers notifications, with `template` and `test` settings. Signed-off-by: Julien Riou --- README.md | 29 ++-- client.go | 88 ++++++++++++ config.go | 33 +++-- ...yaml.example => flexassistant.example.yaml | 20 ++- main.go | 16 ++- miner.go | 14 +- notification.go | 136 +++++++++++++++--- utils.go | 6 + 8 files changed, 289 insertions(+), 53 deletions(-) rename flexassistant.yaml.example => flexassistant.example.yaml (69%) diff --git a/README.md b/README.md index c6e62cc..667c93a 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ another file provided by the `-config` argument. As a good start, you can copy the configuration file example: ``` -cp -p flexassistant.yaml.example flexassistant.yaml +cp -p flexassistant.example.yaml flexassistant.yaml ``` Then edit this file at will. @@ -71,11 +71,13 @@ Reference: * `max-blocks` (optional): maximum number of blocks to retreive from the API * `max-payments` (optional): maximum number of payments to retreive from the API * `pools` (optional): list of pools - * `coin`: coin of the pool (ex: `eth`, `xch`) + * `coin`: coin of the pool (ex: `etc`, `eth`, `xch`) * `enable-blocks` (optional): enable block notifications for this pool (disabled by default) - * `min-block-reward` (optional): send notifications when block reward has reached this minimum threshold in crypto currency unit (ETH, XCH, etc) + * `min-block-reward` (optional): send notifications when block reward has reached this minimum threshold in crypto + currency unit (ETH, XCH, etc) * `miners` (optional): list of miners and/or farmers * `address`: address of the miner or the farmer registered on the API + * `coin` (optional): coin of the miner (ex: `etc`, `eth`, `xch`) (deduced by default, can be wrong for `etc` coin) * `enable-balance` (optional): enable balance notifications (disabled by default) * `enable-payments` (optional): enable payments notifications (disabled by default) * `enable-offline-workers` (optional): enable offline/online notifications for associated workers (disabled by @@ -84,12 +86,19 @@ Reference: * `token`: token of the Telegram bot * `chat-id` (optional if `channel-name` is present): chat identifier to send Telegram notifications * `channel-name` (optional if `chat-id` is present): channel name to send Telegram notifications -* `notification-templates` (optional): path to [template](https://pkg.go.dev/text/template) files for each notification - type - * `balance` (optional): path to template file to format balance notifications - * `payment` (optional): path to template file to format payment notifications - * `block` (optional): path to template file to format block notifications - * `offline-worker` (optional): path to template file to format offline worker notifications +* `notifications` (optional): Notifications configurations + * `balance` (optional): balance notifications settings + * `template` (optional): path to [template](https://pkg.go.dev/text/template) file + * `test` (optional): send a test notification + * `payment` (optional): payment notifications settings + * `template` (optional): path to [template](https://pkg.go.dev/text/template) file + * `test` (optional): send a test notification + * `block` (optional): block notification settings + * `template` (optional): path to [template](https://pkg.go.dev/text/template) file + * `test` (optional): send a test notification + * `offline-worker` (optional): offline workers notification settings + * `template` (optional): path to [template](https://pkg.go.dev/text/template) file + * `test` (optional): send a test notification ## Templating @@ -112,7 +121,7 @@ The following **data** is available to templates: Default templates are available in the [templates](templates) directory. -Custom template files can be used with the `notification-templates` settings (see _Configuration_ section). +Custom template files can be used with the `template` settings (see _Configuration_ section). ## Usage diff --git a/client.go b/client.go index b2f961d..7d920c4 100644 --- a/client.go +++ b/client.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math/rand" "net/http" "sort" "time" @@ -128,6 +129,16 @@ func (f *FlexpoolClient) MinerPayments(coin string, address string, limit int) ( } } +// LastMinerPayment return the last payment of a miner +func (f *FlexpoolClient) LastMinerPayment(miner *Miner) (*Payment, error) { + log.Debugf("Fetching last payment of %s", miner) + payments, err := f.MinerPayments(miner.Coin, miner.Address, 1) + if err != nil { + return nil, err + } + return payments[0], nil +} + // WorkersResponse represents the JSON structure of the Flexpool API response for workers type WorkersResponse struct { Error string `json:"error"` @@ -205,3 +216,80 @@ func (f *FlexpoolClient) PoolBlocks(coin string, limit int) (blocks []*Block, er } } } + +// LastPoolBlock return the last discovered block for a given pool +func (f *FlexpoolClient) LastPoolBlock(pool *Pool) (*Block, error) { + blocks, err := f.PoolBlocks(pool.Coin, 1) + if err != nil { + return nil, err + } + return blocks[0], nil +} + +// CoinsResponse represents the JSON structure of the Flexpool API response for pool coins +type CoinsResponse struct { + Error string `json:"error"` + Result struct { + Coins []struct { + Ticker string `json:"ticker"` + Name string `json:"name"` + } `json:"coins"` + } `json:"result"` +} + +// RandomPool returns a random pool from the API +func (f *FlexpoolClient) RandomPool() (*Pool, error) { + log.Debug("Fetching a random pool") + body, err := f.request(fmt.Sprintf("%s/pool/coins", FlexpoolAPIURL)) + if err != nil { + return nil, err + } + var response CoinsResponse + json.Unmarshal(body, &response) + randomIndex := rand.Intn(len(response.Result.Coins)) + if err != nil { + return nil, err + } + randomCoin := response.Result.Coins[randomIndex] + return NewPool(randomCoin.Ticker), nil +} + +// TopMinersResponse represents the JSON structure of the Flexpool API response for pool top miners +type TopMinersResponse struct { + Error string `json:"error"` + Result []struct { + Address string `json:"address"` + } `json:"result"` +} + +// RandomMiner returns a random miner from the API +func (f *FlexpoolClient) RandomMiner(pool *Pool) (*Miner, error) { + log.Debug("Fetching a random miner") + body, err := f.request(fmt.Sprintf("%s/pool/topMiners?coin=%s", FlexpoolAPIURL, pool.Coin)) + if err != nil { + return nil, err + } + var response TopMinersResponse + json.Unmarshal(body, &response) + randomResult := response.Result[rand.Intn(len(response.Result))] + randomMiner, err := NewMiner(randomResult.Address, pool.Coin) + if err != nil { + return nil, err + } + randomBalance, err := f.MinerBalance(pool.Coin, randomMiner.Address) + if err != nil { + return nil, err + } + randomMiner.Balance = randomBalance + return randomMiner, nil +} + +// RandomWorker returns a random worker from the API +func (f *FlexpoolClient) RandomWorker(miner *Miner) (*Worker, error) { + log.Debug("Fetching a random worker") + workers, err := f.MinerWorkers(miner.Coin, miner.Address) + if err != nil { + return nil, err + } + return workers[rand.Intn(len(workers))], nil +} diff --git a/config.go b/config.go index 692e1f6..39afb32 100644 --- a/config.go +++ b/config.go @@ -8,13 +8,13 @@ import ( // Config to receive settings from the configuration file type Config struct { - DatabaseFile string `yaml:"database-file"` - MaxBlocks int `yaml:"max-blocks"` - MaxPayments int `yaml:"max-payments"` - Pools []PoolConfig `yaml:"pools"` - Miners []MinerConfig `yaml:"miners"` - TelegramConfig TelegramConfig `yaml:"telegram"` - NotificationTemplates NotificationTemplatesConfig `yaml:"notification-templates"` + DatabaseFile string `yaml:"database-file"` + MaxBlocks int `yaml:"max-blocks"` + MaxPayments int `yaml:"max-payments"` + Pools []PoolConfig `yaml:"pools"` + Miners []MinerConfig `yaml:"miners"` + TelegramConfig TelegramConfig `yaml:"telegram"` + Notifications NotificationsConfig `yaml:"notifications"` } // PoolConfig to store Pool configuration @@ -27,6 +27,7 @@ type PoolConfig struct { // MinerConfig to store Miner configuration type MinerConfig struct { Address string `yaml:"address"` + Coin string `yaml:"coin"` EnableBalance bool `yaml:"enable-balance"` EnablePayments bool `yaml:"enable-payments"` EnableOfflineWorkers bool `yaml:"enable-offline-workers"` @@ -39,12 +40,18 @@ type TelegramConfig struct { ChannelName string `yaml:"channel-name"` } -// NotificationTemplatesConfig to store notifications templates configuration -type NotificationTemplatesConfig struct { - Balance string `yaml:"balance"` - Payment string `yaml:"payment"` - Block string `yaml:"block"` - OfflineWorker string `yaml:"offline-worker"` +// NotificationTemplatesConfig to store all notifications configurations +type NotificationsConfig struct { + Balance NotificationConfig `yaml:"balance"` + Payment NotificationConfig `yaml:"payment"` + Block NotificationConfig `yaml:"block"` + OfflineWorker NotificationConfig `yaml:"offline-worker"` +} + +// NotificationConfig to store a single notification configuration +type NotificationConfig struct { + Template string `yaml:"template"` + Test bool `yaml:"test"` } // NewConfig creates a Config with default values diff --git a/flexassistant.yaml.example b/flexassistant.example.yaml similarity index 69% rename from flexassistant.yaml.example rename to flexassistant.example.yaml index ea16ae6..9f8a3fe 100644 --- a/flexassistant.yaml.example +++ b/flexassistant.example.yaml @@ -4,10 +4,12 @@ max-blocks: 10 max-payments: 5 miners: - address: 0x0000000000000000000000000000000000000000 + coin: eth enable-balance: true enable-payments: true enable-offline-workers: true - address: xch00000000000000000000000000000000000000000000000000000000000 + coin: xch enable-balance: true enable-payments: true enable-offline-workers: true @@ -22,8 +24,16 @@ telegram: chat-id: 000000000 channel-name: MyTelegramChannel token: 0000000000000000000000000000000000000000000000 -notification-templates: - balance: balance.tmpl - block: block.tmpl - offline-worker; offline-worker.tmpl - payment: payment.tmpl \ No newline at end of file +#notifications: +# balance: +# template: balance.tmpl +# test: true +# block: +# template: block.tmpl +# test: true +# offline-worker: +# template: offline-worker.tmpl +# test: true +# payment: +# template: payment.tmpl +# test: true \ No newline at end of file diff --git a/main.go b/main.go index 5cbe58f..f98ce5f 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,9 @@ package main import ( "flag" "fmt" + "math/rand" "os" + "time" log "github.com/sirupsen/logrus" "gorm.io/gorm" @@ -30,6 +32,7 @@ const MaxBlocks = 50 // initialize logging func init() { log.SetOutput(os.Stdout) + rand.Seed(time.Now().UnixNano()) } func main() { @@ -84,11 +87,20 @@ func main() { client := NewFlexpoolClient() // Notifications - notifier, err := NewTelegramNotifier(&config.TelegramConfig, &config.NotificationTemplates) + notifier, err := NewTelegramNotifier(&config.TelegramConfig, &config.Notifications) if err != nil { log.Fatalf("Could not create notifier: %v", err) } + 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 + } + // Limits var maxPayments int if config.MaxPayments > 0 { @@ -106,7 +118,7 @@ func main() { // Handle miners for _, configuredMiner := range config.Miners { - miner, err := NewMiner(configuredMiner.Address) + miner, err := NewMiner(configuredMiner.Address, configuredMiner.Coin) if err != nil { log.Warnf("Could not parse miner: %v", err) continue diff --git a/miner.go b/miner.go index 65466d6..cfbb08b 100644 --- a/miner.go +++ b/miner.go @@ -24,13 +24,15 @@ type Miner struct { } // NewMiner creates a Miner -func NewMiner(address string) (*Miner, error) { - miner := &Miner{Address: address} - coin, err := miner.ParseCoin() - if err != nil { - return nil, err +func NewMiner(address string, coin string) (*Miner, error) { + miner := &Miner{Address: address, Coin: coin} + if coin == "" { + coin, err := miner.ParseCoin() + if err != nil { + return nil, err + } + miner.Coin = coin } - miner.Coin = coin return miner, nil } diff --git a/notification.go b/notification.go index 6cf6a17..ffca65d 100644 --- a/notification.go +++ b/notification.go @@ -32,19 +32,20 @@ type Notifier interface { NotifyPayment(miner Miner, payment Payment) error NotifyBlock(pool Pool, block Block) error NotifyOfflineWorker(worker Worker) error + NotifyTest(client FlexpoolClient) error } // TelegramNotifier to send notifications using Telegram // Implements the Notifier interface type TelegramNotifier struct { - bot *telegram.BotAPI - chatID int64 - channelName string - templatesConfig *NotificationTemplatesConfig + bot *telegram.BotAPI + chatID int64 + channelName string + configurations *NotificationsConfig } // NewTelegramNotifier to create a TelegramNotifier -func NewTelegramNotifier(config *TelegramConfig, templatesConfig *NotificationTemplatesConfig) (*TelegramNotifier, error) { +func NewTelegramNotifier(config *TelegramConfig, configurations *NotificationsConfig) (*TelegramNotifier, error) { bot, err := telegram.NewBotAPI(config.Token) if err != nil { return nil, err @@ -52,10 +53,10 @@ func NewTelegramNotifier(config *TelegramConfig, templatesConfig *NotificationTe log.Debugf("Connected to Telegram as %s", bot.Self.UserName) return &TelegramNotifier{ - bot: bot, - chatID: config.ChatID, - channelName: config.ChannelName, - templatesConfig: templatesConfig, + bot: bot, + chatID: config.ChatID, + channelName: config.ChannelName, + configurations: configurations, }, nil } @@ -125,8 +126,8 @@ func fileExists(filename string) bool { // Implements the Notifier interface func (t *TelegramNotifier) NotifyBalance(miner Miner) (err error) { templateName := "templates/balance.tmpl" - if t.templatesConfig.Balance != "" { - templateName = t.templatesConfig.Balance + if t.configurations.Balance.Template != "" { + templateName = t.configurations.Balance.Template } message, err := t.formatMessage(templateName, Attachment{Miner: miner}) if err != nil { @@ -135,12 +136,26 @@ func (t *TelegramNotifier) NotifyBalance(miner Miner) (err error) { return t.sendMessage(message) } +// 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) +} + // 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 { templateName := "templates/payment.tmpl" - if t.templatesConfig.Payment != "" { - templateName = t.templatesConfig.Payment + if t.configurations.Payment.Template != "" { + templateName = t.configurations.Payment.Template } message, err := t.formatMessage(templateName, Attachment{Miner: miner, Payment: payment}) if err != nil { @@ -149,12 +164,30 @@ func (t *TelegramNotifier) NotifyPayment(miner Miner, payment Payment) error { return t.sendMessage(message) } +// 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) +} + // 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 { templateName := "templates/block.tmpl" - if t.templatesConfig.Block != "" { - templateName = t.templatesConfig.Block + if t.configurations.Block.Template != "" { + templateName = t.configurations.Block.Template } message, err := t.formatMessage(templateName, Attachment{Pool: pool, Block: block}) if err != nil { @@ -163,11 +196,25 @@ func (t *TelegramNotifier) NotifyBlock(pool Pool, block Block) error { return t.sendMessage(message) } +// 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) +} + // NotifyOfflineWorker sends a message when a worker is online or offline func (t *TelegramNotifier) NotifyOfflineWorker(worker Worker) error { templateName := "templates/offline-worker.tmpl" - if t.templatesConfig.OfflineWorker != "" { - templateName = t.templatesConfig.OfflineWorker + if t.configurations.OfflineWorker.Template != "" { + templateName = t.configurations.OfflineWorker.Template } message, err := t.formatMessage(templateName, Attachment{Worker: worker}) if err != nil { @@ -175,3 +222,58 @@ func (t *TelegramNotifier) NotifyOfflineWorker(worker Worker) error { } return t.sendMessage(message) } + +// 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 +} diff --git a/utils.go b/utils.go index 88c29dd..97895e2 100644 --- a/utils.go +++ b/utils.go @@ -14,6 +14,8 @@ const MojoToXCHDivider = 1000000000000 // Example: for "eth", convert from Weis to ETH func ConvertCurrency(coin string, value float64) (float64, error) { switch coin { + case "etc": + return ConvertWeis(value), nil case "eth": return ConvertWeis(value), nil case "xch": @@ -36,6 +38,8 @@ func ConvertMojo(value float64) float64 { // FormatBlockURL returns the URL on the respective blockchain explorer given the coin and the block hash func FormatBlockURL(coin string, hash string) (string, error) { switch coin { + case "etc": + return fmt.Sprintf("https://etcblockexplorer.com/block/%s", hash), nil case "eth": return fmt.Sprintf("https://etherscan.io/block/%s", hash), nil case "xch": @@ -47,6 +51,8 @@ func FormatBlockURL(coin string, hash string) (string, error) { // FormatTransactionURL returns the URL on the respective blockchain explorer given the coin and the transaction hash func FormatTransactionURL(coin string, hash string) (string, error) { switch coin { + case "etc": + return fmt.Sprintf("https://etcblockexplorer.com/address/%s", hash), nil case "eth": return fmt.Sprintf("https://etherscan.io/tx/%s", hash), nil case "xch": From e511eb89efa8fd044c39f7a05913edb85497ed77 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Sun, 27 Feb 2022 20:30:24 +0100 Subject: [PATCH 04/10] doc: Improve Telegram documentation (#7) Signed-off-by: Julien Riou --- README.md | 44 ++++++++++++++++++++++++++++++++++++++ flexassistant.example.yaml | 4 ++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 667c93a..425b7be 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,50 @@ ls -l bin/flexassistant ## Configuration +### Telegram + +Follow [this procedure](https://core.telegram.org/bots#3-how-do-i-create-a-bot) to create a bot `token`. + +Then you have two possible destinations to send messages: +* channel using a `channel_name` (string) +* chat using a `chat_id` (integer) + +For testing purpose, you should store the token in a variable for next sections: +``` +read -s TOKEN +``` + +#### Chat + +To get the chat identifier, you can send a message to your bot then read messages using the API: + +``` +curl -s -XGET "https://api.telegram.org/bot${TOKEN}/getUpdates" | jq -r ".result[].message.chat.id" +``` + +You can test to send messages to a chat with: + +``` +read CHAT_ID +curl -s -XGET "https://api.telegram.org/bot${TOKEN}/sendMessage?chat_id=${CHAT_ID}&text=hello" | jq +``` + +#### Channel + +Public channel names can be used (example: `@mychannel`). For private channels, you should use a `chat_id` instead. + +You can test to send messages to a channel with: + +``` +read CHANNEL_NAME +curl -s -XGET "https://api.telegram.org/bot${TOKEN}/sendMessage?chat_id=${CHANNEL_NAME}&text=hello" | jq +``` + +Don't forget to prefix the channel name with an `@`. + + +### flexassistant + *flexassistant* can be configured using a YaML file. By default, the `flexassistant.yaml` file is used but it can be another file provided by the `-config` argument. diff --git a/flexassistant.example.yaml b/flexassistant.example.yaml index 9f8a3fe..498a516 100644 --- a/flexassistant.example.yaml +++ b/flexassistant.example.yaml @@ -22,7 +22,7 @@ pools: min-block-reward: 1.79 telegram: chat-id: 000000000 - channel-name: MyTelegramChannel + channel-name: @MyTelegramChannel token: 0000000000000000000000000000000000000000000000 #notifications: # balance: @@ -36,4 +36,4 @@ telegram: # test: true # payment: # template: payment.tmpl -# test: true \ No newline at end of file +# test: true From b923db5b3c266d017b0d1d56c252d68ea9ac422f Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Sun, 27 Feb 2022 20:31:44 +0100 Subject: [PATCH 05/10] chore: Bump version to 1.6 Signed-off-by: Julien Riou --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c239c60..810ee4e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5 +1.6 From 6445f3f292ef90a8ba544a1082ec24c13322a103 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Mon, 28 Feb 2022 14:01:59 +0100 Subject: [PATCH 06/10] chore: Fix typo in example file Telegram channel name needs an @ and it needs to be escaped in YAML. Signed-off-by: Julien Riou --- flexassistant.example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flexassistant.example.yaml b/flexassistant.example.yaml index 498a516..1470e55 100644 --- a/flexassistant.example.yaml +++ b/flexassistant.example.yaml @@ -22,7 +22,7 @@ pools: min-block-reward: 1.79 telegram: chat-id: 000000000 - channel-name: @MyTelegramChannel + channel-name: '@MyTelegramChannel' token: 0000000000000000000000000000000000000000000000 #notifications: # balance: From d01fefbfbfd1d7f626c15b7e8133f8dc01c17be7 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Fri, 8 Apr 2022 13:41:44 +0200 Subject: [PATCH 07/10] fix(payments): Improve paged response (#8) Signed-off-by: Julien Riou --- client.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/client.go b/client.go index 7d920c4..b87ae26 100644 --- a/client.go +++ b/client.go @@ -87,7 +87,8 @@ func (f *FlexpoolClient) MinerBalance(coin string, address string) (float64, err type PaymentsResponse struct { Error string `json:"error"` Result struct { - Data []struct { + TotalPages int `json:"totalPages"` + Data []struct { Hash string `json:"hash"` Value float64 `json:"value"` Timestamp int64 `json:"timestamp"` @@ -98,7 +99,9 @@ type PaymentsResponse struct { // MinerPayments returns an ordered list of payments func (f *FlexpoolClient) MinerPayments(coin string, address string, limit int) (payments []*Payment, err error) { page := 0 - for { + totalPages := 0 + + for page <= MaxIterations && len(payments) < limit { body, err := f.request(fmt.Sprintf("%s/miner/payments/?coin=%s&address=%s&page=%d", FlexpoolAPIURL, coin, address, page)) if err != nil { return nil, err @@ -107,6 +110,10 @@ func (f *FlexpoolClient) MinerPayments(coin string, address string, limit int) ( var response PaymentsResponse json.Unmarshal(body, &response) + if totalPages == 0 { + totalPages = response.Result.TotalPages + } + for _, result := range response.Result.Data { payment := NewPayment( result.Hash, @@ -115,18 +122,24 @@ func (f *FlexpoolClient) MinerPayments(coin string, address string, limit int) ( ) payments = append(payments, payment) if len(payments) >= limit { - // sort by timestamp - sort.Slice(payments, func(p1, p2 int) bool { - return payments[p1].Timestamp > payments[p2].Timestamp - }) - return payments, nil + break } } - page = page + 1 - if page > MaxIterations { - return nil, fmt.Errorf("Max iterations of %d reached", MaxIterations) + page++ + if page >= totalPages { + break } } + + if page > MaxIterations { + return nil, fmt.Errorf("Max iterations of %d reached", MaxIterations) + } + + // Sort by timestamp + sort.Slice(payments, func(p1, p2 int) bool { + return payments[p1].Timestamp > payments[p2].Timestamp + }) + return payments, nil } // LastMinerPayment return the last payment of a miner From 2f0f4a9fde8f1bdb3ca00556bfee6fcbc9e01835 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Fri, 8 Apr 2022 13:49:39 +0200 Subject: [PATCH 08/10] fix(blocks): Improve paged response Signed-off-by: Julien Riou --- client.go | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/client.go b/client.go index b87ae26..b38e775 100644 --- a/client.go +++ b/client.go @@ -188,7 +188,8 @@ func (f *FlexpoolClient) MinerWorkers(coin string, address string) (workers []*W type BlocksResponse struct { Error string `json:"error"` Result struct { - Data []struct { + TotalPages int `json:"totalPages"` + Data []struct { Hash string `json:"hash"` Number uint64 `json:"number"` Reward float64 `json:"reward"` @@ -199,7 +200,9 @@ type BlocksResponse struct { // PoolBlocks returns an ordered list of blocks func (f *FlexpoolClient) PoolBlocks(coin string, limit int) (blocks []*Block, err error) { page := 0 - for { + totalPages := 0 + + for page <= MaxIterations && len(blocks) < limit { body, err := f.request(fmt.Sprintf("%s/pool/blocks/?coin=%s&page=%d", FlexpoolAPIURL, coin, page)) if err != nil { return nil, err @@ -208,6 +211,10 @@ func (f *FlexpoolClient) PoolBlocks(coin string, limit int) (blocks []*Block, er var response BlocksResponse json.Unmarshal(body, &response) + if totalPages == 0 { + totalPages = response.Result.TotalPages + } + for _, result := range response.Result.Data { block := NewBlock( result.Hash, @@ -216,18 +223,23 @@ func (f *FlexpoolClient) PoolBlocks(coin string, limit int) (blocks []*Block, er ) blocks = append(blocks, block) if len(blocks) >= limit { - // sort by number - sort.Slice(blocks, func(b1, b2 int) bool { - return blocks[b1].Number < blocks[b2].Number - }) - return blocks, nil + break } } - page = page + 1 - if page > MaxIterations { - return nil, fmt.Errorf("Max iterations of %d reached", MaxIterations) + page++ + if page >= totalPages { + break } } + if page > MaxIterations { + return nil, fmt.Errorf("Max iterations of %d reached", MaxIterations) + } + + // Sort by number + sort.Slice(blocks, func(b1, b2 int) bool { + return blocks[b1].Number < blocks[b2].Number + }) + return blocks, nil } // LastPoolBlock return the last discovered block for a given pool From 46395768f7a403d2f21e4ed888d8c09dde9383e0 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Fri, 8 Apr 2022 13:52:49 +0200 Subject: [PATCH 09/10] chore: Bump version to 1.7 Signed-off-by: Julien Riou --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 810ee4e..d3bdbdf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6 +1.7 From e6c8411615693822ccd5506f90c5747df63469d1 Mon Sep 17 00:00:00 2001 From: Julien Riou Date: Sun, 24 Sep 2023 08:18:25 +0200 Subject: [PATCH 10/10] chore: End of life Signed-off-by: Julien Riou --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 425b7be..02f6199 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +# END OF LIFE NOTICE + +> **Flexpool.io will officially wind down its operations on November 1, 2023** + +[See the full +announcement](https://www.reddit.com/r/Flexpool/comments/16q72ul/action_required_flexpoolio_shutdown_notice_nov_1/). + # flexassistant [Flexpool.io](https://www.flexpool.io/) is a famous cryptocurrency mining or farming pool supporting