aboutsummaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--Dockerfile3
-rw-r--r--docker-compose.yaml17
-rw-r--r--exprparser.go207
-rw-r--r--go.mod10
-rw-r--r--go.sum91
-rw-r--r--hived.go248
6 files changed, 330 insertions, 246 deletions
diff --git a/Dockerfile b/Dockerfile
index 2848f6c..1a37fa9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,8 @@ FROM alpine:3.13 as builder
RUN apk update && apk upgrade
RUN apk add go git
-RUN go get -u github.com/go-telegram-bot-api/telegram-bot-api
+COPY ./go.* /hived/
+RUN cd /hived && go mod download
COPY *.go /hived/
RUN cd /hived && go build
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 9495faf..4285550 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,4 +1,4 @@
-version: "3.8"
+version: "3.4"
services:
hived:
image: hived
@@ -12,6 +12,19 @@ services:
restart: unless-stopped
ports:
- "8008:8008"
+ depends_on:
+ - redis
+ redis:
+ image: redis:6.2-alpine
+ networks:
+ - hivednet
+ restart: unless-stopped
+ ports:
+ - "6379:6379"
+ environment:
+ - ALLOW_EMPTY_PASSWORD=yes
+ volumes:
+ - redis-data:/data/
networks:
hivednet:
secrets:
@@ -19,3 +32,5 @@ secrets:
file: ./tgtoken.json
ch_api_key:
file: ./changelly_api_key.json
+volumes:
+ redis-data:
diff --git a/exprparser.go b/exprparser.go
deleted file mode 100644
index 0d62dbc..0000000
--- a/exprparser.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package main
-
-import (
- "bufio"
- "bytes"
- "io"
-)
-
-type Token int
-
-const eof = rune(0)
-
-const (
- ILLEGAL Token = iota
- EOF
- WS
-
- IDENT
-
- ASTERISK
- COMMA
- SEMICOLON
- GT
- LT
- GET
- LET
- EQ
- PLUS
- MINUS
- DIVISION
- R_PAREN
- L_PAREN
- PERIOD
-)
-
-type Scanner struct {
- r *bufio.Reader
-}
-
-func newScanner(r io.Reader) *Scanner {
- return &Scanner{r: bufio.NewReader(r)}
-}
-
-func (s *Scanner) read() rune {
- ch, _, err := s.r.ReadRune()
- if err != nil {
- return eof
- }
-
- return ch
-}
-
-func isWhiteSpace(ch rune) bool {
- if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
- return true
- }
- return false
-}
-
-func isLetter(ch rune) bool {
- if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
- return true
- }
- return false
-}
-
-func isDigit(ch rune) bool {
- if ch >= '0' && ch <= '9' {
- return true
- }
- return false
-}
-
-func isNumber(chs []rune) bool {
- var dotCounter int
- for i := range chs {
- if chs[i] == '.' {
- dotCounter++
- if dotCounter > 1 {
- return false
- }
- continue
- }
-
- if !isDigit(chs[i]) {
- return false
- }
- }
-
- return true
-}
-
-func (s *Scanner) unread() { _ = s.r.UnreadRune() }
-
-func (s *Scanner) scanWhitespace() (tok Token, lit string) {
- var buf bytes.Buffer
- buf.WriteRune(s.read())
-
- for {
- if ch := s.read(); ch == eof {
- break
- } else if !isWhiteSpace(ch) {
- s.unread()
- break
- } else {
- buf.WriteRune(ch)
- }
- }
-
- return WS, buf.String()
-}
-
-func (s *Scanner) scanIdent() (tok Token, lit string) {
- var buf bytes.Buffer
- buf.WriteRune(s.read())
-
- for {
- if ch := s.read(); ch == eof {
- break
- } else if !isLetter(ch) && !isDigit(ch) && ch != '_' {
- s.unread()
- break
- } else {
- _, _ = buf.WriteRune(ch)
- }
- }
-
- return IDENT, buf.String()
-}
-
-func (s *Scanner) scan() (tok Token, lit string) {
- ch := s.read()
-
- if isWhiteSpace(ch) {
- s.unread()
- return s.scanWhitespace()
- } else if isLetter(ch) {
- s.unread()
- return s.scanIdent()
- }
-
- switch ch {
- case eof:
- return EOF, string(ch)
- case '*':
- return ASTERISK, string(ch)
- case '+':
- return PLUS, string(ch)
- case '-':
- return MINUS, string(ch)
- case '/':
- return ASTERISK, string(ch)
- case '(':
- return R_PAREN, string(ch)
- case ')':
- return L_PAREN, string(ch)
- case ',':
- return ASTERISK, string(ch)
- case ';':
- return ASTERISK, string(ch)
- case '<':
- return LT, string(ch)
- case '>':
- return GT, string(ch)
- case '.':
- return PERIOD, string(ch)
- }
-
- return ILLEGAL, string(ch)
-}
-
-type Parser struct {
- s *Scanner
- buf struct {
- tok Token
- lit string
- n int
- }
-}
-
-func newParser(r io.Reader) *Parser {
- return &Parser{s: newScanner(r)}
-}
-
-func (p *Parser) scan() (tok Token, lit string) {
- if p.buf.n != 0 {
- p.buf.n = 0
- return p.buf.tok, p.buf.lit
- }
-
- tok, lit = p.s.scan()
- p.buf.tok, p.buf.lit = tok, lit
-
- return
-}
-
-func (p *Parser) unscan() { p.buf.n = 1 }
-
-type ASTNode struct {
- isIdentifier bool
- isLiteral bool
- isIllegal bool
- isEOF bool
- isWS bool
- children []*ASTNode
- value interface{}
-}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..54eca8b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module github.com/terminaldweller/hived
+
+go 1.15
+
+require (
+ github.com/Knetic/govaluate v3.0.0+incompatible
+ github.com/go-redis/redis/v8 v8.6.0
+ github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
+ github.com/technoweenie/multipartstreamer v1.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..62ad285
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,91 @@
+github.com/Knetic/govaluate v1.5.0 h1:L4MyqdJSld9xr2eZcZHCWLfeIX2SBjqrwIKG1pcm/+4=
+github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
+github.com/go-redis/redis/v8 v8.6.0 h1:swqbqOrxaPztsj2Hf1p94M3YAgl7hYEpcw21z299hh8=
+github.com/go-redis/redis/v8 v8.6.0/go.mod h1:DQ9q4Rk2HtwkrwVrdgmphoOQDMfpvcd/nHEwRsicg8s=
+github.com/go-telegram-bot-api/telegram-bot-api v1.0.0 h1:HXVtsZ+yINQeyyhPFAUU4yKmeN+iFhJ87jXZOC016gs=
+github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
+github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
+github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/otel v0.17.0 h1:6MKOu8WY4hmfpQ4oQn34u6rYhnf2sWf1LXYO/UFm71U=
+go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s=
+go.opentelemetry.io/otel/metric v0.17.0 h1:t+5EioN8YFXQ2EH+1j6FHCKMUj+57zIDSnSGr/mWuug=
+go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0=
+go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE=
+go.opentelemetry.io/otel/trace v0.17.0 h1:SBOj64/GAOyWzs5F680yW1ITIfJkm6cJWL2YAvuL9xY=
+go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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)