first working version

This commit is contained in:
Nils Stinnesbeck 2021-01-12 22:37:37 +01:00
commit 770ce4e21d
Signed by: nils
GPG Key ID: 86D4882C6C6CA48B
6 changed files with 263 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# binaries
addressWatcher
addressWatcher.exe

3
Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM scratch
ADD addressWatcher /opt/addressWatcher/addressWatcher
ENTRYPOINT ["/opt/addressWatcher/addressWatcher"]

44
README.md Executable file
View File

@ -0,0 +1,44 @@
# addressWatcher
## How to build
You can build this program using the following commandline
```
CGO_ENABLED=0 go build -ldflags '-s -w' -trimpath .
```
## How to use
Fill all needed parameters and execute the binary
```
./addressWatcher \
-address YOUR_ADDRESS \
-apikey YOUR_APIKEY_FOR_chainz.cryptoid.info \
-coin SHORT_FOR_YOUR_COIN \
-chatid TELEGRAM_CHATID \
-token TELEGRAM_BOT_TOKEN \
-name NICKNAME_OF_ADDRESS
```
## Prerequisits
1. Check if your Blockchain is available at https://chainz.cryptoid.info/
1. If so, take note of the short handle in the url for your coin (e.g. `btc` for bitcoin, as taken from `https://btc.cryptoid.info/btc/`)
1. You need to create a Telegram bot first by talking to `@botfather` in Telegram and following the instructions
1. Create a seperate group for you (,some others) and your bot in Telegram
1. Invite `@RawDataBot` into your channel and read out the chatid (including the `-` sign at the front) e.g. `-123456`
1. Remove `@RawDataBot` from your channel
1. Request an API key from https://chainz.cryptoid.info/api.key.dws
1. Start this program somewhere it can run forever, e.g. a server
## Use cases
### Monitoring a Masternode
This program can be used to track rewards for a masternode. If you put in the address of the masternode this program will alert you of any changes made to the wallet's amount
### Monitoring a donation address
If you have an address where people can donate cryptocurrency to, you can leave this program running to be alerted of any incoming donations
## Known issues
### API goes down
From time to time the API does not respond, or goes into maintenance mode. If this happens, the API responds with an amount of 0. My program will pick this up and think of it as a withdrawal of all funds.
I need to find a way to check the API's readyness first.

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.nils.zone/nils/addressWatcher
go 1.14
require github.com/shopspring/decimal v1.2.0

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=

206
main.go Normal file
View File

@ -0,0 +1,206 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
"github.com/shopspring/decimal"
)
// Define global variables
type config struct {
address string
apiKey string
coin string
chatID string
token string
name string
}
func main() {
// flags
address := flag.String("address", "", "Address to monitor for changes")
apiKey := flag.String("apikey", "", "APIKEY from https://chainz.cryptoid.info")
coin := flag.String("coin", "", "Coin of address. You can lookup all supported coins at: https://chainz.cryptoid.info/ (use abbreviated form as you see in the url, e.g. Litecoin = ltc)")
chatID := flag.String("chatid", "", "Look up the ChatID of your channel by inviting @RawDataBot to your channel (and removing it afterwards)")
token := flag.String("token", "", "Telegram token for your bot, you need to create a bot by talking to @botfather first")
name := flag.String("name", "", "(Nick)name of address, will appear in Telegram message")
flag.Parse()
c := config{
address: *address,
apiKey: *apiKey,
coin: *coin,
chatID: *chatID,
token: *token,
name: *name,
}
switch {
case c.address == "":
log.Fatalln("Please specify an address using -address")
case c.apiKey == "":
log.Fatalln("Please specify an API key using -apikey")
case c.chatID == "":
log.Fatalln("Please specify a chat ID using -chatid")
case c.coin == "":
log.Fatalln("Please specify a coin using -coin")
case c.token == "":
log.Fatalln("Please specify a token using -token")
case c.name == "":
log.Fatalln("Please specify a name using -name")
}
// all checks completed starting script
log.Println("Passed all checks, starting watcher now!")
// setup scheduler
// new stuff
// tick := time.NewTicker(time.Minute)
tick := time.NewTicker(time.Second * 10)
go scheduler(tick, c)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
tick.Stop()
}
func scheduler(tick *time.Ticker, c config) error {
// variable to hold amound of last call
var previousAmount float64
// run once (directly on start)
previousAmount = task(c, previousAmount)
// loop every time interval
for range tick.C {
previousAmount = task(c, previousAmount)
}
return nil
}
func task(c config, previousAmount float64) float64 {
amount, err := parseAmount(c)
if err != nil {
log.Fatalf("can't parse amount, error is: %s\n", err)
}
previousAmount, err = compareAmount(previousAmount, amount, c)
if err != nil {
log.Fatalf("can't compare amount, error is: %s\n", err)
}
return previousAmount
}
func parseAmount(c config) (float64, error) {
// Define Variables
var url string
var amount float64
// assemble url
url = "https://chainz.cryptoid.info/" + c.coin + "/api.dws?q=getbalance&a=" + c.address + "&key=" + c.apiKey
// form url
request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return 0, fmt.Errorf("can't request %s, error is: %s", url, err)
}
// add headers
request.Header.Set("Content-Type", "application/json")
// request response
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return 0, fmt.Errorf("HTTPS request to url %s failed with error %s", url, err)
}
// read response into data variable
data, err := ioutil.ReadAll(response.Body)
if err != nil {
return 0, fmt.Errorf("can't read body %s, error is %s", response.Body, err)
}
// convert []byte to int
amount, err = strconv.ParseFloat(string(data), 64)
if err != nil {
// can't parse string to number (this happens when website is in maintenance mode)
return 0, nil
}
// output amount to console
log.Printf("Current amount of %s: %f\n", strings.ToUpper(c.coin), amount)
return amount, nil
}
// compareAmount compares the current amount a with the previous amount p
func compareAmount(p float64, a float64, c config) (f float64, err error) {
if a == p {
// current amount is equal to previous
} else if a > p {
// current amount is more than previous
err = sendToTelegram(decimal.NewFromFloat(a), decimal.NewFromFloat(p), false, c)
} else {
// current amount is less than previous
err = sendToTelegram(decimal.NewFromFloat(a), decimal.NewFromFloat(p), true, c)
}
if err != nil {
return 0, err
}
return a, nil
}
// sendToTelegram sends amount a, and previous amount p, and bool less to telegram
func sendToTelegram(a decimal.Decimal, p decimal.Decimal, isLess bool, c config) error {
// Variables
url := "https://api.telegram.org/bot" + c.token + "/sendMessage?chat_id=" + c.chatID + "&text="
var text string
// make coin uppercase
c.coin = strings.ToUpper(c.coin)
// see if less bool is true or false, to send out messages for added or withdrawn coins
if isLess {
// the current amount is less than previous
diff := p.Sub(a)
text = fmt.Sprintf("%s %s have been withdrawn from %s, totalling now %s %s", diff.String(), c.coin, c.name, a.String(), c.coin)
} else {
// amount is more than previous amount
diff := a.Sub(p)
text = fmt.Sprintf("%s %s have been deposited to %s, totalling now %s %s", diff.String(), c.coin, c.name, a.String(), c.coin)
}
// construct url to be called
url = url + text
// call url
request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("can't request %s, error is: %s", url, err)
}
// add headers
request.Header.Set("Content-Type", "application/json")
// request response
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return fmt.Errorf("HTTPS request to url %s failed with error %s", url, err)
}
if response.StatusCode != 200 {
return fmt.Errorf("unexpected status code from telegram API, error is %d", response.StatusCode)
}
return nil
}