addressWatcher/main.go

241 lines
6.5 KiB
Go

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
tick := time.NewTicker(time.Minute)
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 {
b, err := checkAPI()
if err != nil {
log.Fatalln(err)
}
if b == true {
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)
}
} else {
log.Println("API seems down. Skipping this round")
}
return previousAmount
}
func checkAPI() (bool, error) {
url := "https://chainz.cryptoid.info/explorer/api.dws?q=summary"
request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return false, fmt.Errorf("can't request %s, error is: %s", url, err)
}
request.Header.Set("Content-Type", "application/json")
// request response
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return false, 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 false, fmt.Errorf("can't read body %s, error is %s", response.Body, err)
}
if len(data) > 100 {
// API available
return true, nil
}
// API not available
return false, nil
}
func parseAmount(c config) (float64, error) {
// Define Variables
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
}