aboutsummaryrefslogtreecommitdiffstats
path: root/hived.go
diff options
context:
space:
mode:
authorterminaldweller <thabogre@gmail.com>2021-02-23 10:01:14 +0000
committerterminaldweller <thabogre@gmail.com>2021-02-23 10:01:14 +0000
commitc2b3c437772df57162385555933aac440308b31c (patch)
treeee0160dad06e1cf4e331d88836e1aae8be05721d /hived.go
parentforgot to add the parser... (diff)
downloadhived-c2b3c437772df57162385555933aac440308b31c.tar.gz
hived-c2b3c437772df57162385555933aac440308b31c.zip
removed the expression parser. using a lib now. the addalert endpoint is working. alert messages about fullfilment in tg now. using redis as db to keep the alerts in.
Diffstat (limited to '')
-rw-r--r--hived.go248
1 files changed, 211 insertions, 37 deletions
diff --git a/hived.go b/hived.go
index de40ff3..37bf8d0 100644
--- a/hived.go
+++ b/hived.go
@@ -1,6 +1,7 @@
package main
import (
+ "context"
"encoding/json"
"flag"
"fmt"
@@ -10,7 +11,10 @@ import (
"net/url"
"strconv"
"sync"
+ "time"
+ "github.com/Knetic/govaluate"
+ "github.com/go-redis/redis/v8"
"github.com/go-telegram-bot-api/telegram-bot-api"
)
@@ -18,46 +22,71 @@ var flagPort = flag.String("port", "8008", "determined the port the sercice runs
var flagTgTokenFile = flag.String("tgtoken", "/run/secrets/tg_bot_token", "determines the location of the telegram bot token file")
var changelllyAPIKeyFile = flag.String("chapikey", "/run/secrets/ch_api_key", "determines the file that holds the changelly api key")
var alertFile = flag.String("alertfile", "/run/secrets/alerts", "determines the locaiton of the alert files")
-var alertsCheckInterval = flag.Int64("alertinterval", 60., "in seconds, the amount of time between alert checks")
+var alertsCheckInterval = flag.Int64("alertinterval", 600., "in seconds, the amount of time between alert checks")
+var redisAddress = flag.String("redisaddress", "redis:6379", "determines the address of the redis instance")
+var redisPassword = flag.String("redispassword", "", "determines the password of the redis db")
+var redisDB = flag.Int64("redisdb", 0, "determines the db number")
const cryptocomparePriceURL = "https://min-api.cryptocompare.com/data/price?"
const changellyURL = "https://api.changelly.com"
+const botChannelID = 146328407
-func getTgToken() string {
+var getRedisClientOnce sync.Once
+var getTGBotOnce sync.Once
+
+type TgToken struct {
+ Token string `json:"token"`
+}
+
+func getTGToken() string {
tgTokenJsonBytes, err := ioutil.ReadFile(*flagTgTokenFile)
if err != nil {
log.Fatal(err)
}
- type TgToken struct {
- Token string `json:"token"`
- }
-
var tgToken TgToken
err = json.Unmarshal(tgTokenJsonBytes, &tgToken)
if err != nil {
log.Fatal(err)
}
-
return tgToken.Token
}
-func runTgBot() {
- botToken := getTgToken()
- bot, err := tgbotapi.NewBotAPI(botToken)
- if err != nil {
- log.Panic(err)
- }
+func getTgBot() *tgbotapi.BotAPI {
+ var tgbot *tgbotapi.BotAPI
+ getTGBotOnce.Do(func() {
+ tgTokenJsonBytes, err := ioutil.ReadFile(*flagTgTokenFile)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ var tgToken TgToken
+
+ err = json.Unmarshal(tgTokenJsonBytes, &tgToken)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ bot, err := tgbotapi.NewBotAPI(tgToken.Token)
+ if err != nil {
+ log.Panic(err)
+ }
- bot.Debug = true
+ bot.Debug = true
+ tgbot = bot
+ })
+ return tgbot
+}
+func runTgBot() {
+ bot := getTgBot()
log.Printf("Authorized on account %s", bot.Self.UserName)
- u := tgbotapi.NewUpdate(0)
- u.Timeout = 60
+ update := tgbotapi.NewUpdate(0)
+ update.Timeout = 60
- updates, err := bot.GetUpdatesChan(u)
+ updates, err := bot.GetUpdatesChan(update)
if err != nil {
log.Panic(err)
}
@@ -122,10 +151,8 @@ func sendGetToCryptoCompare(
fmt.Println(string(body))
- //FIXME-blocks forever
priceChan <- priceChanStruct{name: name, price: jsonBody[unit]}
errChan <- errorChanStruct{hasError: false, err: nil}
- fmt.Println("done and done")
}
//TODO
@@ -133,11 +160,6 @@ func healthHandler(w http.ResponseWriter, r *http.Request) {
}
func cryptoHandler(w http.ResponseWriter, r *http.Request) {
- if r.URL.Path != "/crypto" {
- http.Error(w, "404 not found.", http.StatusNotFound)
- return
- }
-
if r.Method != "GET" {
http.Error(w, "Method is not supported.", http.StatusNotFound)
}
@@ -222,7 +244,6 @@ func pairHandler(w http.ResponseWriter, r *http.Request) {
go sendGetToCryptoCompare(one, "USD", &wg, priceChan, errChan)
go sendGetToCryptoCompare(two, "USD", &wg, priceChan, errChan)
wg.Wait()
- fmt.Println("getting fucked here")
for i := 0; i < 2; i++ {
select {
@@ -256,33 +277,186 @@ func pairHandler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]interface{}{"ratio": ratio})
}
-func getAlerts() map[string]interface{} {
- alertsBytes, err := ioutil.ReadFile(*flagTgTokenFile)
- if err != nil {
- log.Fatal(err)
- return make(map[string]interface{})
+type alertType struct {
+ Name string `json:"name"`
+ Expr string `json:"expr"`
+}
+
+type alertsType struct {
+ Alerts []alertType `json:"alerts"`
+}
+
+func getRedisClient() *redis.Client {
+ var client *redis.Client
+ getRedisClientOnce.Do(func() {
+ rdb := redis.NewClient(&redis.Options{
+ Addr: *redisAddress,
+ Password: *redisPassword,
+ DB: int(*redisDB),
+ })
+ client = rdb
+ })
+
+ return client
+}
+
+func getAlerts() (alertsType, error) {
+ var alerts alertsType
+ // rdb := getRedisClient()
+ rdb := redis.NewClient(&redis.Options{
+ Addr: *redisAddress,
+ Password: *redisPassword,
+ DB: int(*redisDB),
+ })
+ ctx := context.Background()
+ keys := rdb.SMembersMap(ctx, "alertkeys")
+ alerts.Alerts = make([]alertType, len(keys.Val()))
+ vals := keys.Val()
+
+ i := 0
+ for key := range vals {
+ alert := rdb.Get(ctx, key[6:])
+ expr, _ := alert.Result()
+ alerts.Alerts[i].Name = key
+ alerts.Alerts[i].Expr = expr
+ i++
}
- alertsJson := make(map[string]interface{})
+ return alerts, nil
+}
- err = json.Unmarshal(alertsBytes, &alertsJson)
+func getAlertsFromRedis() (alertsType, error) {
+ // rdb := getRedisClient()
+ rdb := redis.NewClient(&redis.Options{
+ Addr: *redisAddress,
+ Password: *redisPassword,
+ DB: int(*redisDB),
+ })
+ ctx := context.Background()
+ val, err := rdb.Get(ctx, "alert").Result()
if err != nil {
- log.Fatal(err)
- return make(map[string]interface{})
+ log.Printf(err.Error())
+ return alertsType{}, err
}
+ fmt.Println(val)
- return alertsJson
+ return alertsType{}, nil
}
func alertManager() {
- alerts := getAlerts()
- fmt.Println(alerts)
+ for {
+ alerts, err := getAlerts()
+ if err != nil {
+ log.Printf(err.Error())
+ return
+ }
+ fmt.Println(alerts)
+
+ for i := range alerts.Alerts {
+ expression, err := govaluate.NewEvaluableExpression(alerts.Alerts[i].Expr)
+ if err != nil {
+ log.Printf(err.Error())
+ continue
+ }
+
+ vars := expression.Vars()
+ parameters := make(map[string]interface{}, len(vars))
+
+ var wg sync.WaitGroup
+ priceChan := make(chan priceChanStruct, len(vars))
+ errChan := make(chan errorChanStruct, len(vars))
+ defer close(priceChan)
+ defer close(errChan)
+ wg.Add(len(vars))
+
+ for i := range vars {
+ go sendGetToCryptoCompare(vars[i], "USD", &wg, priceChan, errChan)
+ }
+ wg.Wait()
+
+ for i := 0; i < len(vars); i++ {
+ select {
+ case err := <-errChan:
+ if err.hasError != false {
+ log.Printf(err.err.Error())
+ }
+ default:
+ log.Fatal("this shouldnt have happened")
+ }
+ }
+
+ for i := 0; i < len(vars); i++ {
+ select {
+ case price := <-priceChan:
+ parameters[price.name] = price.price
+ default:
+ log.Fatal("this shouldnt have happened")
+ }
+ }
+
+ fmt.Println("parameters:", parameters)
+ result, err := expression.Evaluate(parameters)
+ if err != nil {
+ log.Println(err.Error())
+ }
+
+ var resultBool bool
+ fmt.Println("result:", result)
+ resultBool = result.(bool)
+ if resultBool == true {
+ bot, err := tgbotapi.NewBotAPI(getTGToken())
+ if err != nil {
+ log.Panic(err)
+ }
+ // bot := getTgBot()
+ msgText := "notification " + alerts.Alerts[i].Expr + " has been triggered"
+ msg := tgbotapi.NewMessage(botChannelID, msgText)
+ bot.Send(msg)
+ }
+ }
+
+ time.Sleep(time.Second * time.Duration(*alertsCheckInterval))
+ }
+}
+
+type addAlertJSONType struct {
+ Name string `json:"name"`
+ Expr string `json:"expr"`
+}
+
+func addAlertHandler(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ http.Error(w, "Method is not supported.", http.StatusNotFound)
+ }
+
+ bodyBytes, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ log.Printf(err.Error())
+ }
+
+ var bodyJSON addAlertJSONType
+ json.Unmarshal(bodyBytes, &bodyJSON)
+ fmt.Println(bodyJSON)
+
+ // rdb := getRedisClient()
+ rdb := redis.NewClient(&redis.Options{
+ Addr: *redisAddress,
+ Password: *redisPassword,
+ DB: int(*redisDB),
+ MinIdleConns: 1,
+ })
+ ctx := context.Background()
+ key := "alert:" + bodyJSON.Name
+ rdb.Set(ctx, bodyJSON.Name, bodyJSON.Expr, 0)
+ rdb.SAdd(ctx, "alertkeys", key)
+ json.NewEncoder(w).Encode(map[string]interface{}{"isSuccessful": true, "error": ""})
}
func startServer() {
http.HandleFunc("/health", healthHandler)
http.HandleFunc("/crypto", cryptoHandler)
http.HandleFunc("/pair", pairHandler)
+ http.HandleFunc("/addalert", addAlertHandler)
if err := http.ListenAndServe(":"+*flagPort, nil); err != nil {
log.Fatal(err)