diff options
author | terminaldweller <thabogre@gmail.com> | 2021-02-23 10:01:14 +0000 |
---|---|---|
committer | terminaldweller <thabogre@gmail.com> | 2021-02-23 10:01:14 +0000 |
commit | c2b3c437772df57162385555933aac440308b31c (patch) | |
tree | ee0160dad06e1cf4e331d88836e1aae8be05721d /hived.go | |
parent | forgot to add the parser... (diff) | |
download | hived-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.go | 248 |
1 files changed, 211 insertions, 37 deletions
@@ -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) |