diff options
-rw-r--r-- | Dockerfile | 3 | ||||
-rw-r--r-- | docker-compose.yaml | 17 | ||||
-rw-r--r-- | exprparser.go | 207 | ||||
-rw-r--r-- | go.mod | 10 | ||||
-rw-r--r-- | go.sum | 91 | ||||
-rw-r--r-- | hived.go | 248 |
6 files changed, 330 insertions, 246 deletions
@@ -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{} -} @@ -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 +) @@ -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= @@ -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) |