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 <julien@riou.xyz>
This commit is contained in:
		
					parent
					
						
							
								b519770922
							
						
					
				
			
			
				commit
				
					
						d2d1503779
					
				
			
		
					 8 changed files with 289 additions and 53 deletions
				
			
		
							
								
								
									
										29
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										29
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -60,7 +60,7 @@ another file provided by the `-config` argument.
 | 
				
			||||||
As a good start, you can copy the configuration file example:
 | 
					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.
 | 
					Then edit this file at will.
 | 
				
			||||||
| 
						 | 
					@ -71,11 +71,13 @@ Reference:
 | 
				
			||||||
* `max-blocks` (optional): maximum number of blocks to retreive from the API
 | 
					* `max-blocks` (optional): maximum number of blocks to retreive from the API
 | 
				
			||||||
* `max-payments` (optional): maximum number of payments to retreive from the API
 | 
					* `max-payments` (optional): maximum number of payments to retreive from the API
 | 
				
			||||||
* `pools` (optional): list of pools
 | 
					* `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)
 | 
					    * `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
 | 
					* `miners` (optional): list of miners and/or farmers
 | 
				
			||||||
    * `address`: address of the miner or the farmer registered on the API
 | 
					    * `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-balance` (optional): enable balance notifications (disabled by default)
 | 
				
			||||||
    * `enable-payments` (optional): enable payments 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
 | 
					    * `enable-offline-workers` (optional): enable offline/online notifications for associated workers (disabled by
 | 
				
			||||||
| 
						 | 
					@ -84,12 +86,19 @@ Reference:
 | 
				
			||||||
    * `token`: token of the Telegram bot
 | 
					    * `token`: token of the Telegram bot
 | 
				
			||||||
    * `chat-id` (optional if `channel-name` is present): chat identifier to send Telegram notifications
 | 
					    * `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
 | 
					    * `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
 | 
					* `notifications` (optional): Notifications configurations
 | 
				
			||||||
   type
 | 
					    * `balance` (optional): balance notifications settings
 | 
				
			||||||
    * `balance` (optional): path to template file to format balance notifications
 | 
					        * `template` (optional): path to [template](https://pkg.go.dev/text/template) file
 | 
				
			||||||
    * `payment` (optional): path to template file to format payment notifications
 | 
					        * `test` (optional): send a test notification
 | 
				
			||||||
    * `block` (optional): path to template file to format block notifications
 | 
					    * `payment` (optional): payment notifications settings
 | 
				
			||||||
    * `offline-worker` (optional): path to template file to format offline worker notifications
 | 
					        * `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
 | 
					## Templating
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -112,7 +121,7 @@ The following **data** is available to templates:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Default templates are available in the [templates](templates) directory.
 | 
					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
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										88
									
								
								client.go
									
										
									
									
									
								
							
							
						
						
									
										88
									
								
								client.go
									
										
									
									
									
								
							| 
						 | 
					@ -4,6 +4,7 @@ import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"time"
 | 
						"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
 | 
					// WorkersResponse represents the JSON structure of the Flexpool API response for workers
 | 
				
			||||||
type WorkersResponse struct {
 | 
					type WorkersResponse struct {
 | 
				
			||||||
	Error  string `json:"error"`
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										21
									
								
								config.go
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								config.go
									
										
									
									
									
								
							| 
						 | 
					@ -14,7 +14,7 @@ type Config struct {
 | 
				
			||||||
	Pools          []PoolConfig        `yaml:"pools"`
 | 
						Pools          []PoolConfig        `yaml:"pools"`
 | 
				
			||||||
	Miners         []MinerConfig       `yaml:"miners"`
 | 
						Miners         []MinerConfig       `yaml:"miners"`
 | 
				
			||||||
	TelegramConfig TelegramConfig      `yaml:"telegram"`
 | 
						TelegramConfig TelegramConfig      `yaml:"telegram"`
 | 
				
			||||||
	NotificationTemplates NotificationTemplatesConfig `yaml:"notification-templates"`
 | 
						Notifications  NotificationsConfig `yaml:"notifications"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PoolConfig to store Pool configuration
 | 
					// PoolConfig to store Pool configuration
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ type PoolConfig struct {
 | 
				
			||||||
// MinerConfig to store Miner configuration
 | 
					// MinerConfig to store Miner configuration
 | 
				
			||||||
type MinerConfig struct {
 | 
					type MinerConfig struct {
 | 
				
			||||||
	Address              string `yaml:"address"`
 | 
						Address              string `yaml:"address"`
 | 
				
			||||||
 | 
						Coin                 string `yaml:"coin"`
 | 
				
			||||||
	EnableBalance        bool   `yaml:"enable-balance"`
 | 
						EnableBalance        bool   `yaml:"enable-balance"`
 | 
				
			||||||
	EnablePayments       bool   `yaml:"enable-payments"`
 | 
						EnablePayments       bool   `yaml:"enable-payments"`
 | 
				
			||||||
	EnableOfflineWorkers bool   `yaml:"enable-offline-workers"`
 | 
						EnableOfflineWorkers bool   `yaml:"enable-offline-workers"`
 | 
				
			||||||
| 
						 | 
					@ -39,12 +40,18 @@ type TelegramConfig struct {
 | 
				
			||||||
	ChannelName string `yaml:"channel-name"`
 | 
						ChannelName string `yaml:"channel-name"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NotificationTemplatesConfig to store notifications templates configuration
 | 
					// NotificationTemplatesConfig to store all notifications configurations
 | 
				
			||||||
type NotificationTemplatesConfig struct {
 | 
					type NotificationsConfig struct {
 | 
				
			||||||
	Balance       string `yaml:"balance"`
 | 
						Balance       NotificationConfig `yaml:"balance"`
 | 
				
			||||||
	Payment       string `yaml:"payment"`
 | 
						Payment       NotificationConfig `yaml:"payment"`
 | 
				
			||||||
	Block         string `yaml:"block"`
 | 
						Block         NotificationConfig `yaml:"block"`
 | 
				
			||||||
	OfflineWorker string `yaml:"offline-worker"`
 | 
						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
 | 
					// NewConfig creates a Config with default values
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,10 +4,12 @@ max-blocks: 10
 | 
				
			||||||
max-payments: 5
 | 
					max-payments: 5
 | 
				
			||||||
miners:
 | 
					miners:
 | 
				
			||||||
  - address: 0x0000000000000000000000000000000000000000
 | 
					  - address: 0x0000000000000000000000000000000000000000
 | 
				
			||||||
 | 
					    coin: eth
 | 
				
			||||||
    enable-balance: true
 | 
					    enable-balance: true
 | 
				
			||||||
    enable-payments: true
 | 
					    enable-payments: true
 | 
				
			||||||
    enable-offline-workers: true
 | 
					    enable-offline-workers: true
 | 
				
			||||||
  - address: xch00000000000000000000000000000000000000000000000000000000000
 | 
					  - address: xch00000000000000000000000000000000000000000000000000000000000
 | 
				
			||||||
 | 
					    coin: xch
 | 
				
			||||||
    enable-balance: true
 | 
					    enable-balance: true
 | 
				
			||||||
    enable-payments: true
 | 
					    enable-payments: true
 | 
				
			||||||
    enable-offline-workers: true
 | 
					    enable-offline-workers: true
 | 
				
			||||||
| 
						 | 
					@ -22,8 +24,16 @@ telegram:
 | 
				
			||||||
  chat-id: 000000000
 | 
					  chat-id: 000000000
 | 
				
			||||||
  channel-name: MyTelegramChannel
 | 
					  channel-name: MyTelegramChannel
 | 
				
			||||||
  token: 0000000000000000000000000000000000000000000000
 | 
					  token: 0000000000000000000000000000000000000000000000
 | 
				
			||||||
notification-templates:
 | 
					#notifications:
 | 
				
			||||||
  balance: balance.tmpl
 | 
					#  balance:
 | 
				
			||||||
  block: block.tmpl
 | 
					#    template: balance.tmpl
 | 
				
			||||||
  offline-worker; offline-worker.tmpl
 | 
					#    test: true
 | 
				
			||||||
  payment: payment.tmpl
 | 
					#  block:
 | 
				
			||||||
 | 
					#    template: block.tmpl
 | 
				
			||||||
 | 
					#    test: true
 | 
				
			||||||
 | 
					#  offline-worker:
 | 
				
			||||||
 | 
					#    template: offline-worker.tmpl
 | 
				
			||||||
 | 
					#    test: true
 | 
				
			||||||
 | 
					#  payment:
 | 
				
			||||||
 | 
					#    template: payment.tmpl
 | 
				
			||||||
 | 
					#    test: true
 | 
				
			||||||
							
								
								
									
										16
									
								
								main.go
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								main.go
									
										
									
									
									
								
							| 
						 | 
					@ -3,7 +3,9 @@ package main
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log "github.com/sirupsen/logrus"
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
| 
						 | 
					@ -30,6 +32,7 @@ const MaxBlocks = 50
 | 
				
			||||||
// initialize logging
 | 
					// initialize logging
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	log.SetOutput(os.Stdout)
 | 
						log.SetOutput(os.Stdout)
 | 
				
			||||||
 | 
						rand.Seed(time.Now().UnixNano())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
| 
						 | 
					@ -84,11 +87,20 @@ func main() {
 | 
				
			||||||
	client := NewFlexpoolClient()
 | 
						client := NewFlexpoolClient()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Notifications
 | 
						// Notifications
 | 
				
			||||||
	notifier, err := NewTelegramNotifier(&config.TelegramConfig, &config.NotificationTemplates)
 | 
						notifier, err := NewTelegramNotifier(&config.TelegramConfig, &config.Notifications)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("Could not create notifier: %v", err)
 | 
							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
 | 
						// Limits
 | 
				
			||||||
	var maxPayments int
 | 
						var maxPayments int
 | 
				
			||||||
	if config.MaxPayments > 0 {
 | 
						if config.MaxPayments > 0 {
 | 
				
			||||||
| 
						 | 
					@ -106,7 +118,7 @@ func main() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Handle miners
 | 
						// Handle miners
 | 
				
			||||||
	for _, configuredMiner := range config.Miners {
 | 
						for _, configuredMiner := range config.Miners {
 | 
				
			||||||
		miner, err := NewMiner(configuredMiner.Address)
 | 
							miner, err := NewMiner(configuredMiner.Address, configuredMiner.Coin)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Warnf("Could not parse miner: %v", err)
 | 
								log.Warnf("Could not parse miner: %v", err)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								miner.go
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								miner.go
									
										
									
									
									
								
							| 
						 | 
					@ -24,13 +24,15 @@ type Miner struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewMiner creates a Miner
 | 
					// NewMiner creates a Miner
 | 
				
			||||||
func NewMiner(address string) (*Miner, error) {
 | 
					func NewMiner(address string, coin string) (*Miner, error) {
 | 
				
			||||||
	miner := &Miner{Address: address}
 | 
						miner := &Miner{Address: address, Coin: coin}
 | 
				
			||||||
 | 
						if coin == "" {
 | 
				
			||||||
		coin, err := miner.ParseCoin()
 | 
							coin, err := miner.ParseCoin()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		miner.Coin = coin
 | 
							miner.Coin = coin
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return miner, nil
 | 
						return miner, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										124
									
								
								notification.go
									
										
									
									
									
								
							
							
						
						
									
										124
									
								
								notification.go
									
										
									
									
									
								
							| 
						 | 
					@ -32,6 +32,7 @@ type Notifier interface {
 | 
				
			||||||
	NotifyPayment(miner Miner, payment Payment) error
 | 
						NotifyPayment(miner Miner, payment Payment) error
 | 
				
			||||||
	NotifyBlock(pool Pool, block Block) error
 | 
						NotifyBlock(pool Pool, block Block) error
 | 
				
			||||||
	NotifyOfflineWorker(worker Worker) error
 | 
						NotifyOfflineWorker(worker Worker) error
 | 
				
			||||||
 | 
						NotifyTest(client FlexpoolClient) error
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TelegramNotifier to send notifications using Telegram
 | 
					// TelegramNotifier to send notifications using Telegram
 | 
				
			||||||
| 
						 | 
					@ -40,11 +41,11 @@ type TelegramNotifier struct {
 | 
				
			||||||
	bot            *telegram.BotAPI
 | 
						bot            *telegram.BotAPI
 | 
				
			||||||
	chatID         int64
 | 
						chatID         int64
 | 
				
			||||||
	channelName    string
 | 
						channelName    string
 | 
				
			||||||
	templatesConfig *NotificationTemplatesConfig
 | 
						configurations *NotificationsConfig
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTelegramNotifier to create a TelegramNotifier
 | 
					// 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)
 | 
						bot, err := telegram.NewBotAPI(config.Token)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
| 
						 | 
					@ -55,7 +56,7 @@ func NewTelegramNotifier(config *TelegramConfig, templatesConfig *NotificationTe
 | 
				
			||||||
		bot:            bot,
 | 
							bot:            bot,
 | 
				
			||||||
		chatID:         config.ChatID,
 | 
							chatID:         config.ChatID,
 | 
				
			||||||
		channelName:    config.ChannelName,
 | 
							channelName:    config.ChannelName,
 | 
				
			||||||
		templatesConfig: templatesConfig,
 | 
							configurations: configurations,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,8 +126,8 @@ func fileExists(filename string) bool {
 | 
				
			||||||
// Implements the Notifier interface
 | 
					// Implements the Notifier interface
 | 
				
			||||||
func (t *TelegramNotifier) NotifyBalance(miner Miner) (err error) {
 | 
					func (t *TelegramNotifier) NotifyBalance(miner Miner) (err error) {
 | 
				
			||||||
	templateName := "templates/balance.tmpl"
 | 
						templateName := "templates/balance.tmpl"
 | 
				
			||||||
	if t.templatesConfig.Balance != "" {
 | 
						if t.configurations.Balance.Template != "" {
 | 
				
			||||||
		templateName = t.templatesConfig.Balance
 | 
							templateName = t.configurations.Balance.Template
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	message, err := t.formatMessage(templateName, Attachment{Miner: miner})
 | 
						message, err := t.formatMessage(templateName, Attachment{Miner: miner})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -135,12 +136,26 @@ func (t *TelegramNotifier) NotifyBalance(miner Miner) (err error) {
 | 
				
			||||||
	return t.sendMessage(message)
 | 
						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
 | 
					// NotifyPayment to format and send a notification when a new payment has been detected
 | 
				
			||||||
// Implements the Notifier interface
 | 
					// Implements the Notifier interface
 | 
				
			||||||
func (t *TelegramNotifier) NotifyPayment(miner Miner, payment Payment) error {
 | 
					func (t *TelegramNotifier) NotifyPayment(miner Miner, payment Payment) error {
 | 
				
			||||||
	templateName := "templates/payment.tmpl"
 | 
						templateName := "templates/payment.tmpl"
 | 
				
			||||||
	if t.templatesConfig.Payment != "" {
 | 
						if t.configurations.Payment.Template != "" {
 | 
				
			||||||
		templateName = t.templatesConfig.Payment
 | 
							templateName = t.configurations.Payment.Template
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	message, err := t.formatMessage(templateName, Attachment{Miner: miner, Payment: payment})
 | 
						message, err := t.formatMessage(templateName, Attachment{Miner: miner, Payment: payment})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -149,12 +164,30 @@ func (t *TelegramNotifier) NotifyPayment(miner Miner, payment Payment) error {
 | 
				
			||||||
	return t.sendMessage(message)
 | 
						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
 | 
					// NotifyBlock to format and send a notification when a new block has been detected
 | 
				
			||||||
// Implements the Notifier interface
 | 
					// Implements the Notifier interface
 | 
				
			||||||
func (t *TelegramNotifier) NotifyBlock(pool Pool, block Block) error {
 | 
					func (t *TelegramNotifier) NotifyBlock(pool Pool, block Block) error {
 | 
				
			||||||
	templateName := "templates/block.tmpl"
 | 
						templateName := "templates/block.tmpl"
 | 
				
			||||||
	if t.templatesConfig.Block != "" {
 | 
						if t.configurations.Block.Template != "" {
 | 
				
			||||||
		templateName = t.templatesConfig.Block
 | 
							templateName = t.configurations.Block.Template
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	message, err := t.formatMessage(templateName, Attachment{Pool: pool, Block: block})
 | 
						message, err := t.formatMessage(templateName, Attachment{Pool: pool, Block: block})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -163,11 +196,25 @@ func (t *TelegramNotifier) NotifyBlock(pool Pool, block Block) error {
 | 
				
			||||||
	return t.sendMessage(message)
 | 
						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
 | 
					// NotifyOfflineWorker sends a message when a worker is online or offline
 | 
				
			||||||
func (t *TelegramNotifier) NotifyOfflineWorker(worker Worker) error {
 | 
					func (t *TelegramNotifier) NotifyOfflineWorker(worker Worker) error {
 | 
				
			||||||
	templateName := "templates/offline-worker.tmpl"
 | 
						templateName := "templates/offline-worker.tmpl"
 | 
				
			||||||
	if t.templatesConfig.OfflineWorker != "" {
 | 
						if t.configurations.OfflineWorker.Template != "" {
 | 
				
			||||||
		templateName = t.templatesConfig.OfflineWorker
 | 
							templateName = t.configurations.OfflineWorker.Template
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	message, err := t.formatMessage(templateName, Attachment{Worker: worker})
 | 
						message, err := t.formatMessage(templateName, Attachment{Worker: worker})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -175,3 +222,58 @@ func (t *TelegramNotifier) NotifyOfflineWorker(worker Worker) error {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return t.sendMessage(message)
 | 
						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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								utils.go
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								utils.go
									
										
									
									
									
								
							| 
						 | 
					@ -14,6 +14,8 @@ const MojoToXCHDivider = 1000000000000
 | 
				
			||||||
// Example: for "eth", convert from Weis to ETH
 | 
					// Example: for "eth", convert from Weis to ETH
 | 
				
			||||||
func ConvertCurrency(coin string, value float64) (float64, error) {
 | 
					func ConvertCurrency(coin string, value float64) (float64, error) {
 | 
				
			||||||
	switch coin {
 | 
						switch coin {
 | 
				
			||||||
 | 
						case "etc":
 | 
				
			||||||
 | 
							return ConvertWeis(value), nil
 | 
				
			||||||
	case "eth":
 | 
						case "eth":
 | 
				
			||||||
		return ConvertWeis(value), nil
 | 
							return ConvertWeis(value), nil
 | 
				
			||||||
	case "xch":
 | 
						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
 | 
					// FormatBlockURL returns the URL on the respective blockchain explorer given the coin and the block hash
 | 
				
			||||||
func FormatBlockURL(coin string, hash string) (string, error) {
 | 
					func FormatBlockURL(coin string, hash string) (string, error) {
 | 
				
			||||||
	switch coin {
 | 
						switch coin {
 | 
				
			||||||
 | 
						case "etc":
 | 
				
			||||||
 | 
							return fmt.Sprintf("https://etcblockexplorer.com/block/%s", hash), nil
 | 
				
			||||||
	case "eth":
 | 
						case "eth":
 | 
				
			||||||
		return fmt.Sprintf("https://etherscan.io/block/%s", hash), nil
 | 
							return fmt.Sprintf("https://etherscan.io/block/%s", hash), nil
 | 
				
			||||||
	case "xch":
 | 
						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
 | 
					// FormatTransactionURL returns the URL on the respective blockchain explorer given the coin and the transaction hash
 | 
				
			||||||
func FormatTransactionURL(coin string, hash string) (string, error) {
 | 
					func FormatTransactionURL(coin string, hash string) (string, error) {
 | 
				
			||||||
	switch coin {
 | 
						switch coin {
 | 
				
			||||||
 | 
						case "etc":
 | 
				
			||||||
 | 
							return fmt.Sprintf("https://etcblockexplorer.com/address/%s", hash), nil
 | 
				
			||||||
	case "eth":
 | 
						case "eth":
 | 
				
			||||||
		return fmt.Sprintf("https://etherscan.io/tx/%s", hash), nil
 | 
							return fmt.Sprintf("https://etherscan.io/tx/%s", hash), nil
 | 
				
			||||||
	case "xch":
 | 
						case "xch":
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Reference in a new issue